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,192 @@
|
|
|
1
|
+
"""The stages that run once an action was sent: the screen after, the record, and whether the chain goes on."""
|
|
2
|
+
|
|
3
|
+
import time
|
|
4
|
+
|
|
5
|
+
from ... import contract_asks as proof_asks
|
|
6
|
+
from ... import contract_screen as proof_screen
|
|
7
|
+
from ...agent_hooks import GuardVerdict
|
|
8
|
+
from ..observe import TWO_READ_OPS, UNPROVEN_SETTLES
|
|
9
|
+
from ..prompt import row_texts
|
|
10
|
+
from ..protocol import HOUSEKEEPING_OPS, SCROLL_OPS, TYPED_OPS
|
|
11
|
+
from ..safety import COVERED, PENDING_REASON, SAME_FAILURES_TO_CHECK, GuardStop, consequential, input_shows
|
|
12
|
+
from ..screen import names_act, screen_signature, text_status
|
|
13
|
+
|
|
14
|
+
LOG_LABEL_CHARS, LOG_TEXT_CHARS = 200, 600
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
def settled_rect(op, element, snapshot):
|
|
18
|
+
"""Where the element this step acted on sits on the screen the step ends on, as fractions ``[x, y, w, h]``, for a
|
|
19
|
+
tap or typing step whose element is still there (the same locator, role and label). None when it is unknown."""
|
|
20
|
+
if element is None or not (op in TYPED_OPS or op in ("TAP", "LONG_PRESS")):
|
|
21
|
+
return None
|
|
22
|
+
for other in getattr(snapshot, "elements", None) or ():
|
|
23
|
+
if (other.locator and other.locator == element.locator and other.role == element.role
|
|
24
|
+
and other.label == element.label):
|
|
25
|
+
try:
|
|
26
|
+
x, y, w, h = (float(value) for value in other.rect)
|
|
27
|
+
except (TypeError, ValueError):
|
|
28
|
+
return None
|
|
29
|
+
return [round(x, 4), round(y, 4), round(w, 4), round(h, 4)] if w > 0 and h > 0 else None
|
|
30
|
+
return None
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
def may_commit(op, element, snapshot):
|
|
34
|
+
"""Whether a press may have committed something, by the loop's own test. A test that fails counts as a commit."""
|
|
35
|
+
try:
|
|
36
|
+
return consequential(op, element, snapshot)
|
|
37
|
+
except Exception:
|
|
38
|
+
return True
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
def action_label(op, element, app):
|
|
42
|
+
if op in ("SWIPE_LEFT", "SWIPE_RIGHT"):
|
|
43
|
+
return element.label[:120] if element is not None and element.label else None
|
|
44
|
+
return element.label[:120] if element else app
|
|
45
|
+
|
|
46
|
+
|
|
47
|
+
def settle(agent, attempt, turn):
|
|
48
|
+
index = turn.index
|
|
49
|
+
op, element, gate, before = attempt.op, attempt.element, attempt.gate, attempt.before
|
|
50
|
+
attempt.exec_ms = agent._spend("exec", attempt.t_exec)
|
|
51
|
+
t_settle = time.monotonic()
|
|
52
|
+
following = turn.chunk[index + 1].op if index + 1 < len(turn.chunk) else None
|
|
53
|
+
last = following in (None, "DONE", "BLOCKED")
|
|
54
|
+
# HOME right before LAUNCH_APP uses nothing on the home screen, so its settle is skipped.
|
|
55
|
+
passing = op == "HOME" and following == "LAUNCH_APP"
|
|
56
|
+
if passing:
|
|
57
|
+
turn.snapshot = before
|
|
58
|
+
elif op in TYPED_OPS and following == "SUBMIT":
|
|
59
|
+
# Return goes to the field just typed into: one read finds it.
|
|
60
|
+
turn.snapshot = agent._read_once()
|
|
61
|
+
else:
|
|
62
|
+
turn.snapshot = agent._settle(before, last and op not in TWO_READ_OPS, rest=last and op == "LAUNCH_APP")
|
|
63
|
+
turn.unproven = last and getattr(agent.driver, "settled_by", None) in UNPROVEN_SETTLES
|
|
64
|
+
attempt.settle_ms = agent._spend("settle", t_settle)
|
|
65
|
+
if agent._contract is not None:
|
|
66
|
+
agent._contract.observe(agent._masked(turn.snapshot), agent._app_name(turn.snapshot), turn.step)
|
|
67
|
+
attempt.changed = passing or turn.snapshot.content_fingerprint != before.content_fingerprint
|
|
68
|
+
if (not attempt.changed and gate is not None and gate.item is not None and proof_asks.asks(gate.item)
|
|
69
|
+
and agent.permissions is not None and gate.item.status != "closed"
|
|
70
|
+
and str(gate.item.count or "1").strip() in ("", "1")):
|
|
71
|
+
# Sent, no receipt yet: the same act again asks first, with the warning, in every mode.
|
|
72
|
+
agent.run_state.held_items[gate.item.id] = PENDING_REASON
|
|
73
|
+
gone = agent._row_gone(op, element, before, turn.snapshot)
|
|
74
|
+
if gone is not None:
|
|
75
|
+
turn.result = {"status": "blocked", "answer": None, "code": "unexpected_change",
|
|
76
|
+
"reason": gone}
|
|
77
|
+
agent._emit({"event": "frontier_refused", "step": turn.step, "label": "row_gone"})
|
|
78
|
+
return "end"
|
|
79
|
+
if op in SCROLL_OPS and following in SCROLL_OPS and attempt.changed:
|
|
80
|
+
# The next swipe was chosen without this screen: its rows would scroll past unseen (mem-009).
|
|
81
|
+
if turn.turn_rows is None:
|
|
82
|
+
turn.turn_rows = set(row_texts(agent._masked(turn.turn_screen)))
|
|
83
|
+
for row in row_texts(agent._masked(turn.snapshot)):
|
|
84
|
+
if row not in turn.turn_rows:
|
|
85
|
+
turn.turn_rows.add(row)
|
|
86
|
+
agent.run_state.passed.append(row)
|
|
87
|
+
done_key = agent._act_key(op, element, before) if attempt.changed else None
|
|
88
|
+
if agent.run_state.act_seen:
|
|
89
|
+
where, now = agent._app_name(turn.snapshot), None
|
|
90
|
+
for (act_app, _, _), looked in agent.run_state.act_seen.items():
|
|
91
|
+
if act_app == where:
|
|
92
|
+
now = now or screen_signature(turn.snapshot)
|
|
93
|
+
looked.add(now)
|
|
94
|
+
if done_key is not None:
|
|
95
|
+
agent.run_state.acts_done[done_key] = turn.step
|
|
96
|
+
agent.run_state.act_left[done_key] = screen_signature(turn.snapshot)
|
|
97
|
+
agent.run_state.act_seen[done_key] = {agent.run_state.act_left[done_key]}
|
|
98
|
+
agent.run_state.act_refused.pop(done_key, None)
|
|
99
|
+
|
|
100
|
+
|
|
101
|
+
def record(agent, attempt, turn):
|
|
102
|
+
index = turn.index
|
|
103
|
+
op, text, app, element, gate, before, changed = (
|
|
104
|
+
attempt.op, attempt.text, attempt.app, attempt.element, attempt.gate, attempt.before, attempt.changed)
|
|
105
|
+
turn.after = op if changed or op in TYPED_OPS else None
|
|
106
|
+
turn.last_done = (attempt.key, before.content_fingerprint, turn.snapshot.content_fingerprint)
|
|
107
|
+
turn.recent.append(attempt.spot)
|
|
108
|
+
agent.last_screen = turn.snapshot
|
|
109
|
+
agent._emit({"event": "frontier_action", "step": turn.step, "index": index, "operation": op,
|
|
110
|
+
"target_label": action_label(op, element, app), "changed": changed,
|
|
111
|
+
"text": agent._mask(text[:200]) if text is not None and (op in TYPED_OPS or op == "SCROLL_TO") else None,
|
|
112
|
+
"field": ("search" if proof_screen.is_search_field(element) else "composer"
|
|
113
|
+
if proof_screen.COMPOSER.search(element.label or "") else "field")
|
|
114
|
+
if element is not None and op in TYPED_OPS else None,
|
|
115
|
+
"app_name": agent._app_name(before) or None,
|
|
116
|
+
# Whether a press may have committed something: the step's commit.
|
|
117
|
+
**({"consequential": may_commit(op, element, before)} if op in ("TAP", "LONG_PRESS") else {}),
|
|
118
|
+
**({"element_rect": rect} if (rect := settled_rect(op, element, turn.snapshot)) else {}),
|
|
119
|
+
"refresh_ms": attempt.refresh_ms, "exec_ms": attempt.exec_ms, "settle_ms": attempt.settle_ms,
|
|
120
|
+
**({"settle_prediction": ps} if (ps := getattr(agent.driver, "predicted_settle", None)) else {})})
|
|
121
|
+
what = (element.label[:LOG_LABEL_CHARS] if element else app or "")
|
|
122
|
+
receipt = agent.run_state.last_receipt
|
|
123
|
+
if receipt is not None:
|
|
124
|
+
agent._emit({"event": "frontier_text", "step": turn.step, "index": index, "method": receipt.get("method"),
|
|
125
|
+
"chars": receipt.get("chars"), "matches": receipt.get("matches"),
|
|
126
|
+
"repaired": receipt.get("repaired"), "flattened": receipt.get("flattened")})
|
|
127
|
+
turn.history.append(f"{turn.step}.{index}: {op} {what!r}" + (f" text={text[:LOG_TEXT_CHARS]!r}" if text else "")
|
|
128
|
+
+ (" -> screen changed" if changed else " -> NO visible change")
|
|
129
|
+
+ (f"; field: {text_status(receipt)[:160]}" if receipt is not None else "")
|
|
130
|
+
+ ("; Return NOT pressed" if receipt is not None and op == "TYPE_SUBMIT"
|
|
131
|
+
and receipt.get("submitted") is False else ""))
|
|
132
|
+
if agent.run_state.readback:
|
|
133
|
+
if changed and op in ("TAP", "LONG_PRESS", "TAP_XY") and names_act(op, element, text):
|
|
134
|
+
said = agent._read_back(turn.snapshot)
|
|
135
|
+
if said:
|
|
136
|
+
turn.history[-1] += "; " + said
|
|
137
|
+
agent._remember_lists(turn.snapshot)
|
|
138
|
+
if op == "SET_TEXT" and text and not changed and not input_shows(element, text):
|
|
139
|
+
# Only an unchanged SET_TEXT stops what follows: a TYPE's text is checked by its receipt.
|
|
140
|
+
turn.untaken = (op, text)
|
|
141
|
+
if agent.step_observer is not None:
|
|
142
|
+
agent._observe_step(turn.step, index, op, before, element, text, changed, gate)
|
|
143
|
+
else:
|
|
144
|
+
agent.run_state.asked = None
|
|
145
|
+
agent._check_app(turn.snapshot)
|
|
146
|
+
if gate is not None and gate.item is not None and gate.item.act == "install":
|
|
147
|
+
# The App Store's own confirmation is the person's: the guard hands it off or stops (C3).
|
|
148
|
+
verdict = agent._guard_check("sheet_suspected", turn.history)
|
|
149
|
+
if verdict is not None and verdict.state == "unlocked":
|
|
150
|
+
turn.snapshot = agent._observe()
|
|
151
|
+
if changed:
|
|
152
|
+
turn.covered = 0
|
|
153
|
+
turn.failures.clear() # only failures in a row stop the run
|
|
154
|
+
if (not changed and op in ("TAP", "TAP_XY", "LONG_PRESS") and not turn.checked_front
|
|
155
|
+
and agent._front_app_differs(turn.snapshot)):
|
|
156
|
+
# The tree is the app's, but SpringBoard is in front: the lock screen or a system sheet.
|
|
157
|
+
turn.checked_front = True
|
|
158
|
+
verdict = agent._guard_check("lock_suspected", turn.history)
|
|
159
|
+
if verdict is not None and verdict.state == "unlocked":
|
|
160
|
+
turn.snapshot = agent._observe()
|
|
161
|
+
elif verdict is None:
|
|
162
|
+
# No guard to look: taps land on a screen the tree doesn't show, and the second time the run stops (B2).
|
|
163
|
+
turn.covered += 1
|
|
164
|
+
name = agent._app_name(turn.snapshot) or "the app"
|
|
165
|
+
if turn.covered >= SAME_FAILURES_TO_CHECK:
|
|
166
|
+
raise GuardStop(GuardVerdict("stop", "phone_locked", COVERED.format(app=name)))
|
|
167
|
+
turn.feedback = (f"The iPhone's lock screen, Home Screen or a system sheet is in front of {name}: "
|
|
168
|
+
"what is listed may not be what the screen shows. If it is the lock screen or a "
|
|
169
|
+
"passcode screen, finish with BLOCKED; never tap it.")
|
|
170
|
+
return "stop"
|
|
171
|
+
|
|
172
|
+
|
|
173
|
+
def chain(agent, attempt, turn):
|
|
174
|
+
index = turn.index
|
|
175
|
+
op, element, changed = attempt.op, attempt.element, attempt.changed
|
|
176
|
+
if attempt.commit_now and index + 1 < len(turn.chunk) and turn.chunk[index + 1].op not in ("DONE", "BLOCKED"):
|
|
177
|
+
# A commit ends its chain: the next action is chosen on the screen it left.
|
|
178
|
+
agent._emit({"event": "frontier_chunk_stop", "step": turn.step, "index": index, "reason": "commit"})
|
|
179
|
+
return "stop"
|
|
180
|
+
if not changed and op in HOUSEKEEPING_OPS and [a.op for a in turn.chunk[index + 1:]] == ["DONE"]:
|
|
181
|
+
# Closing or scrolling had nothing to do; the DONE after it stands (mem-004).
|
|
182
|
+
return "next"
|
|
183
|
+
if (not changed and op == "TAP" and element is not None and element.editable
|
|
184
|
+
and index + 1 < len(turn.chunk) and turn.chunk[index + 1].op in TYPED_OPS):
|
|
185
|
+
# Focusing a field often shows nothing; the typing after it is the point (multi-010).
|
|
186
|
+
return "next"
|
|
187
|
+
if not changed and op not in TYPED_OPS:
|
|
188
|
+
if index + 1 < len(turn.chunk):
|
|
189
|
+
agent._emit({"event": "frontier_chunk_stop", "step": turn.step, "index": index,
|
|
190
|
+
"reason": "no_change"})
|
|
191
|
+
return "stop" # an action with no effect: look again before going on
|
|
192
|
+
return "next"
|
|
@@ -0,0 +1,138 @@
|
|
|
1
|
+
"""The stage that decides who asks for an action, and what follows the answer."""
|
|
2
|
+
|
|
3
|
+
from ... import contract_asks as proof_asks
|
|
4
|
+
from ...agent_hooks import RESUME_AFTER_SECONDS
|
|
5
|
+
from ..interrupts import Where, check
|
|
6
|
+
from ..protocol import ELEMENT_OPS
|
|
7
|
+
from ..safety import (
|
|
8
|
+
CAP_REASON,
|
|
9
|
+
CHANGED_WHILE_ASKING,
|
|
10
|
+
INJECTION_REASON,
|
|
11
|
+
MAX_COMMITS_PER_RUN,
|
|
12
|
+
PENDING_REASON,
|
|
13
|
+
SAME_REFUSALS_TO_STOP,
|
|
14
|
+
UNKNOWN_REASON,
|
|
15
|
+
_nobody,
|
|
16
|
+
binding,
|
|
17
|
+
declined_result,
|
|
18
|
+
refused_sentence,
|
|
19
|
+
same_binding,
|
|
20
|
+
)
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
def approval(agent, attempt, turn):
|
|
24
|
+
index = turn.index
|
|
25
|
+
op, text, unknown_repeat, gate = attempt.op, attempt.text, attempt.unknown_repeat, attempt.gate
|
|
26
|
+
asker, force = agent.approve, False
|
|
27
|
+
agent.run_state.ask_reason = None
|
|
28
|
+
attempt.commit_now = agent._is_commit(gate, op, attempt.element, turn.snapshot, text)
|
|
29
|
+
held_reason = (agent.run_state.held_items.get(gate.item.id) if gate is not None and gate.item is not None
|
|
30
|
+
and gate.item.status != "closed" else None)
|
|
31
|
+
if (held_reason is None and gate is not None and gate.item is not None and gate.item.status != "closed"
|
|
32
|
+
and gate.item.act in agent.run_state.held_acts and proof_asks.asks(gate.item)):
|
|
33
|
+
held_reason = UNKNOWN_REASON # an earlier run of this task may have done it already
|
|
34
|
+
extra = None
|
|
35
|
+
if held_reason is not None and unknown_repeat is None:
|
|
36
|
+
if (agent.approve or agent.forced_approve or agent.floor_approve) is None and agent.permissions is None:
|
|
37
|
+
if held_reason == UNKNOWN_REASON:
|
|
38
|
+
turn.feedback = (f"Refused: an earlier {gate.item.act} for this request has an unknown outcome: "
|
|
39
|
+
"it may have happened. Doing it again needs the user's OK, and nobody can "
|
|
40
|
+
"give it here.")
|
|
41
|
+
turn.history.append(f"{turn.step}.{index}: {op} {attempt.element.label[:60]!r} refused "
|
|
42
|
+
"(an earlier attempt's outcome is unknown)")
|
|
43
|
+
agent._emit({"event": "frontier_refused", "step": turn.step, "label": "outcome_unknown"})
|
|
44
|
+
return "stop"
|
|
45
|
+
else:
|
|
46
|
+
extra = held_reason
|
|
47
|
+
elif (attempt.commit_now or agent._out_of_scope(op, attempt.element, turn.request)) and agent.run_state.injected:
|
|
48
|
+
# A screen this run tried to steer Mobster: every commit and every change no request named asks.
|
|
49
|
+
extra = INJECTION_REASON
|
|
50
|
+
elif attempt.commit_now and agent.run_state.commits >= MAX_COMMITS_PER_RUN:
|
|
51
|
+
extra = CAP_REASON.format(agent.run_state.commits)
|
|
52
|
+
if extra is not None and attempt.element is not None:
|
|
53
|
+
force, agent.run_state.ask_reason = True, extra
|
|
54
|
+
asker = asker or agent.forced_approve or agent.floor_approve or (
|
|
55
|
+
_nobody if agent.permissions is not None or extra in (UNKNOWN_REASON, PENDING_REASON) else None)
|
|
56
|
+
if turn.session is not None and attempt.element is not None and agent._long_asks(gate, op, attempt.element):
|
|
57
|
+
# A long run asks before every act that asks, Ask before acting off too (P1-2).
|
|
58
|
+
force = True
|
|
59
|
+
if force and attempt.element is not None:
|
|
60
|
+
if asker is None:
|
|
61
|
+
asker = agent.forced_approve
|
|
62
|
+
if asker is None:
|
|
63
|
+
turn.feedback = (f"Refused: {attempt.element.label[:60]!r} needs the user's OK, and this run can't ask for "
|
|
64
|
+
"it. Finish with what you found, or BLOCKED if the request needs this.")
|
|
65
|
+
turn.history.append(
|
|
66
|
+
f"{turn.step}.{index}: {op} {attempt.element.label[:60]!r} refused (needs the user's OK)")
|
|
67
|
+
agent._emit({"event": "frontier_refused", "step": turn.step, "label": "needs_approval"})
|
|
68
|
+
return "stop"
|
|
69
|
+
if asker is None and agent.permissions is not None:
|
|
70
|
+
# Full access: what asks in every mode (a one-time code) still reaches the person.
|
|
71
|
+
asker = agent.floor_approve
|
|
72
|
+
if asker is None and agent._asks_mode():
|
|
73
|
+
# A mode that asks with nobody to answer: what would ask is refused, never done.
|
|
74
|
+
asker = _nobody
|
|
75
|
+
cards_before = agent.run_state.cards
|
|
76
|
+
if unknown_repeat == "ask":
|
|
77
|
+
# A consequential action whose first outcome is unknown asks in every mode.
|
|
78
|
+
asker = agent.approve or agent.forced_approve or agent.floor_approve
|
|
79
|
+
if asker is not None:
|
|
80
|
+
verdict, waited = agent._approval(gate, op, attempt.element, turn.snapshot, text, turn.chain_typed, turn.step,
|
|
81
|
+
asker=asker, force=force, unknown=unknown_repeat == "ask")
|
|
82
|
+
turn.started += waited # waiting for the user never spends the task's time
|
|
83
|
+
agent._waited(waited)
|
|
84
|
+
if verdict is None and check(agent, turn, Where.WAITED) is not None:
|
|
85
|
+
# Approved, but Stop came too: a stopped task never acts on an approval.
|
|
86
|
+
return "end"
|
|
87
|
+
if verdict is None and agent.run_state.cards == cards_before and op in ("TAP", "LONG_PRESS") \
|
|
88
|
+
and agent._asks_mode():
|
|
89
|
+
# The two-headed check: the pixels in the control's frame against its label.
|
|
90
|
+
verdict, more = agent._two_heads(asker, op, attempt.element, turn.snapshot, turn.chain_typed, turn.step)
|
|
91
|
+
turn.started += more
|
|
92
|
+
agent._waited(more)
|
|
93
|
+
if verdict is not None and verdict.startswith("refused:"):
|
|
94
|
+
turn.feedback = (f"Refused: {attempt.element.label[:60]!r} needs the user's OK, and nobody can approve it "
|
|
95
|
+
"here. Finish with what you found, or BLOCKED if the request needs this.")
|
|
96
|
+
turn.history.append(
|
|
97
|
+
f"{turn.step}.{index}: {op} {attempt.element.label[:60]!r} refused (needs the user's OK)")
|
|
98
|
+
agent._emit({"event": "frontier_refused", "step": turn.step, "label": "needs_approval"})
|
|
99
|
+
turn.refusals[turn.feedback] = turn.refusals.get(turn.feedback, 0) + 1
|
|
100
|
+
if turn.refusals[turn.feedback] >= SAME_REFUSALS_TO_STOP:
|
|
101
|
+
turn.result = {"status": "blocked", "answer": None, "code": "stuck",
|
|
102
|
+
"reason": refused_sentence(turn.feedback, turn.refusals[turn.feedback], turn.actions)}
|
|
103
|
+
return "end"
|
|
104
|
+
return "stop"
|
|
105
|
+
if verdict is not None and verdict.startswith("redirected:"):
|
|
106
|
+
agent.run_state.asked = "redirected"
|
|
107
|
+
instruction = " ".join(verdict.split(":", 1)[1].split())[:500]
|
|
108
|
+
turn.request = f"{turn.request}\n\nThe user's later instruction: {instruction}"
|
|
109
|
+
turn.feedback = (f"The user declined {attempt.element.label[:60]!r} and asked instead: {instruction!r}. "
|
|
110
|
+
"Do that now; change what you typed if it needs changing.")
|
|
111
|
+
turn.history.append(f"{turn.step}.{index}: {op} {attempt.element.label[:60]!r} declined; user redirected")
|
|
112
|
+
return "stop"
|
|
113
|
+
if verdict is not None:
|
|
114
|
+
turn.result = declined_result(verdict)
|
|
115
|
+
return "end"
|
|
116
|
+
if waited >= RESUME_AFTER_SECONDS:
|
|
117
|
+
# The phone may have locked while the person decided.
|
|
118
|
+
resumed = agent._guard_check("resume", turn.history)
|
|
119
|
+
if resumed is not None and resumed.state == "unlocked" and attempt.element is not None:
|
|
120
|
+
turn.snapshot = agent._observe()
|
|
121
|
+
attempt.element = agent._find(
|
|
122
|
+
turn.snapshot, attempt.element.label, attempt.element.role, attempt.element.locator)
|
|
123
|
+
if attempt.element is None:
|
|
124
|
+
turn.feedback = "The screen changed while Mobster waited for your OK: look again."
|
|
125
|
+
return "stop"
|
|
126
|
+
if agent.run_state.cards != cards_before and attempt.element is not None and op in ELEMENT_OPS:
|
|
127
|
+
# The approval is bound to what the card showed: any change on a fresh read means no action now.
|
|
128
|
+
bound = binding(turn.snapshot, attempt.element)
|
|
129
|
+
fresh = agent._read_once()
|
|
130
|
+
found = agent._find(fresh, attempt.element.label, attempt.element.role, attempt.element.locator)
|
|
131
|
+
if found is None or not same_binding(bound, binding(fresh, found)):
|
|
132
|
+
turn.feedback = CHANGED_WHILE_ASKING
|
|
133
|
+
turn.history.append(f"{turn.step}.{index}: {op} {attempt.element.label[:60]!r} not done "
|
|
134
|
+
"(the screen changed while asking)")
|
|
135
|
+
agent._emit({"event": "frontier_refused", "step": turn.step, "label": "changed_while_asking"})
|
|
136
|
+
turn.snapshot, turn.unproven = fresh, False
|
|
137
|
+
return "stop"
|
|
138
|
+
turn.snapshot, attempt.element = fresh, found
|
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
"""The stages that find the control an action names and check its shape."""
|
|
2
|
+
|
|
3
|
+
import dataclasses
|
|
4
|
+
|
|
5
|
+
from ...agent_hooks import safe_allows
|
|
6
|
+
from ...state import ADJUSTABLE_ROLES, TEXT_OPERATIONS, pixel_read
|
|
7
|
+
from ..act import opens_field
|
|
8
|
+
from ..protocol import ELEMENT_OPS, TYPED_OPS
|
|
9
|
+
from ..safety import GuardStop, holds_text, revealed_actions, row_under_stroke
|
|
10
|
+
from ..screen import editable_in
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
def target(agent, attempt, turn):
|
|
14
|
+
index = turn.index
|
|
15
|
+
if attempt.op in ELEMENT_OPS:
|
|
16
|
+
attempt.element = agent._target(attempt.act, index, turn.aliases, turn.snapshot, turn.after)
|
|
17
|
+
if attempt.element is None:
|
|
18
|
+
agent._emit({"event": "frontier_chunk_stop", "step": turn.step, "index": index,
|
|
19
|
+
"reason": "target_missing", "wanted": (attempt.act.target_label or "")[:80]})
|
|
20
|
+
if index == 0:
|
|
21
|
+
turn.feedback = f"{attempt.op} needs a target element id from the current screen."
|
|
22
|
+
wanted = attempt.act.target_label or attempt.act.target or attempt.act.opened_by
|
|
23
|
+
turn.history.append(f"{turn.step}.{index}: {attempt.op} target {wanted!r} "
|
|
24
|
+
+ ("(the field it opened) " if attempt.act.opened_by else "")
|
|
25
|
+
+ "not on screen; stopped the sequence")
|
|
26
|
+
return "stop"
|
|
27
|
+
if attempt.op in TYPED_OPS and opens_field(attempt.element, turn.snapshot):
|
|
28
|
+
# Typing into a field's prompt drawn as a button fails: it is tapped first, with every check a TAP gets.
|
|
29
|
+
agent._emit({"event": "frontier_repaired", "step": turn.step, "index": index, "operation": attempt.op,
|
|
30
|
+
"as": "TAP", "label": agent._mask(attempt.element.label)[:80]})
|
|
31
|
+
turn.chunk.insert(index + 1, dataclasses.replace(attempt.act, target=None, target_label=None,
|
|
32
|
+
opened_by=attempt.element.label))
|
|
33
|
+
attempt.act = dataclasses.replace(attempt.act, op="TAP", text=None)
|
|
34
|
+
attempt.op, attempt.text = "TAP", None
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
def sideways_swipe(agent, attempt, turn):
|
|
38
|
+
index = turn.index
|
|
39
|
+
act, op = attempt.act, attempt.op
|
|
40
|
+
if op in ("SWIPE_LEFT", "SWIPE_RIGHT"):
|
|
41
|
+
# A sideways swipe strokes through the row it targets (B7); untargeted over a list row it is refused.
|
|
42
|
+
named = act.target or act.target_label
|
|
43
|
+
attempt.element = agent._target(act, index, turn.aliases, turn.snapshot, turn.after) if named else None
|
|
44
|
+
if named and attempt.element is None:
|
|
45
|
+
agent._emit({"event": "frontier_chunk_stop", "step": turn.step, "index": index,
|
|
46
|
+
"reason": "target_missing", "wanted": str(named)[:80]})
|
|
47
|
+
turn.history.append(f"{turn.step}.{index}: {op} target {named!r} not on screen; stopped the sequence")
|
|
48
|
+
return "stop"
|
|
49
|
+
if (attempt.element is not None and (attempt.element.role == "Cell" or pixel_read(turn.snapshot))
|
|
50
|
+
and revealed_actions(turn.snapshot, attempt.element)):
|
|
51
|
+
# Its actions already show: another swipe could run the app's full swipe (Mail trashes).
|
|
52
|
+
turn.feedback = (f"Refused: {agent._mask(attempt.element.label)[:60]!r} already shows its actions. "
|
|
53
|
+
"TAP the one the request needs, or leave it.")
|
|
54
|
+
turn.history.append(f"{turn.step}.{index}: {op} refused (its row's actions already show)")
|
|
55
|
+
agent._emit({"event": "frontier_refused", "step": turn.step, "label": "swipe_again"})
|
|
56
|
+
return "stop"
|
|
57
|
+
crossed = row_under_stroke(turn.snapshot) if attempt.element is None else None
|
|
58
|
+
if crossed is not None:
|
|
59
|
+
turn.feedback = (f"Refused: {op} needs a target here: name the row to swipe (its id), or it "
|
|
60
|
+
f"would swipe whichever row is in the middle of the screen "
|
|
61
|
+
f"({agent._mask(crossed.label)[:60]!r}).")
|
|
62
|
+
turn.history.append(f"{turn.step}.{index}: {op} refused (no target row)")
|
|
63
|
+
agent._emit({"event": "frontier_refused", "step": turn.step, "label": "untargeted_swipe"})
|
|
64
|
+
return "stop"
|
|
65
|
+
|
|
66
|
+
|
|
67
|
+
def shape(agent, attempt, turn):
|
|
68
|
+
index = turn.index
|
|
69
|
+
app = attempt.app
|
|
70
|
+
clearing = attempt.op == "SET_TEXT" and attempt.text == "" and attempt.element is not None and (
|
|
71
|
+
agent._exact_text() or callable(getattr(agent.driver, "clear_text", None)))
|
|
72
|
+
if (attempt.op in TYPED_OPS or attempt.op == "SCROLL_TO") and not attempt.text and not clearing:
|
|
73
|
+
turn.feedback = f"{attempt.op} needs text." + (
|
|
74
|
+
' SET_TEXT with text "" empties a field.' if attempt.op in TYPED_OPS else "")
|
|
75
|
+
return "stop"
|
|
76
|
+
if attempt.op in ("TAP", "LONG_PRESS") and attempt.element is not None and not attempt.element.enabled:
|
|
77
|
+
turn.feedback = (f"Refused: {attempt.element.label[:60]!r} is disabled: it does nothing yet. "
|
|
78
|
+
"Do what enables it first (fill the required fields, choose an option).")
|
|
79
|
+
turn.history.append(f"{turn.step}.{index}: {attempt.op} {attempt.element.label[:60]!r} refused (disabled)")
|
|
80
|
+
agent._emit({"event": "frontier_refused", "step": turn.step, "label": "disabled"})
|
|
81
|
+
return "stop"
|
|
82
|
+
if attempt.op == "LAUNCH_APP" and app not in agent.apps:
|
|
83
|
+
turn.feedback = "LAUNCH_APP needs one of the listed apps."
|
|
84
|
+
return "stop"
|
|
85
|
+
if attempt.op == "LAUNCH_APP" and not safe_allows(agent.guard, app):
|
|
86
|
+
instruction = agent._app_instruction()
|
|
87
|
+
if not instruction:
|
|
88
|
+
raise GuardStop(agent._blocked_app(app))
|
|
89
|
+
turn.request = f"{turn.request}\n\nThe user's later instruction: {instruction}"
|
|
90
|
+
turn.feedback = (f"The user didn't allow {agent.apps.get(app, app)} and asked instead: {instruction!r}. "
|
|
91
|
+
"Do that now.")
|
|
92
|
+
turn.history.append(f"{turn.step}.{index}: LAUNCH_APP {app} declined; user redirected")
|
|
93
|
+
return "stop"
|
|
94
|
+
if attempt.op in TYPED_OPS and attempt.element is not None and not attempt.element.editable \
|
|
95
|
+
and attempt.element.role not in ADJUSTABLE_ROLES:
|
|
96
|
+
# A row that holds a field is not the field.
|
|
97
|
+
attempt.element = editable_in(turn.snapshot, attempt.element) or attempt.element
|
|
98
|
+
if attempt.text and attempt.element is not None and agent._exact_text():
|
|
99
|
+
# write_text keeps line breaks where the field takes them and never types Return; other controls are spaces.
|
|
100
|
+
attempt.text = "".join(ch if ch.isprintable() or ch == "\n" else " " for ch in attempt.text)
|
|
101
|
+
elif attempt.text and attempt.element is not None and attempt.element.role != "TextView":
|
|
102
|
+
# Line breaks are text only in a text view; elsewhere Return would submit.
|
|
103
|
+
attempt.text = " ".join("".join(ch if ch.isprintable() else " " for ch in attempt.text).split())
|
|
104
|
+
if (attempt.op in TEXT_OPERATIONS and attempt.element is not None and attempt.element.editable
|
|
105
|
+
and holds_text(attempt.element, attempt.text)
|
|
106
|
+
and (attempt.op == "TYPE" or agent._appends(attempt.op, attempt.element))):
|
|
107
|
+
# Text a field already shows isn't typed again; a TYPE_SUBMIT that would append only presses Return.
|
|
108
|
+
turn.feedback = f"{attempt.element.label[:60]!r} already held that text, so it wasn't typed again."
|
|
109
|
+
turn.history.append(f"{turn.step}.{index}: {attempt.op} {attempt.element.label[:60]!r} not typed: "
|
|
110
|
+
"the field already held the text")
|
|
111
|
+
agent._emit({"event": "frontier_skipped", "step": turn.step, "index": index, "operation": attempt.op,
|
|
112
|
+
"reason": "already_typed"})
|
|
113
|
+
if attempt.op == "TYPE":
|
|
114
|
+
return "next"
|
|
115
|
+
attempt.op, attempt.text = "SUBMIT", None
|
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
"""The stages that end or hold an action: an input that changed nothing, DONE, BLOCKED and WAIT."""
|
|
2
|
+
|
|
3
|
+
import time
|
|
4
|
+
|
|
5
|
+
from ..prompt import DONE_CHECK
|
|
6
|
+
from ..protocol import TYPED_OPS, WRAP_UP_SHARE
|
|
7
|
+
|
|
8
|
+
IDLE_WAIT_LIMIT = 2
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
def input_not_taken(agent, attempt, turn):
|
|
12
|
+
index = turn.index
|
|
13
|
+
op = attempt.op
|
|
14
|
+
if turn.untaken is not None and op not in TYPED_OPS:
|
|
15
|
+
# An input that changed nothing: what follows was planned on a value that isn't there.
|
|
16
|
+
turn.feedback = (f"Your {turn.untaken[0]} {agent._mask(turn.untaken[1])[:60]!r} changed nothing on screen, so "
|
|
17
|
+
f"{op} did not run. Check the values on screen and set them before going on.")
|
|
18
|
+
turn.history.append(f"{turn.step}.{index}: {op} not run: the {turn.untaken[0]} before it changed nothing")
|
|
19
|
+
agent._emit({"event": "frontier_chunk_stop", "step": turn.step, "index": index,
|
|
20
|
+
"reason": "input_not_taken"})
|
|
21
|
+
return "stop"
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
def _said(attempt, turn):
|
|
25
|
+
return turn.out.get("answer") or attempt.text
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
def done_gate(agent, attempt, turn):
|
|
29
|
+
if agent._contract is None:
|
|
30
|
+
return None
|
|
31
|
+
index = turn.index
|
|
32
|
+
wrap = (agent.max_cost_usd is not None and turn.spent is not None
|
|
33
|
+
and turn.spent + turn.reserve >= WRAP_UP_SHARE * agent.max_cost_usd)
|
|
34
|
+
accepted, reason, answer = agent._contract.done_gate(
|
|
35
|
+
_said(attempt, turn), 0 if wrap else agent.max_steps - turn.calls)
|
|
36
|
+
if not accepted:
|
|
37
|
+
turn.feedback = reason
|
|
38
|
+
turn.history.append(f"{turn.step}.{index}: DONE refused: items not shown on screen yet")
|
|
39
|
+
agent._emit({"event": "frontier_chunk_stop", "step": turn.step, "index": index,
|
|
40
|
+
"reason": "proof_gate", "open": [i.id for i in agent._contract.items
|
|
41
|
+
if i.reason and not i.unproven]})
|
|
42
|
+
return "stop"
|
|
43
|
+
agent._reveal_found(turn)
|
|
44
|
+
turn.result = {"status": "completed", "answer": answer}
|
|
45
|
+
if turn.data_json:
|
|
46
|
+
turn.result["data_json"] = turn.out.get("data_json")
|
|
47
|
+
return "end"
|
|
48
|
+
|
|
49
|
+
|
|
50
|
+
# No contract: a DONE right after a change, or before the request was walked item by item, is deferred once.
|
|
51
|
+
def done_deferred(agent, attempt, turn):
|
|
52
|
+
if turn.calls >= agent.max_steps - 1 or (
|
|
53
|
+
agent.max_cost_usd is not None and turn.spent is not None
|
|
54
|
+
and turn.spent >= WRAP_UP_SHARE * agent.max_cost_usd):
|
|
55
|
+
return None
|
|
56
|
+
index = turn.index
|
|
57
|
+
reasons = ["You finished right after changing things: check on this screen that every "
|
|
58
|
+
"change took effect as intended."] if turn.acted else []
|
|
59
|
+
if not turn.request_checked:
|
|
60
|
+
reasons.append(DONE_CHECK)
|
|
61
|
+
turn.request_checked = True
|
|
62
|
+
if reasons:
|
|
63
|
+
turn.feedback = " ".join(reasons)
|
|
64
|
+
turn.history.append(f"{turn.step}.{index}: DONE deferred: check the result and the request first")
|
|
65
|
+
agent._emit({"event": "frontier_chunk_stop", "step": turn.step, "index": index,
|
|
66
|
+
"reason": "verify_before_done"})
|
|
67
|
+
return "stop"
|
|
68
|
+
|
|
69
|
+
|
|
70
|
+
def finish(agent, attempt, turn):
|
|
71
|
+
said = _said(attempt, turn)
|
|
72
|
+
if attempt.op == "BLOCKED":
|
|
73
|
+
sentence = said or (turn.out.get("thought") or "").strip() or None
|
|
74
|
+
turn.result = {"status": "blocked", "answer": sentence, "reason": sentence}
|
|
75
|
+
else:
|
|
76
|
+
turn.result = {"status": "completed", "answer": said}
|
|
77
|
+
if turn.data_json:
|
|
78
|
+
turn.result["data_json"] = turn.out.get("data_json")
|
|
79
|
+
return "end"
|
|
80
|
+
|
|
81
|
+
|
|
82
|
+
def wait(agent, attempt, turn):
|
|
83
|
+
index = turn.index
|
|
84
|
+
if turn.idle_waits >= IDLE_WAIT_LIMIT:
|
|
85
|
+
turn.feedback = ("Refused: you have waited twice and nothing on screen changed; nothing is "
|
|
86
|
+
"loading. Act instead: the thing you wait for may not exist (check that "
|
|
87
|
+
"earlier steps were really saved), or try another route.")
|
|
88
|
+
turn.history.append(f"{turn.step}.{index}: WAIT refused (screen idle)")
|
|
89
|
+
agent._emit({"event": "frontier_refused", "step": turn.step, "label": "idle_wait"})
|
|
90
|
+
return "stop"
|
|
91
|
+
turn.history.append(f"{turn.step}.{index}: WAIT")
|
|
92
|
+
t_wait = time.monotonic()
|
|
93
|
+
time.sleep(1.0)
|
|
94
|
+
fresh = agent._observe()
|
|
95
|
+
agent._spend("wait", t_wait)
|
|
96
|
+
turn.idle_waits = turn.idle_waits + 1 if fresh.content_fingerprint == turn.snapshot.content_fingerprint else 0
|
|
97
|
+
turn.snapshot, turn.unproven = fresh, False
|
|
98
|
+
return "stop"
|
|
99
|
+
|
|
100
|
+
|
|
101
|
+
def active(agent, attempt, turn):
|
|
102
|
+
turn.idle_waits = 0
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
"""The stages that ask the task's contract and the signed-in profile before an action."""
|
|
2
|
+
|
|
3
|
+
from ... import contract_asks as proof_asks
|
|
4
|
+
from ... import contract_screen as proof_screen
|
|
5
|
+
from ... import harness_api
|
|
6
|
+
from ...agent_hooks import GuardVerdict
|
|
7
|
+
from ..actions import Action
|
|
8
|
+
from ..safety import SAME_REFUSALS_TO_STOP, GuardStop, refused_sentence
|
|
9
|
+
from ..screen import screen_image
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
def contract_gate(agent, attempt, turn):
|
|
13
|
+
index = turn.index
|
|
14
|
+
op, element = attempt.op, attempt.element
|
|
15
|
+
attempt.gate = None
|
|
16
|
+
if agent._contract is not None and element is not None:
|
|
17
|
+
attempt.gate = agent._contract.check(op, element, turn.snapshot, agent._app_name(turn.snapshot),
|
|
18
|
+
text=attempt.text, chain_typed=tuple(turn.chain_typed))
|
|
19
|
+
if not attempt.gate.allowed:
|
|
20
|
+
turn.feedback = attempt.gate.reason
|
|
21
|
+
turn.history.append(f"{turn.step}.{index}: {op} {element.label[:60]!r} refused (not declared)")
|
|
22
|
+
agent._emit({"event": "frontier_refused", "step": turn.step, "label": element.label[:120],
|
|
23
|
+
"reason": attempt.gate.reason[:200]})
|
|
24
|
+
turn.refusals[attempt.gate.reason] = turn.refusals.get(attempt.gate.reason, 0) + 1
|
|
25
|
+
if turn.refusals[attempt.gate.reason] >= SAME_REFUSALS_TO_STOP:
|
|
26
|
+
refused = turn.refusals[attempt.gate.reason]
|
|
27
|
+
agent._emit({"event": "frontier_stuck", "step": turn.step, "refused": refused,
|
|
28
|
+
"reason": attempt.gate.reason[:200]})
|
|
29
|
+
turn.result = {"status": "blocked", "answer": None, "code": "stuck",
|
|
30
|
+
"reason": refused_sentence(attempt.gate.reason, refused, turn.actions)}
|
|
31
|
+
return "end"
|
|
32
|
+
return "stop"
|
|
33
|
+
if (attempt.unknown_repeat == "retry" and attempt.gate is not None and attempt.gate.item is not None
|
|
34
|
+
and proof_asks.asks(attempt.gate.item)):
|
|
35
|
+
# The contract calls it a commit, so the person is asked again.
|
|
36
|
+
if (agent.approve or agent.forced_approve or agent.floor_approve) is None:
|
|
37
|
+
turn.feedback = (f"Refused: your earlier {op} {element.label[:60]!r} has an unknown outcome: it may "
|
|
38
|
+
"have happened. Sending it again needs the user's OK, and nobody can give it here.")
|
|
39
|
+
turn.history.append(f"{turn.step}.{index}: {op} {element.label[:60]!r} refused (its earlier outcome was "
|
|
40
|
+
"unknown)")
|
|
41
|
+
agent._emit({"event": "frontier_refused", "step": turn.step, "label": "outcome_unknown"})
|
|
42
|
+
return "stop"
|
|
43
|
+
attempt.unknown_repeat = "ask"
|
|
44
|
+
|
|
45
|
+
|
|
46
|
+
def ask_at_send(agent, attempt, turn):
|
|
47
|
+
index = turn.index
|
|
48
|
+
text, element = attempt.text, attempt.element
|
|
49
|
+
can_ask = agent.approve is not None or (turn.session is not None and agent.forced_approve is not None)
|
|
50
|
+
if (attempt.op == "TYPE_SUBMIT" and can_ask and attempt.gate is not None
|
|
51
|
+
and attempt.gate.item is not None and proof_asks.asks(attempt.gate.item)
|
|
52
|
+
and attempt.gate.verbs == frozenset({"send"})):
|
|
53
|
+
# One message, one approval: type now, unasked; the Return asks with the typed text in its composer.
|
|
54
|
+
turn.chunk.insert(index + 1, Action("SUBMIT", target_label=element.label, locator=element.locator,
|
|
55
|
+
role=element.role))
|
|
56
|
+
attempt.op, attempt.gate = "TYPE", proof_screen.Gate(True)
|
|
57
|
+
attempt.key = (attempt.op, element.label, text)
|
|
58
|
+
attempt.spot = (attempt.op, element.locator or element.label, text, turn.snapshot.content_fingerprint)
|
|
59
|
+
agent._emit({"event": "frontier_split", "step": turn.step, "index": index, "reason": "ask_at_send"})
|
|
60
|
+
|
|
61
|
+
|
|
62
|
+
def account(agent, attempt, turn):
|
|
63
|
+
index = turn.index
|
|
64
|
+
op, element, gate = attempt.op, attempt.element, attempt.gate
|
|
65
|
+
if agent.commit_check is not None and element is not None:
|
|
66
|
+
commit = (gate.item.act if gate is not None and gate.item is not None and proof_asks.asks(gate.item)
|
|
67
|
+
else None) if agent._contract is not None else harness_api.commit_act(
|
|
68
|
+
op, element.label, None, getattr(turn.snapshot, "bundle_id", None))
|
|
69
|
+
agent.run_state.acting_as = agent.run_state.media_checked = None
|
|
70
|
+
if commit:
|
|
71
|
+
checked = agent.commit_check(commit, turn.snapshot) or {}
|
|
72
|
+
if checked.get("stop"):
|
|
73
|
+
agent._emit({"event": "frontier_refused", "step": turn.step, "label": element.label[:120],
|
|
74
|
+
"reason": "account"})
|
|
75
|
+
raise GuardStop(GuardVerdict("stop", checked.get("code") or "account_mismatch",
|
|
76
|
+
checked["stop"]))
|
|
77
|
+
if checked.get("refuse"):
|
|
78
|
+
turn.feedback = checked["refuse"]
|
|
79
|
+
turn.history.append(f"{turn.step}.{index}: {op} {element.label[:60]!r} refused (profile not "
|
|
80
|
+
"confirmed)")
|
|
81
|
+
agent._emit({"event": "frontier_refused", "step": turn.step, "label": element.label[:120],
|
|
82
|
+
"reason": "account"})
|
|
83
|
+
return "stop"
|
|
84
|
+
agent.run_state.acting_as = checked.get("acting_as")
|
|
85
|
+
if callable(checked.get("preview")):
|
|
86
|
+
# A check of the screen this commit is made on (the queue's media check): no picture, or any failure,
|
|
87
|
+
# is no match.
|
|
88
|
+
image = screen_image(agent.driver, getattr(turn.snapshot, "read_started", None))
|
|
89
|
+
try:
|
|
90
|
+
agent.run_state.media_checked = bool(image is not None and checked["preview"](image))
|
|
91
|
+
except Exception: # noqa: BLE001
|
|
92
|
+
agent.run_state.media_checked = False
|