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,599 @@
|
|
|
1
|
+
"""Runtime's task work: the Smart engine's run, the worker thread and a run's end."""
|
|
2
|
+
|
|
3
|
+
import threading
|
|
4
|
+
import time
|
|
5
|
+
from pathlib import Path
|
|
6
|
+
|
|
7
|
+
from . import (
|
|
8
|
+
ai_plans,
|
|
9
|
+
costs,
|
|
10
|
+
engines,
|
|
11
|
+
harness_api,
|
|
12
|
+
orchestrate,
|
|
13
|
+
permissions,
|
|
14
|
+
project_env,
|
|
15
|
+
result_blocks,
|
|
16
|
+
smart_events,
|
|
17
|
+
smart_result,
|
|
18
|
+
traces,
|
|
19
|
+
)
|
|
20
|
+
from .api_errors import APIError
|
|
21
|
+
from .compose import close_all
|
|
22
|
+
from .costs import SpendLedger
|
|
23
|
+
from .latency_trace import Trace
|
|
24
|
+
from .lockscreen import after_error
|
|
25
|
+
from .replay_core import path as replay_path
|
|
26
|
+
from .runtime_driver import PhoneNotReady, RunSetup
|
|
27
|
+
from .wda import watchdog as wda_watchdog
|
|
28
|
+
from .wda.guards import DriverRejection
|
|
29
|
+
|
|
30
|
+
# Finished runs whose full-size final screenshot stays in memory; older ones show their step frames.
|
|
31
|
+
KEPT_PREVIEWS = 3
|
|
32
|
+
MAX_SMART_APPS = 120
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
def smart_request(run):
|
|
36
|
+
pins = [pin["name"] for pin in run.pinned_apps or () if pin.get("name")]
|
|
37
|
+
if len(pins) > 1:
|
|
38
|
+
return f"In {orchestrate.joined(pins)}: {run.goal}"
|
|
39
|
+
return f"In {run.app['name']}: {run.goal}" if run.app.get("bundleId") else run.goal
|
|
40
|
+
|
|
41
|
+
|
|
42
|
+
# Each provider runs on its own thread while the phone gets ready; join waits until BUDGET after the gather began.
|
|
43
|
+
class ContextGather:
|
|
44
|
+
BUDGET = 2.0
|
|
45
|
+
|
|
46
|
+
def __init__(self, ctx, emit, budget=None):
|
|
47
|
+
self.ctx, self.emit = ctx, emit
|
|
48
|
+
self.budget = self.BUDGET if budget is None else budget
|
|
49
|
+
self.started = time.monotonic()
|
|
50
|
+
self.providers = harness_api.context_providers(ctx)
|
|
51
|
+
self.jobs = []
|
|
52
|
+
for provider in self.providers:
|
|
53
|
+
box = {}
|
|
54
|
+
thread = threading.Thread(target=self._collect, args=(provider, box), daemon=True,
|
|
55
|
+
name=f"mobster-context-{getattr(provider, 'key', '?')}")
|
|
56
|
+
thread.start()
|
|
57
|
+
self.jobs.append((provider, box, thread))
|
|
58
|
+
|
|
59
|
+
def _collect(self, provider, box):
|
|
60
|
+
try:
|
|
61
|
+
box["blocks"] = list(provider.blocks(self.ctx) or ())
|
|
62
|
+
except BaseException as error: # noqa: BLE001
|
|
63
|
+
box["error"] = type(error).__name__
|
|
64
|
+
|
|
65
|
+
def join(self):
|
|
66
|
+
deadline = self.started + self.budget
|
|
67
|
+
blocks, ready, used, chars = [], [], [], 0
|
|
68
|
+
for provider, box, thread in self.jobs:
|
|
69
|
+
thread.join(max(0.0, deadline - time.monotonic()))
|
|
70
|
+
key = str(getattr(provider, "key", "") or "")[:40]
|
|
71
|
+
if thread.is_alive():
|
|
72
|
+
self.emit({"event": "context_error", "key": key, "error": "timeout"})
|
|
73
|
+
continue
|
|
74
|
+
if "error" in box:
|
|
75
|
+
self.emit({"event": "context_error", "key": key, "error": box["error"]})
|
|
76
|
+
continue
|
|
77
|
+
ready.append(provider)
|
|
78
|
+
cap = getattr(provider, "max_chars", None)
|
|
79
|
+
for block in box.get("blocks", ()):
|
|
80
|
+
if not isinstance(block, harness_api.ContextBlock) or not block.text:
|
|
81
|
+
continue
|
|
82
|
+
if isinstance(cap, int) and cap > 0 and len(block.text) > cap:
|
|
83
|
+
block = harness_api.ContextBlock(block.key, block.title, block.text[:cap - 1] + "…",
|
|
84
|
+
block.stable, block.untrusted, block.images)
|
|
85
|
+
blocks.append(block)
|
|
86
|
+
chars += len(block.text)
|
|
87
|
+
if key not in used:
|
|
88
|
+
used.append(key)
|
|
89
|
+
if blocks:
|
|
90
|
+
self.emit({"event": "context_used", "keys": used, "chars": chars})
|
|
91
|
+
return blocks, ready
|
|
92
|
+
|
|
93
|
+
def turn_context(self, providers):
|
|
94
|
+
# A provider that raises is skipped for that turn.
|
|
95
|
+
ctx = self.ctx
|
|
96
|
+
|
|
97
|
+
def turn(state):
|
|
98
|
+
out = []
|
|
99
|
+
for provider in providers:
|
|
100
|
+
try:
|
|
101
|
+
out.extend(provider.turn_blocks(ctx, state) or ())
|
|
102
|
+
except Exception: # noqa: BLE001
|
|
103
|
+
harness_api.log.exception("a provider's turn_blocks failed")
|
|
104
|
+
return [block for block in out if isinstance(block, harness_api.ContextBlock) and block.text]
|
|
105
|
+
return turn
|
|
106
|
+
|
|
107
|
+
|
|
108
|
+
class TaskWorkMixin:
|
|
109
|
+
def smart_apps(self, run):
|
|
110
|
+
apps = {}
|
|
111
|
+
if run.app.get("bundleId"):
|
|
112
|
+
apps[run.app["bundleId"]] = run.app["name"]
|
|
113
|
+
for pin in run.pinned_apps or ():
|
|
114
|
+
if pin.get("bundleId"):
|
|
115
|
+
apps.setdefault(pin["bundleId"], pin["name"])
|
|
116
|
+
for app in self.apps_for(self.phone_for(run)):
|
|
117
|
+
bundle = app.get("bundleId")
|
|
118
|
+
if bundle and app.get("installed") is not False and bundle not in apps:
|
|
119
|
+
apps[bundle] = app["name"]
|
|
120
|
+
allowed = getattr(run, "phone_apps", None)
|
|
121
|
+
if isinstance(allowed, frozenset):
|
|
122
|
+
apps = {bundle: name for bundle, name in apps.items() if bundle in allowed}
|
|
123
|
+
return dict(list(apps.items())[:MAX_SMART_APPS])
|
|
124
|
+
|
|
125
|
+
def estimate_for(self, engine, goal, output_schema=None, output_format=None):
|
|
126
|
+
try:
|
|
127
|
+
estimates = self.engine_estimates(goal, output_schema, output_format=output_format)
|
|
128
|
+
if engine not in estimates:
|
|
129
|
+
return None
|
|
130
|
+
# The figure kept with the run is what the model would cost: a path that costs nothing can still hand over.
|
|
131
|
+
return costs.midpoint({**estimates[engine], **estimates[engine].get("fallbackUsd", {})})
|
|
132
|
+
except Exception:
|
|
133
|
+
return None
|
|
134
|
+
|
|
135
|
+
def engine_estimates(self, goal="", output_schema=None, output_format=None):
|
|
136
|
+
state = self.engines_status()
|
|
137
|
+
names = [app["name"] for app in self.apps() if app.get("installed") is not False]
|
|
138
|
+
structured = output_schema is not None or output_format in smart_result.AUTOMATIC_FORMATS
|
|
139
|
+
estimate = costs.smart_estimate(goal, names, available=state[engines.SMART]["available"],
|
|
140
|
+
reason=state[engines.SMART]["reason"], structured=structured,
|
|
141
|
+
model=state[engines.SMART]["model"])
|
|
142
|
+
free = None if structured or not goal else self.free_kind(goal)
|
|
143
|
+
if free is not None:
|
|
144
|
+
# A request that costs no model call: $0, and what the model would cost if the path doesn't fit is kept.
|
|
145
|
+
estimate = {**estimate, "lowUsd": 0.0, "highUsd": 0.0, "basis": free,
|
|
146
|
+
"fallbackUsd": {"lowUsd": estimate["lowUsd"], "highUsd": estimate["highUsd"]}}
|
|
147
|
+
return {engines.SMART: estimate}
|
|
148
|
+
|
|
149
|
+
def free_kind(self, goal):
|
|
150
|
+
from .learned import zero
|
|
151
|
+
state_db = getattr(self.config, "state_db", None)
|
|
152
|
+
return zero.free_kind(Path(state_db).parent if state_db else None, goal)
|
|
153
|
+
|
|
154
|
+
def run_smart(self, run, trace, setup):
|
|
155
|
+
key = engines.smart_key()
|
|
156
|
+
ledger = SpendLedger()
|
|
157
|
+
keyless = harness_api.calls_no_model(run.extras)
|
|
158
|
+
with trace.span("setup.models"):
|
|
159
|
+
client = None if keyless else self.smart_client() # a run that calls no model opens no connection
|
|
160
|
+
long_task = getattr(run, "longform", None)
|
|
161
|
+
events = smart_events.SmartEvents(run.emit, ledger=ledger if long_task else None)
|
|
162
|
+
|
|
163
|
+
def inference(event):
|
|
164
|
+
ledger.add_event(event)
|
|
165
|
+
events.inference(event)
|
|
166
|
+
|
|
167
|
+
if client is not None:
|
|
168
|
+
costs.meter(client, inference)
|
|
169
|
+
session = None
|
|
170
|
+
recording = traces.Recording(getattr(self, "frames", None), run.id, smart_request(run))
|
|
171
|
+
try:
|
|
172
|
+
ctx = self.run_context(run)
|
|
173
|
+
hooks_ = harness_api.pre_runs(ctx)
|
|
174
|
+
handoff = None
|
|
175
|
+
for hook in [h for h in hooks_ if getattr(h, "before_phone", False)]:
|
|
176
|
+
if run.stop.is_set():
|
|
177
|
+
return None
|
|
178
|
+
try:
|
|
179
|
+
done = hook(ctx, None, None)
|
|
180
|
+
except Exception as error: # noqa: BLE001 -- a track's hook never breaks the run
|
|
181
|
+
harness_api.log.exception("a pre-run hook failed")
|
|
182
|
+
run.emit({"event": "context_error", "key": "pre_run", "error": type(error).__name__})
|
|
183
|
+
continue
|
|
184
|
+
if done is not None and done.result is not None:
|
|
185
|
+
result = {**done.result, "costUsd": run.cost_usd}
|
|
186
|
+
run.emit(result)
|
|
187
|
+
return result
|
|
188
|
+
if done is not None and done.handoff is not None and handoff is None:
|
|
189
|
+
handoff = done.handoff
|
|
190
|
+
if (run.extras or {}).get("resumeFrom") and handoff is None:
|
|
191
|
+
# A disabled or missing checkpoint hook must not turn a pickup into a fresh task.
|
|
192
|
+
from .harness import checkpoints
|
|
193
|
+
result = checkpoints._pickup_unavailable(ctx, "missing_hook").result
|
|
194
|
+
run.emit(result)
|
|
195
|
+
return result
|
|
196
|
+
# The plan, before the phone is touched: Start, Skip or a change.
|
|
197
|
+
ended = self.confirm_plan(run)
|
|
198
|
+
if ended is not None:
|
|
199
|
+
run.emit(ended)
|
|
200
|
+
return ended
|
|
201
|
+
long_task = getattr(run, "longform", None)
|
|
202
|
+
# A run that calls no model reads no context: its hook ends it, and nothing reads a provider's blocks.
|
|
203
|
+
gather = None if keyless else ContextGather(ctx, run.emit)
|
|
204
|
+
driver = self.open_driver(run, trace, setup)
|
|
205
|
+
if driver is None:
|
|
206
|
+
return None
|
|
207
|
+
run.capture = getattr(driver, "capture_preview", None)
|
|
208
|
+
events.driver, events.apps = driver, self.smart_apps(run)
|
|
209
|
+
gate = self.permission_gate(run)
|
|
210
|
+
# Full access has no approver; a one-time code the agent types still asks, through floor_approve.
|
|
211
|
+
approve = events.approving(run.request_approval) if gate.mode != permissions.DONT_ASK else None
|
|
212
|
+
floor_approve = None if approve is not None else events.approving(run.request_approval)
|
|
213
|
+
from .accounts.screen import commit_check_for
|
|
214
|
+
commit_check = commit_check_for(ctx)
|
|
215
|
+
# iOS's privacy prompts ask the person in every permission mode, even with Ask before acting off.
|
|
216
|
+
dialog_approve = approve or (events.approving(run.request_approval) if ctx.interactive else None)
|
|
217
|
+
program = parts = None
|
|
218
|
+
for hook in hooks_:
|
|
219
|
+
if getattr(hook, "before_phone", False):
|
|
220
|
+
continue
|
|
221
|
+
if (handoff is not None or program is not None) and not getattr(hook, "after_handoff", False):
|
|
222
|
+
continue
|
|
223
|
+
if run.stop.is_set():
|
|
224
|
+
return None
|
|
225
|
+
try:
|
|
226
|
+
# No approver: a routine hands off at its first commit, which the loop checks the account for.
|
|
227
|
+
done = hook(ctx, driver, approve if commit_check is None else None)
|
|
228
|
+
except Exception as error: # noqa: BLE001 -- a hook with the phone may have acted before it failed
|
|
229
|
+
harness_api.log.exception("a pre-run hook failed")
|
|
230
|
+
run.emit({"event": "context_error", "key": "pre_run", "error": type(error).__name__})
|
|
231
|
+
result = {"event": "result", "engine": engines.SMART, "status": "completion_not_confirmed",
|
|
232
|
+
"outcome": "check", "code": "outcome_unknown", "answer": None, "proof": [],
|
|
233
|
+
"independently_verified": False, "actions_may_have_run": True, "costUsd": run.cost_usd,
|
|
234
|
+
"reason": "Mobster stopped while preparing the task. An action may have run. "
|
|
235
|
+
"Inspect your iPhone before starting another task."}
|
|
236
|
+
run.emit(result)
|
|
237
|
+
return result
|
|
238
|
+
if done is None:
|
|
239
|
+
continue
|
|
240
|
+
if handoff is None and done.result is not None:
|
|
241
|
+
result = {**done.result, "costUsd": run.cost_usd}
|
|
242
|
+
run.emit(result)
|
|
243
|
+
return result
|
|
244
|
+
if handoff is None and done.handoff is not None:
|
|
245
|
+
handoff = done.handoff
|
|
246
|
+
if handoff is None and program is None and getattr(done, "program", None) is not None:
|
|
247
|
+
program = done.program # a saved program: the live loop takes its steps first
|
|
248
|
+
if parts is None and getattr(done, "parts", None) is not None:
|
|
249
|
+
parts = done.parts # parts of saved programs: the live loop may run one at a planning point
|
|
250
|
+
if keyless:
|
|
251
|
+
# A hook that doesn't end a run that calls no model leaves nothing to run: the loop below is a model's.
|
|
252
|
+
raise RuntimeError("Nothing ran this task, which calls no model. No action was taken; try again.")
|
|
253
|
+
if long_task:
|
|
254
|
+
from .longform import plan as long_plan
|
|
255
|
+
from .longform import session as long_session
|
|
256
|
+
card = long_task["plan"]
|
|
257
|
+
session = long_session.start(
|
|
258
|
+
run_id=run.id, goal=run.goal, plan=card, budget=long_plan.budget_of(card), origin=run.origin,
|
|
259
|
+
nav_client=client, key=key, inference=inference, ledger=ledger, cap_usd=self.run_cap(run),
|
|
260
|
+
emit=events, frames=getattr(self, "frames", None), journal=self.journal, awake=self.awake,
|
|
261
|
+
spent_offset=float(getattr(handoff, "cost_usd", 0.0) or 0.0), rules=self.thread_rules(ctx),
|
|
262
|
+
earlier=self.earlier_seen(ctx, card), run=run)
|
|
263
|
+
blocks, providers = gather.join()
|
|
264
|
+
if session is not None:
|
|
265
|
+
session.guidance = tuple(b.text for b in blocks or () if getattr(b, "key", None) == "plugins")
|
|
266
|
+
hooks = {}
|
|
267
|
+
if blocks:
|
|
268
|
+
hooks["context_blocks"] = blocks
|
|
269
|
+
if providers:
|
|
270
|
+
hooks["turn_context"] = gather.turn_context(providers)
|
|
271
|
+
extra_tools = harness_api.tools(ctx)
|
|
272
|
+
withheld = harness_api.WITHHELD_SKILLS.get(run.origin, frozenset())
|
|
273
|
+
if extra_tools or withheld:
|
|
274
|
+
from .frontier import default_skills
|
|
275
|
+
hooks["skills"] = tuple(skill for skill in (*default_skills(), *extra_tools)
|
|
276
|
+
if getattr(skill, "op", None) not in withheld)
|
|
277
|
+
if commit_check is not None:
|
|
278
|
+
# A task as an account: every commit in its app waits for the account confirmed on screen.
|
|
279
|
+
hooks["commit_check"] = commit_check
|
|
280
|
+
try:
|
|
281
|
+
hooks.update(harness_api.frontier_options(ctx))
|
|
282
|
+
except Exception as error: # noqa: BLE001
|
|
283
|
+
harness_api.log.exception("frontier options failed")
|
|
284
|
+
run.emit({"event": "context_error", "key": "frontier_options", "error": type(error).__name__})
|
|
285
|
+
listeners = run.listeners if harness_api.listeners() else None
|
|
286
|
+
if listeners is not None:
|
|
287
|
+
# Only the hooks a listener takes: the checkpoint hook runs the secret filter on the agent's thread.
|
|
288
|
+
wanted = {name for listener in harness_api.listeners() for name in ("on_step", "on_checkpoint")
|
|
289
|
+
if callable(getattr(listener, name, None))}
|
|
290
|
+
if "on_step" in wanted:
|
|
291
|
+
hooks["step_observer"] = lambda record: listeners.post("on_step", run, record)
|
|
292
|
+
if "on_checkpoint" in wanted:
|
|
293
|
+
hooks["checkpoint"] = lambda state: listeners.post("on_checkpoint", run, state)
|
|
294
|
+
# A long run's DONE turn carries no data: its schema is applied to the final report instead.
|
|
295
|
+
nav_schema = None
|
|
296
|
+
if session is None:
|
|
297
|
+
nav_schema = run.output_schema
|
|
298
|
+
if nav_schema is None and run.output_format in smart_result.AUTOMATIC_FORMATS:
|
|
299
|
+
nav_schema = smart_result.TABLE
|
|
300
|
+
if session is not None:
|
|
301
|
+
# A long run asks before every act that asks, with Ask before acting off too: forced_approve asks.
|
|
302
|
+
hooks.update(longform=session, forced_approve=events.approving(run.request_approval))
|
|
303
|
+
agent = engines.build_frontier(driver, client, apps=events.apps, emit=events,
|
|
304
|
+
max_cost_usd=self.run_cap(run),
|
|
305
|
+
approve=approve, cancelled=run.stop.is_set, steering=run.steering,
|
|
306
|
+
clarify=ctx.clarify, run_context=ctx, dialog_approve=dialog_approve,
|
|
307
|
+
permissions=gate, floor_approve=floor_approve,
|
|
308
|
+
permission_mode=gate.mode, saved_program=program,
|
|
309
|
+
program_parts=parts,
|
|
310
|
+
output_schema=nav_schema,
|
|
311
|
+
guard=run.guard, recorder=recording.attach, **hooks)
|
|
312
|
+
events.agent = agent
|
|
313
|
+
if run.stop.is_set():
|
|
314
|
+
return None # The loop's first step is a model call (the task contract), billed even if stopped.
|
|
315
|
+
setup.phase("running")
|
|
316
|
+
request = smart_request(run)
|
|
317
|
+
if (run.extras or {}).get("projectEnv"):
|
|
318
|
+
# The goal keeps ${NAME}; the agent gets the values and masks them from its first turn on.
|
|
319
|
+
values = project_env.for_task(run.extras["projectEnv"], request)
|
|
320
|
+
request = project_env.expand(request, values)
|
|
321
|
+
agent.preset_secrets = set(values.values())
|
|
322
|
+
with trace.span("smart.run"):
|
|
323
|
+
outcome = agent.run(request, initial=handoff) if handoff is not None else agent.run(request)
|
|
324
|
+
recording.finish(outcome)
|
|
325
|
+
report = markdown = None
|
|
326
|
+
if session is not None:
|
|
327
|
+
try:
|
|
328
|
+
session.capture_contract(getattr(agent, "_contract", None), final=True)
|
|
329
|
+
except Exception: # receipt persistence cannot prevent phone release or report synthesis
|
|
330
|
+
try:
|
|
331
|
+
run.emit({"event": "longform_receipt_unavailable"})
|
|
332
|
+
except Exception:
|
|
333
|
+
pass
|
|
334
|
+
# Synthesis needs no phone, so the phone is free before the report is written.
|
|
335
|
+
setup.finish_phone()
|
|
336
|
+
session.release_phone()
|
|
337
|
+
events.finish()
|
|
338
|
+
run.private = {"notes": list(outcome.get("notes") or ()), "plan": outcome.get("plan") or "",
|
|
339
|
+
"contract": [{name: getattr(item, name, "") for name in (
|
|
340
|
+
"kind", "app", "what", "payload", "act", "count", "condition", "quote")}
|
|
341
|
+
for item in (getattr(getattr(agent, "_contract", None), "items", None) or ())],
|
|
342
|
+
# Hashes with no text: a replay that ends on this screen with its typed words is done.
|
|
343
|
+
"screen": replay_path.screen_hashes(getattr(agent, "last_screen", None))}
|
|
344
|
+
if run.stop.is_set() and outcome.get("status") not in ("approval_denied", "approval_timeout"):
|
|
345
|
+
outcome = {**outcome, "status": "stopped"}
|
|
346
|
+
if session is not None:
|
|
347
|
+
from .longform.synthesis import synthesize
|
|
348
|
+
with trace.span("longform.report"):
|
|
349
|
+
report, markdown = synthesize(session, outcome.get("status") or "error",
|
|
350
|
+
answer=outcome.get("answer"))
|
|
351
|
+
result = smart_result.summarize(outcome, agent, events, request=request, output_format=run.output_format,
|
|
352
|
+
output_schema=run.output_schema, cost_usd=run.cost_usd, report=report,
|
|
353
|
+
markdown=markdown)
|
|
354
|
+
blocks = result_blocks.from_report(report) if report is not None else None
|
|
355
|
+
if blocks is not None:
|
|
356
|
+
result["blocks"] = blocks
|
|
357
|
+
if report is not None and report.get("partial"):
|
|
358
|
+
from .longform.report import partial_line
|
|
359
|
+
result["reason"] = partial_line(report, session.budget) or result.get("reason")
|
|
360
|
+
self.learn_reach(key, client, outcome.get("reason"), result.get("structure_error"))
|
|
361
|
+
result["costUsd"] = run.cost_usd
|
|
362
|
+
self.note_limit(run, result)
|
|
363
|
+
run.emit(result)
|
|
364
|
+
return result
|
|
365
|
+
finally:
|
|
366
|
+
recording.finish()
|
|
367
|
+
if session is not None:
|
|
368
|
+
setup.finish_phone()
|
|
369
|
+
session.release_phone()
|
|
370
|
+
session.close()
|
|
371
|
+
engines.release_client(client)
|
|
372
|
+
|
|
373
|
+
def thread_rules(self, ctx):
|
|
374
|
+
thread_id = getattr(ctx, "thread_id", None)
|
|
375
|
+
if not thread_id:
|
|
376
|
+
return ()
|
|
377
|
+
try:
|
|
378
|
+
from .threads.service import service_for
|
|
379
|
+
service = service_for(self)
|
|
380
|
+
thread = service.store.get(thread_id) if service is not None else None
|
|
381
|
+
return tuple(str(rule) for rule in (thread or {}).get("rules") or () if str(rule).strip())[:12]
|
|
382
|
+
except Exception: # noqa: BLE001
|
|
383
|
+
return ()
|
|
384
|
+
|
|
385
|
+
def earlier_seen(self, ctx, card):
|
|
386
|
+
if (card or {}).get("kind") not in ("feed", "browse", "survey"):
|
|
387
|
+
return set()
|
|
388
|
+
try:
|
|
389
|
+
from .longform.recall import earlier_identities
|
|
390
|
+
return earlier_identities(self, getattr(ctx, "thread_id", None), ctx.run_id, card.get("kind"))
|
|
391
|
+
except Exception: # noqa: BLE001
|
|
392
|
+
return set()
|
|
393
|
+
|
|
394
|
+
def agents_status(self, max_age=60.0, wait=False):
|
|
395
|
+
from .ai_plans import agents
|
|
396
|
+
return agents.snapshot(max_age=max_age, wait=wait)
|
|
397
|
+
|
|
398
|
+
def plan_agent(self):
|
|
399
|
+
if self.scripted_models() or engines.smart_key():
|
|
400
|
+
return None
|
|
401
|
+
from .ai_plans import agents
|
|
402
|
+
return agents.ready()
|
|
403
|
+
|
|
404
|
+
def check_agent(self, engine):
|
|
405
|
+
from .ai_plans import agents
|
|
406
|
+
agent = agents.get(engine)
|
|
407
|
+
state = agent.status(max_age=30)
|
|
408
|
+
if not state.get("ready"):
|
|
409
|
+
agent.forget_status()
|
|
410
|
+
state = agent.status(max_age=0)
|
|
411
|
+
if not state.get("ready"):
|
|
412
|
+
reason = (f"{agent.name} isn't installed on this Mac." if not state.get("installed")
|
|
413
|
+
else agent.signed_out_sentence())
|
|
414
|
+
raise APIError(reason, 409, "engine_unavailable", engine=engine,
|
|
415
|
+
agentState="not_installed" if not state.get("installed") else "signed_out")
|
|
416
|
+
|
|
417
|
+
def run_agent(self, run, trace, setup):
|
|
418
|
+
from .ai_plans import runner
|
|
419
|
+
return runner.run(self, run, trace, setup)
|
|
420
|
+
|
|
421
|
+
def learn_reach(self, key, client, *errors):
|
|
422
|
+
reach = getattr(self, "model_reach", None)
|
|
423
|
+
if reach is None:
|
|
424
|
+
return
|
|
425
|
+
for error in errors:
|
|
426
|
+
ok, why = engines.model_failure(error)
|
|
427
|
+
if ok is False:
|
|
428
|
+
reach.record(key, False, why)
|
|
429
|
+
return
|
|
430
|
+
if (getattr(client, "usage", None) or {}).get("calls") and not getattr(client, "model_failed", True):
|
|
431
|
+
reach.record(key, True)
|
|
432
|
+
|
|
433
|
+
def work(self, run):
|
|
434
|
+
driver = None
|
|
435
|
+
trace = Trace()
|
|
436
|
+
phase = "preparing"
|
|
437
|
+
result = {"status": "stopped", "independently_verified": False,
|
|
438
|
+
"reason": "The task stopped before executing agent actions."}
|
|
439
|
+
|
|
440
|
+
def set_phase(value):
|
|
441
|
+
nonlocal phase
|
|
442
|
+
phase = value
|
|
443
|
+
|
|
444
|
+
def hold(value):
|
|
445
|
+
nonlocal driver
|
|
446
|
+
driver = value
|
|
447
|
+
return value
|
|
448
|
+
|
|
449
|
+
def finish_phone():
|
|
450
|
+
nonlocal driver
|
|
451
|
+
if run.phone_finished:
|
|
452
|
+
return
|
|
453
|
+
# Drain a concurrent preview before closing its driver. Never capture after the lease moves.
|
|
454
|
+
if driver and not run.stop.is_set() and not self.closing:
|
|
455
|
+
with trace.span("finish.preview"):
|
|
456
|
+
self.preview(run, final=True)
|
|
457
|
+
with run.image_lock:
|
|
458
|
+
run.phone_finished = True
|
|
459
|
+
run.capture = None
|
|
460
|
+
if run.guard is not None and driver is not None:
|
|
461
|
+
try:
|
|
462
|
+
run.guard.finish(driver)
|
|
463
|
+
except Exception:
|
|
464
|
+
pass
|
|
465
|
+
with trace.span("finish.close"):
|
|
466
|
+
close_all(driver)
|
|
467
|
+
driver = None
|
|
468
|
+
run.guard = None
|
|
469
|
+
if run.lease:
|
|
470
|
+
run.lease.close()
|
|
471
|
+
run.lease = None
|
|
472
|
+
with self.lock:
|
|
473
|
+
self.active_runs.pop(run.id, None)
|
|
474
|
+
# Admission also holds this lock; end the old tracker state before a new run can start.
|
|
475
|
+
wda_watchdog.tracker.run_ended(self.run_phone_url(run), run.id)
|
|
476
|
+
try:
|
|
477
|
+
self.run_ended(run)
|
|
478
|
+
except Exception: # queue notification cannot change a task's completed phone work or report
|
|
479
|
+
harness_api.log.exception("phone released but its queue could not advance")
|
|
480
|
+
|
|
481
|
+
try:
|
|
482
|
+
if run.stop.is_set():
|
|
483
|
+
return
|
|
484
|
+
run.status = "running"
|
|
485
|
+
run.started_at = time.time()
|
|
486
|
+
self.power.run_started(run)
|
|
487
|
+
run.emit({"event": "run_started", "mode": run.mode, "app": run.app["name"], "engine": run.engine})
|
|
488
|
+
if ai_plans.is_agent(run.engine):
|
|
489
|
+
done = self.run_agent(run, trace, RunSetup(phase=set_phase, hold=hold,
|
|
490
|
+
finish_phone=finish_phone))
|
|
491
|
+
if done is not None:
|
|
492
|
+
result = done
|
|
493
|
+
return
|
|
494
|
+
done = self.run_smart(run, trace, RunSetup(phase=set_phase, hold=hold,
|
|
495
|
+
finish_phone=finish_phone))
|
|
496
|
+
if done is not None:
|
|
497
|
+
result = done
|
|
498
|
+
except Exception as exc:
|
|
499
|
+
inactive_app = phase == "reading_app" and isinstance(exc, DriverRejection) and exc.code == "app_not_active"
|
|
500
|
+
outdated_bridge = (phase == "reading_app" and isinstance(exc, DriverRejection)
|
|
501
|
+
and exc.code == "bridge_update_required")
|
|
502
|
+
result = {"event": "result", "status": "stopped" if run.stop.is_set() else "error",
|
|
503
|
+
"independently_verified": False, "error_type": type(exc).__name__,
|
|
504
|
+
"native_code": exc.code if isinstance(exc, DriverRejection) else None,
|
|
505
|
+
"actions_may_have_run": phase in {"running", "launching"},
|
|
506
|
+
"reason": ("The task was stopped while preparing the app."
|
|
507
|
+
if run.stop.is_set() and phase == "preparing" else
|
|
508
|
+
str(exc) if isinstance(exc, PhoneNotReady) else
|
|
509
|
+
"The app launch could not be confirmed. It may have opened; the launch was not retried."
|
|
510
|
+
if phase == "launching" else
|
|
511
|
+
(f"iOS has not activated {run.app['name']}. Its accessibility bridge is responding, "
|
|
512
|
+
"but the app is inactive. No agent action was dispatched.") if inactive_app else
|
|
513
|
+
(f"{run.app['name']} is still running an outdated interaction bridge. Close and reopen "
|
|
514
|
+
"the app to load the installed update. No agent action was dispatched.")
|
|
515
|
+
if outdated_bridge else
|
|
516
|
+
(f"Could not read {run.app['name']} on your iPhone. Check for a locked screen or a "
|
|
517
|
+
"system prompt, then try again. No agent action was dispatched.")
|
|
518
|
+
if phase == "reading_app" else
|
|
519
|
+
"The task could not prepare your app. No agent action was dispatched."
|
|
520
|
+
if phase == "preparing" else
|
|
521
|
+
"The task stopped after a service error. An in-flight action may have run; "
|
|
522
|
+
"it was not retried.")}
|
|
523
|
+
if isinstance(exc, PhoneNotReady) and not run.stop.is_set():
|
|
524
|
+
result["stop_code"] = exc.code # phone_locked, unplugged ... (agent_hooks.STOP_CODES)
|
|
525
|
+
elif not run.phone_finished and not run.stop.is_set() and phase in {"running", "launching"}:
|
|
526
|
+
# A step failed outside the loop's guard: ask the phone once whether it was unplugged or locked.
|
|
527
|
+
try:
|
|
528
|
+
verdict = after_error(run.guard, driver)
|
|
529
|
+
except Exception: # noqa: BLE001 -- the service-error sentence stands
|
|
530
|
+
verdict = None
|
|
531
|
+
if verdict is not None:
|
|
532
|
+
result.update(reason=verdict.message, stop_code=verdict.code)
|
|
533
|
+
result.update(engine=run.engine, costUsd=run.cost_usd,
|
|
534
|
+
outcome="stopped" if run.stop.is_set() else "couldnt_finish", answer=None, proof=[])
|
|
535
|
+
finally:
|
|
536
|
+
self.awake.release(run.id)
|
|
537
|
+
self.power.run_finished(run)
|
|
538
|
+
finish_phone()
|
|
539
|
+
try:
|
|
540
|
+
run.emit(trace.event())
|
|
541
|
+
except Exception:
|
|
542
|
+
pass # Telemetry never changes a run's outcome.
|
|
543
|
+
try:
|
|
544
|
+
run.finish(result)
|
|
545
|
+
finally:
|
|
546
|
+
with self.lock:
|
|
547
|
+
self.threads.discard(threading.current_thread())
|
|
548
|
+
self.release(run)
|
|
549
|
+
try:
|
|
550
|
+
self.finished(run)
|
|
551
|
+
except Exception: # noqa: BLE001 -- listeners and the bus never change a run's outcome
|
|
552
|
+
harness_api.log.exception("run_finished listeners failed")
|
|
553
|
+
run.settled.set()
|
|
554
|
+
|
|
555
|
+
def release(self, run):
|
|
556
|
+
# Otherwise every run kept its driver's screens and trees until it left the 100-run history.
|
|
557
|
+
run.capture = None
|
|
558
|
+
run.guard = None
|
|
559
|
+
with self.lock:
|
|
560
|
+
shown = sorted((r for r in self.runs.values() if r.finished_at is not None and r.image is not None),
|
|
561
|
+
key=lambda r: r.finished_at, reverse=True)
|
|
562
|
+
for old in shown[KEPT_PREVIEWS:]:
|
|
563
|
+
if old.image_lock.acquire(blocking=False): # held only while a capture is in flight
|
|
564
|
+
try:
|
|
565
|
+
old.image, old.image_captured_at = None, None
|
|
566
|
+
finally:
|
|
567
|
+
old.image_lock.release()
|
|
568
|
+
|
|
569
|
+
def finished(self, run):
|
|
570
|
+
# Listeners' on_finished, then the bus's run_finished, in that order, on the listeners' thread.
|
|
571
|
+
listeners = getattr(run, "listeners", None)
|
|
572
|
+
thread_id = (run.extras or {}).get("threadId") if isinstance(getattr(run, "extras", None), dict) else None
|
|
573
|
+
event = {"event": "run_finished", "runId": run.id, "status": run.status,
|
|
574
|
+
**({"threadId": thread_id} if thread_id else {})}
|
|
575
|
+
fanout = getattr(self, "fanout", None) if getattr(run, "group_id", None) else None
|
|
576
|
+
fleet = getattr(self, "fleet", None)
|
|
577
|
+
if fleet is not None and getattr(fleet, "health", None) is not None:
|
|
578
|
+
fleet.health.note_run(run)
|
|
579
|
+
if listeners is None:
|
|
580
|
+
self.publish("runs", event)
|
|
581
|
+
if fanout is not None:
|
|
582
|
+
fanout.member_finished(run)
|
|
583
|
+
run.settled.set()
|
|
584
|
+
return
|
|
585
|
+
listeners.post("on_finished", run, run.summary, dict(run.private or {}))
|
|
586
|
+
listeners.call(run.settled.set)
|
|
587
|
+
listeners.call(lambda: self.publish("runs", event))
|
|
588
|
+
if fanout is not None:
|
|
589
|
+
# After the conversation has the run's result: the group's summary reads every phone's.
|
|
590
|
+
listeners.call(lambda: fanout.member_finished(run))
|
|
591
|
+
|
|
592
|
+
def _finished_run(self, identifier, still_running=None):
|
|
593
|
+
with self.lock:
|
|
594
|
+
run = self.runs.get(identifier)
|
|
595
|
+
if not run:
|
|
596
|
+
raise APIError("Task not found", 404, "not_found")
|
|
597
|
+
if still_running and run.finished_at is None:
|
|
598
|
+
raise APIError(still_running, 409, "run_active")
|
|
599
|
+
return run
|