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,467 @@
|
|
|
1
|
+
"""What the agent refuses, holds back or asks about, and the secrets it never shows."""
|
|
2
|
+
|
|
3
|
+
import dataclasses
|
|
4
|
+
import json
|
|
5
|
+
import re
|
|
6
|
+
|
|
7
|
+
from .. import contract_screen as proof_screen
|
|
8
|
+
from .. import harness_api
|
|
9
|
+
from ..agent_hooks import mask
|
|
10
|
+
from ..effect_ledger import RETRY_LIMIT as EFFECT_RETRY_LIMIT
|
|
11
|
+
from ..state import ADJUSTABLE_ROLES, pixel_read
|
|
12
|
+
from ..task_policy import APPROVAL_COMMIT, COMMIT_CONTROL, GUARD_WORDS, SHEET_OPENERS, commit_label, requested_by
|
|
13
|
+
from ..transport import WDA_ERRORS, NotSent, TransportError
|
|
14
|
+
from ..wda.gestures import _SWIPE_Y
|
|
15
|
+
from ..wda.guards import DriverRejection
|
|
16
|
+
from ..wda.text import NoKeyboardFocus
|
|
17
|
+
from .protocol import TYPED_OPS
|
|
18
|
+
from .screen import reads_back, screen_pairs, screen_signature, screenshot_part
|
|
19
|
+
|
|
20
|
+
# Actions that could do something twice: held once their outcome is unknown, until a fresh look settles it.
|
|
21
|
+
UNKNOWN_BLOCKS = frozenset({"TAP", "LONG_PRESS", "TAP_XY", "TYPE", "TYPE_SUBMIT", "SET_TEXT", "SUBMIT"})
|
|
22
|
+
# Taps are held on the screen they were tried on; typing stays held on its field, or it would be typed twice.
|
|
23
|
+
SCREEN_SCOPED = frozenset({"TAP", "LONG_PRESS", "TAP_XY"})
|
|
24
|
+
LEAVING_WORDS = frozenset({"back", "cancel", "close", "dismiss", "not now", "later",
|
|
25
|
+
"maybe later", "skip", "no thanks"})
|
|
26
|
+
UNKNOWN_REASON = "The first attempt's outcome is unknown; it may have gone through"
|
|
27
|
+
# A commit sent with no receipt yet: the same act again asks, in every mode.
|
|
28
|
+
PENDING_REASON = "The first one may still be going through"
|
|
29
|
+
# With nobody to answer, what would ask is refused, never done.
|
|
30
|
+
NOBODY = "refused:nobody can approve it here"
|
|
31
|
+
# Past this many commits each further one asks, in every mode: a runaway task never commits in bulk unseen.
|
|
32
|
+
MAX_COMMITS_PER_RUN = 15
|
|
33
|
+
CAP_REASON = "This task already did {} things that needed your OK"
|
|
34
|
+
# Screen text addressed to an assistant: once a screen shows it, every commit asks, Full access included.
|
|
35
|
+
INJECTION = re.compile(
|
|
36
|
+
r"\b(?:ignore (?:all |any |the )?(?:previous|prior|above|earlier) (?:instructions|directions|rules)|"
|
|
37
|
+
r"(?:assistant|ai agent|agent|mobster|claude|chatgpt|gpt|gemini|system|admin)\s*[:>]\s*\S|"
|
|
38
|
+
r"tap (?:on )?[\"“']?\w+(?: \w+)?[\"”']? (?:to|and) (?:continue|proceed|confirm|verify)|"
|
|
39
|
+
r"(?:your|the) (?:new |real )?(?:task|instructions?) (?:is|are)\b|you must now|"
|
|
40
|
+
r"(?:user|owner|person) (?:has |already )?(?:approved|authori[sz]ed|consented)|approve(?:d)? (?:all|every)|"
|
|
41
|
+
r"(?:do not|don.?t|without) (?:ask(?:ing)?|confirm(?:ing)?) (?:the user|for (?:confirmation|approval))|"
|
|
42
|
+
r"(?:send|forward|share) (?:this|the|your) (?:code|password|passcode) to)", re.I)
|
|
43
|
+
INJECTION_REASON = "This screen has text that may be trying to steer Mobster"
|
|
44
|
+
TAINT_REASON = "This message holds text copied from the screen"
|
|
45
|
+
CHANGED_WHILE_ASKING = ("The screen changed while Mobster waited for your OK, so it didn't act. Look at the screen "
|
|
46
|
+
"again: the person is asked again on the screen as it is now.")
|
|
47
|
+
# COMMIT_CONTROL has no like, and a long run asks before one (P1-2).
|
|
48
|
+
LIKE = re.compile(r"\b(like|super ?like)\b", re.I)
|
|
49
|
+
LOOP_REPEATS = 3
|
|
50
|
+
# A tapped act may already be done: the same tap on the same screen waits until the run looks somewhere new.
|
|
51
|
+
REPEAT_ACT_SKIP = frozenset({"done", "close", "cancel", "back", "ok", "next", "continue"})
|
|
52
|
+
REPEAT_ACT_FEEDBACK = ("Refused: you already tapped {label!r} on this screen in this app (turn {step}), and it changed "
|
|
53
|
+
"the screen then, so it may already be done. Look at the list or page it adds to first. If it "
|
|
54
|
+
"isn't there, come back and tap it again.")
|
|
55
|
+
REPEAT_ACT_AGAIN = ("Refused again: since {label!r} (turn {step}) you have seen only this screen and the one it left. "
|
|
56
|
+
"Open the list or page it adds to and check before tapping it again.")
|
|
57
|
+
LOOP_WINDOW = 12
|
|
58
|
+
# Repeated failures call the phone guard, then end the run (B2: 50 failed launches on a locked phone).
|
|
59
|
+
SAME_FAILURES_TO_CHECK = 2
|
|
60
|
+
SAME_FAILURES_TO_STOP = 3
|
|
61
|
+
SAME_REFUSALS_TO_STOP = 4
|
|
62
|
+
PHONE_LOCKED = "Your iPhone is locked. Unlock it and try again."
|
|
63
|
+
COVERED = ("Your iPhone's lock screen or a system alert is in front of {app}, so Mobster stopped instead of "
|
|
64
|
+
"tapping what it can't see. Unlock your iPhone or close the alert, then try again.")
|
|
65
|
+
KEYPAD_REFUSAL = ("Refused: this is the iPhone's passcode screen. Mobster can't unlock the phone; never tap "
|
|
66
|
+
"it, type on it or try a passcode.")
|
|
67
|
+
# StaleSession: the runner restarted under the driver's session, and recovery attaches to the new one.
|
|
68
|
+
CONNECTION_LOST = ("ConnectionRefusedError", "ConnectionResetError", "RemoteDisconnected", "BrokenPipeError",
|
|
69
|
+
"ConnectionAbortedError", "StaleSession")
|
|
70
|
+
# The errors whose request never ran: only these are sent again, after a fresh look.
|
|
71
|
+
NOTHING_SENT = ("ConnectionRefusedError", "StaleSession")
|
|
72
|
+
|
|
73
|
+
|
|
74
|
+
def declined_result(answer):
|
|
75
|
+
status = {"timeout": "approval_timeout", "stopped": "stopped"}.get(answer, "approval_denied")
|
|
76
|
+
reason = {"timeout": "No answer to the approval request, so the action was not taken",
|
|
77
|
+
"stopped": "Stopped while waiting for your approval"}.get(
|
|
78
|
+
answer, "You declined the action, so it was not taken")
|
|
79
|
+
return {"status": status, "answer": None, "reason": reason}
|
|
80
|
+
|
|
81
|
+
|
|
82
|
+
def input_shows(element, text):
|
|
83
|
+
wanted = " ".join(str(text or "").split()).casefold()
|
|
84
|
+
value = " ".join(str(getattr(element, "value", None) or "").split()).casefold()
|
|
85
|
+
if not wanted or not value:
|
|
86
|
+
return False
|
|
87
|
+
return value == wanted or (getattr(element, "role", None) in ADJUSTABLE_ROLES
|
|
88
|
+
and value.split()[:1] == wanted.split()[:1])
|
|
89
|
+
|
|
90
|
+
|
|
91
|
+
def row_under_stroke(snapshot):
|
|
92
|
+
for element in getattr(snapshot, "elements", ()) or ():
|
|
93
|
+
x, y, w, h = element.rect
|
|
94
|
+
if element.role == "Cell" and h < .2 and w >= .8 and y <= _SWIPE_Y <= y + h:
|
|
95
|
+
return element
|
|
96
|
+
return None
|
|
97
|
+
|
|
98
|
+
|
|
99
|
+
def refused_sentence(reason, times, acted):
|
|
100
|
+
why = re.sub(r"^Refused:\s*", "", " ".join(str(reason or "").split()))[:240].rstrip(". ")
|
|
101
|
+
tail = "" if acted else " No action was taken."
|
|
102
|
+
return (f"Mobster stopped: it was refused the same step {times} times ({why}). Reword the request to say exactly "
|
|
103
|
+
f"what to do, or do that step yourself.{tail}")
|
|
104
|
+
|
|
105
|
+
|
|
106
|
+
def stuck_sentence(op, name, detail, acted):
|
|
107
|
+
tail = "" if acted else " No action was taken."
|
|
108
|
+
if op == "LAUNCH_APP":
|
|
109
|
+
return (f"Mobster couldn't open {name} ({detail}). Your iPhone may be locked, or {name} may be asking for a "
|
|
110
|
+
f"passcode or Face ID: unlock your iPhone (or open {name} once yourself), then try again.{tail}")
|
|
111
|
+
return f"The same step failed three times ({detail}), so Mobster stopped instead of trying again.{tail}"
|
|
112
|
+
|
|
113
|
+
|
|
114
|
+
# A BaseException, so it passes every ``except Exception`` on its way out of the run.
|
|
115
|
+
class GuardStop(BaseException):
|
|
116
|
+
def __init__(self, verdict):
|
|
117
|
+
super().__init__(verdict.code)
|
|
118
|
+
self.verdict = verdict
|
|
119
|
+
|
|
120
|
+
|
|
121
|
+
def scrub(value, secrets):
|
|
122
|
+
if isinstance(value, str):
|
|
123
|
+
return mask(value, secrets)
|
|
124
|
+
if isinstance(value, dict):
|
|
125
|
+
return {scrub(key, secrets): scrub(item, secrets) for key, item in value.items()}
|
|
126
|
+
if isinstance(value, list):
|
|
127
|
+
return [scrub(item, secrets) for item in value]
|
|
128
|
+
if isinstance(value, tuple):
|
|
129
|
+
return tuple(scrub(item, secrets) for item in value)
|
|
130
|
+
return value
|
|
131
|
+
|
|
132
|
+
|
|
133
|
+
def failure_feedback(op, element, text, error):
|
|
134
|
+
message = str(error)
|
|
135
|
+
if op in TYPED_OPS and "control characters" in message:
|
|
136
|
+
if text and "\n" in text and (element is None or element.role != "TextView"):
|
|
137
|
+
return ("Refused: this field takes one line. Line breaks go only in a multi-line text view (a note or "
|
|
138
|
+
"document body): type the lines there, or send this field text without line breaks.")
|
|
139
|
+
if text and len(text) > 1000:
|
|
140
|
+
return f"Refused: the text is too long ({len(text)} characters) for one action: type it in parts."
|
|
141
|
+
if outcome_unknown(error, op):
|
|
142
|
+
return (("The action timed out" if isinstance(error, TimeoutError) else "The action's outcome is unknown")
|
|
143
|
+
+ ": it may have happened, or partly. " + unknown_advice(op))
|
|
144
|
+
return f"The action could not be performed ({type(error).__name__}: {message[:120]}); the screen may have changed."
|
|
145
|
+
|
|
146
|
+
|
|
147
|
+
def unknown_advice(op):
|
|
148
|
+
return "; ".join(["Look at the screen before acting"]
|
|
149
|
+
+ (["Mobster sends this same action again only once a fresh look shows it didn't happen"]
|
|
150
|
+
if op in UNKNOWN_BLOCKS else [])
|
|
151
|
+
+ (["read the fields' current values and never retype text that is already there"]
|
|
152
|
+
if op in TYPED_OPS else [])) + "."
|
|
153
|
+
|
|
154
|
+
|
|
155
|
+
# WDA's names for a request it refuses before it acts, so nothing ran (FBCommandStatus, in v16.12.10's handlers). They
|
|
156
|
+
# speak for one request, so only for an action of one primitive: typing is several, and a later request's refusal says
|
|
157
|
+
# nothing about the earlier ones.
|
|
158
|
+
WDA_NOTHING_RAN = frozenset({"invalid argument", "invalid selector", "invalid coordinates", "unknown command",
|
|
159
|
+
"unknown method", "unsupported operation", "no such alert", "no such element"})
|
|
160
|
+
|
|
161
|
+
|
|
162
|
+
# Fails closed: unsure is unknown. Connection recovery doesn't resend such an action, and a further attempt the model
|
|
163
|
+
# asks for goes through _recheck_unknown. ``op``: on a tap or on typing, any transport failure is unknown unless it
|
|
164
|
+
# proves nothing ran: it never left, or the driver says nothing was typed, or WDA named its refusal of a tap with the
|
|
165
|
+
# status it pairs with that name. An answer that can't be read, has no name, or has another status proves nothing.
|
|
166
|
+
def outcome_unknown(error, op=None):
|
|
167
|
+
if isinstance(error, DriverRejection):
|
|
168
|
+
return error.code not in DriverRejection.PRE_DISPATCH
|
|
169
|
+
if isinstance(error, TransportError) and op in UNKNOWN_BLOCKS:
|
|
170
|
+
if isinstance(error, (NotSent, NoKeyboardFocus)) or any(mark in str(error) for mark in NOTHING_SENT):
|
|
171
|
+
return False
|
|
172
|
+
refused = error.wda_error in WDA_NOTHING_RAN and error.status == WDA_ERRORS[error.wda_error]
|
|
173
|
+
return op in TYPED_OPS or not refused
|
|
174
|
+
message = str(error)
|
|
175
|
+
if isinstance(error, ConnectionError):
|
|
176
|
+
return not isinstance(error, ConnectionRefusedError)
|
|
177
|
+
return (isinstance(error, TimeoutError) or "outcome unknown" in message or "outcome_unknown" in message
|
|
178
|
+
or any(name in message for name in CONNECTION_LOST if name not in NOTHING_SENT))
|
|
179
|
+
|
|
180
|
+
|
|
181
|
+
def control_state(element):
|
|
182
|
+
if element is None:
|
|
183
|
+
return None
|
|
184
|
+
return [element.label or "", element.role or "", element.value or "", bool(element.selected),
|
|
185
|
+
bool(element.enabled)]
|
|
186
|
+
|
|
187
|
+
|
|
188
|
+
VOLATILE = re.compile(r"(?:\d[\d,.]*\s*[kmb]?\s*(?:likes?|views?|comments?|followers?|reactions?|replies|plays?|"
|
|
189
|
+
r"seconds?|minutes?|hours?|days?|weeks?|[smhdw])?(?: ago)?|\d{1,2}:\d{2}(?::\d{2})?\s*(?:am|pm)?|"
|
|
190
|
+
r"now|just now|yesterday|today|typing…?|typing\.\.\.|delivered|read|sent|seen|live|"
|
|
191
|
+
r"\d+\s*%)", re.I)
|
|
192
|
+
|
|
193
|
+
|
|
194
|
+
# Checked again at dispatch: a clock, a typing dot or a video's progress may change; nothing else may.
|
|
195
|
+
def binding(snapshot, element):
|
|
196
|
+
if element is None:
|
|
197
|
+
return None
|
|
198
|
+
elements = list(getattr(snapshot, "elements", ()) or ())
|
|
199
|
+
from ..permissions.consequence import CONTAINERS
|
|
200
|
+
try:
|
|
201
|
+
to = proof_screen.addressed_to(snapshot)
|
|
202
|
+
except Exception:
|
|
203
|
+
to = None
|
|
204
|
+
try:
|
|
205
|
+
amount = proof_screen.commit_amount(element, elements)
|
|
206
|
+
except Exception:
|
|
207
|
+
amount = None
|
|
208
|
+
cx, cy = element.rect[0] + element.rect[2] / 2, element.rect[1] + element.rect[3] / 2
|
|
209
|
+
rows = [e for e in elements if e is not element and e.role == "Cell" and e.rect[0] <= cx <= e.rect[0] + e.rect[2]
|
|
210
|
+
and e.rect[1] <= cy <= e.rect[1] + e.rect[3]]
|
|
211
|
+
row = min(rows, key=lambda e: e.rect[2] * e.rect[3]).label if rows else None
|
|
212
|
+
# The text above a control says what it acts on: a moved feed puts another post's Like in its place.
|
|
213
|
+
above = [e for e in elements if e.role in ("StaticText", "Cell", "Link") and e is not element
|
|
214
|
+
and 0 <= cy - (e.rect[1] + e.rect[3] / 2) <= .4 and re.search(r"[A-Za-z]{3}", e.label or "")
|
|
215
|
+
and not VOLATILE.fullmatch(" ".join((e.label or "").split()))]
|
|
216
|
+
context = " ".join(min(above, key=lambda e: cy - e.rect[1]).label.split()) if above else None
|
|
217
|
+
fields = tuple(sorted(" ".join((e.value or "").split()) for e in elements
|
|
218
|
+
if e.editable and e.role != "SearchField" and (e.value or "") != (e.placeholder or "")))
|
|
219
|
+
return {"app": getattr(snapshot, "bundle_id", "") or "", "label": " ".join((element.label or "").split()),
|
|
220
|
+
"role": element.role, "frame": tuple(round(v, 3) for v in element.rect), "row": row, "context": context,
|
|
221
|
+
"title": proof_screen.screen_title(snapshot), "to": to, "fields": fields, "amount": amount,
|
|
222
|
+
"overlays": tuple(sorted(" ".join((e.label or "").split()) for e in elements if e.role in CONTAINERS))}
|
|
223
|
+
|
|
224
|
+
|
|
225
|
+
def same_binding(approved, now, tolerance=.02):
|
|
226
|
+
if approved is None or now is None:
|
|
227
|
+
return False
|
|
228
|
+
for key, value in approved.items():
|
|
229
|
+
if key == "frame":
|
|
230
|
+
if any(abs(a - b) > tolerance for a, b in zip(value, now[key])):
|
|
231
|
+
return False
|
|
232
|
+
elif now.get(key) != value:
|
|
233
|
+
return False
|
|
234
|
+
return True
|
|
235
|
+
|
|
236
|
+
|
|
237
|
+
def _commits(element):
|
|
238
|
+
from ..permissions.consequence import label_act
|
|
239
|
+
return bool(label_act(element.label, element.role) or commit_label(element.label, element.role))
|
|
240
|
+
|
|
241
|
+
|
|
242
|
+
# On a screen read from pixels a row's swipe actions are lines of text, not buttons.
|
|
243
|
+
SWIPE_ACTIONS = re.compile(r"^(?:delete|trash|archive|more|flag|unflag|mark as (?:read|unread)|read|unread|pin|unpin|"
|
|
244
|
+
r"mute|unmute|hide alerts|show alerts|move|report|remove|edit|share)$", re.I)
|
|
245
|
+
|
|
246
|
+
|
|
247
|
+
def revealed_actions(snapshot, row):
|
|
248
|
+
x, y, w, h = row.rect
|
|
249
|
+
if w >= .95:
|
|
250
|
+
return []
|
|
251
|
+
pixels = pixel_read(snapshot)
|
|
252
|
+
return [e for e in getattr(snapshot, "elements", ()) or () if e is not row
|
|
253
|
+
and (e.role == "Button" or pixels and SWIPE_ACTIONS.match(" ".join((e.label or "").split())))
|
|
254
|
+
and y <= e.rect[1] + e.rect[3] / 2 <= y + h and e.rect[0] >= x + w - .01]
|
|
255
|
+
|
|
256
|
+
|
|
257
|
+
def _nobody(request):
|
|
258
|
+
return NOBODY
|
|
259
|
+
|
|
260
|
+
|
|
261
|
+
# Fails closed: whatever could commit counts, and is never sent again without the person's OK.
|
|
262
|
+
def consequential(op, element, snapshot):
|
|
263
|
+
from ..permissions.rules import DESTRUCTIVE, PRICE, Action, outside_act
|
|
264
|
+
if op in ("SUBMIT", "TYPE_SUBMIT", "TAP_XY") or element is None:
|
|
265
|
+
return True
|
|
266
|
+
label = " ".join((element.label or "").split())
|
|
267
|
+
if harness_api.commit_act(op, label, None, getattr(snapshot, "bundle_id", None)) is not None:
|
|
268
|
+
return True
|
|
269
|
+
if APPROVAL_COMMIT.search(label) or DESTRUCTIVE.search(label) or PRICE.search(label):
|
|
270
|
+
return True
|
|
271
|
+
if outside_act(Action(op=op, label=label, role=element.role)) is not None:
|
|
272
|
+
return True
|
|
273
|
+
if op in TYPED_OPS or label.casefold() in LEAVING_WORDS:
|
|
274
|
+
return False
|
|
275
|
+
# A tap on a form holding typed text may submit it; moving back or away from it doesn't.
|
|
276
|
+
return any(e.editable and e.role != "SearchField" and (e.value or "").strip() and e.value != e.placeholder
|
|
277
|
+
and (e.value or "").strip() != (e.label or "").strip() for e in snapshot.elements)
|
|
278
|
+
|
|
279
|
+
|
|
280
|
+
def holds_text(element, text):
|
|
281
|
+
shown = element.value or ""
|
|
282
|
+
if not text or not shown.strip() or shown == element.placeholder or shown.strip() == (element.label or "").strip():
|
|
283
|
+
return False
|
|
284
|
+
return reads_back(shown, text)
|
|
285
|
+
|
|
286
|
+
|
|
287
|
+
def guard(request, operation, element):
|
|
288
|
+
if operation != "TAP" or element is None:
|
|
289
|
+
return None
|
|
290
|
+
# A control names its act in its first words; a long row that mentions one is not it (multi-071).
|
|
291
|
+
label = " ".join((element.label or "").split()[:GUARD_WORDS])
|
|
292
|
+
# Only commits the person would want to see first: Save, Done and Add complete a requested change.
|
|
293
|
+
if not COMMIT_CONTROL.search(label) or commit_label(element.label, element.role) is None:
|
|
294
|
+
return None
|
|
295
|
+
if " ".join(label.split()).casefold() in SHEET_OPENERS:
|
|
296
|
+
# Opening a share or action sheet commits nothing; its Send or Post is guarded in turn.
|
|
297
|
+
return None
|
|
298
|
+
if requested_by(request, "TAP", element.role, label):
|
|
299
|
+
return None
|
|
300
|
+
return (f"Refused: tapping {label!r} performs an act the request does not ask for. Choose an action "
|
|
301
|
+
"that does what the user asked.")
|
|
302
|
+
|
|
303
|
+
|
|
304
|
+
class SafetyMixin:
|
|
305
|
+
def _act_key(self, op, element, snapshot):
|
|
306
|
+
if op not in ("TAP", "LONG_PRESS") or element is None:
|
|
307
|
+
return None
|
|
308
|
+
label = proof_screen.plain(element.label or "")
|
|
309
|
+
if not label or label.split()[0] in REPEAT_ACT_SKIP or not proof_screen.ACT_LABEL.search(
|
|
310
|
+
" ".join(label.split()[:GUARD_WORDS])):
|
|
311
|
+
return None
|
|
312
|
+
# The whole screen, not its title: paying one row of a list of Pay rows makes the next row's Pay another act.
|
|
313
|
+
return (self._app_name(snapshot), label, screen_signature(snapshot))
|
|
314
|
+
|
|
315
|
+
@staticmethod
|
|
316
|
+
def _effect(op, element, app, text, app_name, snapshot):
|
|
317
|
+
if op not in UNKNOWN_BLOCKS:
|
|
318
|
+
return None
|
|
319
|
+
target = (proof_screen.plain(element.label or "") or element.locator) if element is not None else str(app or "")
|
|
320
|
+
role = element.role if element is not None else None
|
|
321
|
+
screen = screen_signature(snapshot) if op in SCREEN_SCOPED else None
|
|
322
|
+
return ("TAP" if op in ("LONG_PRESS", "TAP_XY") else op), (app_name, op, target, role, text, screen)
|
|
323
|
+
|
|
324
|
+
# Fails closed: an attempt's screen that couldn't be read holds the control on any screen.
|
|
325
|
+
def _held(self, effect, snapshot):
|
|
326
|
+
if effect is None:
|
|
327
|
+
return None
|
|
328
|
+
if self.run_state.effects.would_duplicate(effect[0], None, effect=effect[1]):
|
|
329
|
+
return effect
|
|
330
|
+
if effect[1][-1] is None:
|
|
331
|
+
return None
|
|
332
|
+
pairs = screen_pairs(snapshot)
|
|
333
|
+
|
|
334
|
+
def under(key, record):
|
|
335
|
+
if key[:-1] != effect[1][:-1]:
|
|
336
|
+
return False
|
|
337
|
+
then = (record.get("context") or {}).get("pairs")
|
|
338
|
+
return then is None or pairs is None or then <= pairs
|
|
339
|
+
|
|
340
|
+
keys = self.run_state.effects.held(under)
|
|
341
|
+
return (effect[0], keys[0]) if keys else None
|
|
342
|
+
|
|
343
|
+
@staticmethod
|
|
344
|
+
def _before_unknown(element, snapshot):
|
|
345
|
+
return {"screen": screen_signature(snapshot), "pairs": screen_pairs(snapshot),
|
|
346
|
+
"app": getattr(snapshot, "bundle_id", "") or "", "control": control_state(element)}
|
|
347
|
+
|
|
348
|
+
# Fails closed: anything missing counts as changed.
|
|
349
|
+
def _unchanged(self, before, element, snapshot):
|
|
350
|
+
if not before or snapshot is None or element is None:
|
|
351
|
+
return False, None
|
|
352
|
+
found = self._find(snapshot, element.label, element.role, element.locator)
|
|
353
|
+
same = (found is not None and before.get("screen") and screen_signature(snapshot) == before["screen"]
|
|
354
|
+
and (getattr(snapshot, "bundle_id", "") or "") == before.get("app")
|
|
355
|
+
and control_state(found) == before.get("control"))
|
|
356
|
+
return bool(same), found
|
|
357
|
+
|
|
358
|
+
# Unchanged, a neutral action goes once more and a consequential one asks again, never on its own.
|
|
359
|
+
def _recheck_unknown(self, op, element, effect, what):
|
|
360
|
+
record = self.run_state.effects.lookup(effect[0], None, effect=effect[1]) or {}
|
|
361
|
+
done = (f"Refused: your earlier {op} {what!r} took effect (the screen changed after it), so Mobster doesn't "
|
|
362
|
+
"send it again. Go on from the screen as it is now.")
|
|
363
|
+
if record.get("outcome") == "observed_change":
|
|
364
|
+
return "refuse", done, None
|
|
365
|
+
held = (f"Refused: your earlier {op} {what!r} has an unknown outcome: it may have happened, so Mobster doesn't "
|
|
366
|
+
"send it again. Look at the screen: if it took effect, go on; if not, try another way, or finish with "
|
|
367
|
+
"BLOCKED and say what is left.")
|
|
368
|
+
if record.get("outcome") != "unknown" or element is None:
|
|
369
|
+
return "refuse", held, None
|
|
370
|
+
fresh = self._read_once()
|
|
371
|
+
unchanged, found = self._unchanged(record.get("context"), element, fresh)
|
|
372
|
+
if not unchanged:
|
|
373
|
+
self.run_state.effects.settle(effect[0], None, effect=effect[1], happened=True)
|
|
374
|
+
return "refuse", done, None
|
|
375
|
+
if consequential(op, found, fresh):
|
|
376
|
+
if (self.approve or self.forced_approve or self.floor_approve) is None:
|
|
377
|
+
return "refuse", held[:-1] + " Sending it again needs the user's OK, and nobody can give it here.", None
|
|
378
|
+
return "ask", fresh, found
|
|
379
|
+
if record.get("retries", 0) >= EFFECT_RETRY_LIMIT:
|
|
380
|
+
return "refuse", (f"Refused: your earlier {op} {what!r} was sent once more after an unknown outcome, and "
|
|
381
|
+
"that one's outcome is unknown too, so Mobster doesn't send it again. Try another way, "
|
|
382
|
+
"or finish with BLOCKED and say what is left."), None
|
|
383
|
+
return "retry", fresh, found
|
|
384
|
+
|
|
385
|
+
def _mask(self, text):
|
|
386
|
+
return mask(text, self.run_state.secrets)
|
|
387
|
+
|
|
388
|
+
def _mask_reply(self, out):
|
|
389
|
+
if not self.run_state.secrets:
|
|
390
|
+
return out
|
|
391
|
+
try:
|
|
392
|
+
return json.loads(self._mask(json.dumps(out, ensure_ascii=False)))
|
|
393
|
+
except (TypeError, ValueError):
|
|
394
|
+
return {}
|
|
395
|
+
|
|
396
|
+
# The model, the contract and the trace see this copy; the driver acts on the real one.
|
|
397
|
+
def _masked(self, snapshot):
|
|
398
|
+
if not self.run_state.secrets or snapshot is None:
|
|
399
|
+
return snapshot
|
|
400
|
+
elements = [dataclasses.replace(e, label=self._mask(e.label)[:500], value=self._mask(e.value)[:500],
|
|
401
|
+
placeholder=self._mask(e.placeholder)[:500]) for e in snapshot.elements]
|
|
402
|
+
return dataclasses.replace(snapshot, elements=elements, text=self._mask(snapshot.text))
|
|
403
|
+
|
|
404
|
+
def _blur_rects(self, snapshot):
|
|
405
|
+
keep = []
|
|
406
|
+
for rect, bundle in self.run_state.secret_rects:
|
|
407
|
+
x, y, w, h = rect
|
|
408
|
+
if bundle and getattr(snapshot, "bundle_id", None) not in (bundle, None, ""):
|
|
409
|
+
continue
|
|
410
|
+
if any(e.editable and e.rect[0] < x + w and x < e.rect[0] + e.rect[2] and e.rect[1] < y + h
|
|
411
|
+
and y < e.rect[1] + e.rect[3] for e in snapshot.elements):
|
|
412
|
+
keep.append((rect, bundle))
|
|
413
|
+
self.run_state.secret_rects = keep
|
|
414
|
+
return tuple(rect for rect, _ in keep)
|
|
415
|
+
|
|
416
|
+
# A full swipe can act on a row; nothing is undone here, the run stops and says where to look.
|
|
417
|
+
def _row_gone(self, op, element, before, after):
|
|
418
|
+
if op not in ("SWIPE_LEFT", "SWIPE_RIGHT") or element is None or element.role != "Cell":
|
|
419
|
+
return None
|
|
420
|
+
if getattr(after, "bundle_id", None) != getattr(before, "bundle_id", None) or \
|
|
421
|
+
proof_screen.screen_title(after) != proof_screen.screen_title(before):
|
|
422
|
+
return None
|
|
423
|
+
label = " ".join((element.label or "").split())
|
|
424
|
+
if any(" ".join((e.label or "").split()) == label for e in after.elements):
|
|
425
|
+
return None
|
|
426
|
+
name = self._app_name(after) or "the app"
|
|
427
|
+
return (f"A swipe on {self._mask(label)[:60]!r} removed it from the list in {name}. Mobster stopped so you can "
|
|
428
|
+
f"check: the app may offer Undo, or keep it in Recently Deleted or Trash.")
|
|
429
|
+
|
|
430
|
+
def _secret_blur(self, snapshot):
|
|
431
|
+
blur = self._blur_rects(snapshot) if self.run_state.secret_rects else ()
|
|
432
|
+
if self.run_state.secrets:
|
|
433
|
+
blur = tuple(blur) + tuple(
|
|
434
|
+
tuple(e.rect) for e in getattr(snapshot, "elements", ()) or ()
|
|
435
|
+
if any(secret in f"{e.label}\n{e.value}\n{e.placeholder}" for secret in self.run_state.secrets))
|
|
436
|
+
return blur
|
|
437
|
+
|
|
438
|
+
def _wants_image(self, turn, lines):
|
|
439
|
+
if self._image_mode != "tree" or self.longform is not None:
|
|
440
|
+
self.run_state.image_why = "off" if self._image_mode != "tree" else "long task"
|
|
441
|
+
return True
|
|
442
|
+
app = getattr(turn.snapshot, "bundle_id", "") or ""
|
|
443
|
+
facts = self._image_policy.Turn(
|
|
444
|
+
first=self.run_state.imaged_app is None, new_app=app != self.run_state.imaged_app,
|
|
445
|
+
blind=self.run_state.blind, missed=bool(turn.feedback) or bool(turn.unproven),
|
|
446
|
+
skill_pictures=bool(self.run_state.skill_images), pixel_read=pixel_read(turn.snapshot),
|
|
447
|
+
request=turn.request or "")
|
|
448
|
+
send, self.run_state.image_why = self._image_policy.decide(self._image_policy.parse_rows(lines), facts)
|
|
449
|
+
return send
|
|
450
|
+
|
|
451
|
+
def _screenshot(self, snapshot):
|
|
452
|
+
return screenshot_part(self.driver, since=getattr(snapshot, "read_started", None),
|
|
453
|
+
blur=self._secret_blur(snapshot))
|
|
454
|
+
|
|
455
|
+
|
|
456
|
+
def note_injection(agent, snapshot, step):
|
|
457
|
+
run = agent.run_state
|
|
458
|
+
if run.injected or agent.permissions is None:
|
|
459
|
+
return
|
|
460
|
+
visible = (" ".join(f"{element.label or ''} {element.value or ''}".split())
|
|
461
|
+
for element in snapshot.elements
|
|
462
|
+
if element.role not in ("TextField", "SecureTextField", "SearchField"))
|
|
463
|
+
seen = next((text for text in (*visible, *run.passed, *(run.listing or ())) if INJECTION.search(text)), None)
|
|
464
|
+
if seen is not None:
|
|
465
|
+
# A transient read can steer the next action in the same model chunk.
|
|
466
|
+
run.injected = True
|
|
467
|
+
agent._emit({"event": "frontier_injection", "step": step, "text": agent._mask(seen)[:160]})
|