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,644 @@
|
|
|
1
|
+
"""A long task's run-wide state (DESIGN §13.1): the plan, budget, notebook and models, and the run's spend and time."""
|
|
2
|
+
|
|
3
|
+
import math
|
|
4
|
+
import threading
|
|
5
|
+
import time
|
|
6
|
+
|
|
7
|
+
from . import context as ctx_text
|
|
8
|
+
from .notebook import Notebook, cut
|
|
9
|
+
|
|
10
|
+
# longform_progress is journaled like every event, and the app reads only the newest: one at most every 5 s while
|
|
11
|
+
# nothing changes (a 10-minute run journals about 120), at once on a new phase and on every 10th item.
|
|
12
|
+
PROGRESS_INTERVAL = 5.0
|
|
13
|
+
STORE_EVERY_ITEMS = 10
|
|
14
|
+
WRAP_UP_SHARE = 0.8
|
|
15
|
+
COVERED_GRACE_TURNS = 2 # turns the model may take after the budget is covered before the run ends
|
|
16
|
+
# A run that makes no progress ends early with a partial report (review of PR #150, P1-1): the fake Calendar that
|
|
17
|
+
# ignored app switches kept a run repeating LAUNCH_APP for 38 turns, until the spend cap.
|
|
18
|
+
STALL_TURNS = 6 # turns in a row with no new item, note, step change or screen
|
|
19
|
+
REFUSED_IN_A_ROW = 3 # actions refused in a row (a loop, a repeat, a gate)
|
|
20
|
+
DRY_MACROS = 2 # macro returns in a row that saved nothing and say the feed or list is done
|
|
21
|
+
DRY_REASONS = frozenset({"stuck", "looping", "not_feed", "end", "refused"})
|
|
22
|
+
MAX_RULES, MAX_RULE_CHARS = 12, 160 # the conversation's standing rules a long run ranks against
|
|
23
|
+
|
|
24
|
+
_sessions = {}
|
|
25
|
+
_lock = threading.Lock()
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
def for_run(run_id):
|
|
29
|
+
"""The run's LongSession, or None (not a long run, or it ended)."""
|
|
30
|
+
with _lock:
|
|
31
|
+
return _sessions.get(run_id)
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
def bind(session):
|
|
35
|
+
with _lock:
|
|
36
|
+
_sessions[session.run_id] = session
|
|
37
|
+
return session
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
def unbind(run_id):
|
|
41
|
+
with _lock:
|
|
42
|
+
return _sessions.pop(run_id, None)
|
|
43
|
+
|
|
44
|
+
|
|
45
|
+
class LongSession:
|
|
46
|
+
# The prompt text of a long task (context.py), for FrontierAgent: the engine reaches it through the session.
|
|
47
|
+
context = ctx_text
|
|
48
|
+
|
|
49
|
+
def __init__(self, run_id, request, plan, budget, *, models=None, interactive=True, focus=None,
|
|
50
|
+
preferences=(), ledger=None, cap_usd=None, emit=None, frames=None, store=None, journal=None,
|
|
51
|
+
notebook=None, spent_offset=0.0, phone_offset=0.0, clock=time.monotonic, awake=None):
|
|
52
|
+
self.run_id, self.request, self.plan, self.budget = run_id, request, dict(plan or {}), budget
|
|
53
|
+
self.models, self.interactive = models, bool(interactive)
|
|
54
|
+
self.focus = focus
|
|
55
|
+
self.preferences = list(preferences or ())
|
|
56
|
+
self.ledger, self.cap_usd = ledger, cap_usd
|
|
57
|
+
self.frames, self.store, self.journal = frames, store, journal
|
|
58
|
+
self.clock = clock
|
|
59
|
+
self.started = clock()
|
|
60
|
+
self.awake = awake
|
|
61
|
+
steps = [{**s, "state": s.get("state") or "next"} for s in (self.plan.get("steps") or ())]
|
|
62
|
+
self.notebook = notebook or Notebook(run_id, request, self.plan.get("kind", "feed"), focus, self.preferences,
|
|
63
|
+
steps)
|
|
64
|
+
if notebook is None and self.notebook.steps:
|
|
65
|
+
self.notebook.steps[0]["state"] = "now"
|
|
66
|
+
self.notebook.steps[0]["started_turn"] = 0
|
|
67
|
+
self.spent_offset, self.phone_offset = float(spent_offset or 0.0), float(phone_offset or 0.0)
|
|
68
|
+
self._sink = emit or (lambda event: None)
|
|
69
|
+
self._agent = None
|
|
70
|
+
self._first_action = None
|
|
71
|
+
self._released = None
|
|
72
|
+
self._waited = 0.0
|
|
73
|
+
self._last_progress = -PROGRESS_INTERVAL
|
|
74
|
+
self._last_tenth = 0
|
|
75
|
+
self._phase = "navigating"
|
|
76
|
+
self._pool = None
|
|
77
|
+
self._digester = None
|
|
78
|
+
self._step_notes = {}
|
|
79
|
+
self._covered_turns = 0
|
|
80
|
+
# Progress (P1-1): the work signature at the last progress, the screens seen since, and the streaks.
|
|
81
|
+
self._work = None
|
|
82
|
+
self._screens = set()
|
|
83
|
+
self._stall_turns = 0
|
|
84
|
+
self._refused_row = 0
|
|
85
|
+
self._dry_macros = []
|
|
86
|
+
self.stalled = None # why the run stopped making progress, once it has
|
|
87
|
+
self.turn = 0
|
|
88
|
+
self.bundle = None # the app on screen at the latest turn (an item without its own takes it)
|
|
89
|
+
self.apps_by_turn = {}
|
|
90
|
+
self.epochs = ctx_text.Epochs()
|
|
91
|
+
self.compactor = ctx_text.Compactor()
|
|
92
|
+
self.report = None
|
|
93
|
+
self.closed = False
|
|
94
|
+
self.lock = threading.RLock()
|
|
95
|
+
|
|
96
|
+
def attach(self, agent):
|
|
97
|
+
"""The frontier running this session: its emit (masked), its mask, its stop and steering."""
|
|
98
|
+
self._agent = agent
|
|
99
|
+
self.notebook.mask = self.mask
|
|
100
|
+
|
|
101
|
+
def emit(self, event):
|
|
102
|
+
"""An event through FrontierAgent.emit (masked, timed) once attached, else straight to the run's events."""
|
|
103
|
+
agent = self._agent
|
|
104
|
+
if agent is not None:
|
|
105
|
+
agent._emit(event)
|
|
106
|
+
else:
|
|
107
|
+
self._sink(event)
|
|
108
|
+
|
|
109
|
+
def mask(self, text):
|
|
110
|
+
agent = self._agent
|
|
111
|
+
return agent._mask(text) if agent is not None and isinstance(text, str) else text
|
|
112
|
+
|
|
113
|
+
def should_yield(self):
|
|
114
|
+
"""Why a macro should return now: "stopped" | "stop_after" | "paused" | "steer" | "budget" | None."""
|
|
115
|
+
agent = self._agent
|
|
116
|
+
if agent is not None:
|
|
117
|
+
try:
|
|
118
|
+
if agent._stopping():
|
|
119
|
+
return "stopped"
|
|
120
|
+
if agent._stop_after_asked():
|
|
121
|
+
return "stop_after"
|
|
122
|
+
if agent._pause_asked():
|
|
123
|
+
return "paused"
|
|
124
|
+
steering = getattr(agent, "steering", None)
|
|
125
|
+
if steering is not None and steering.pending():
|
|
126
|
+
return "steer"
|
|
127
|
+
except Exception: # noqa: BLE001 -- can't tell: return to the model
|
|
128
|
+
return "stopped"
|
|
129
|
+
if self.covered() is not None:
|
|
130
|
+
return "budget"
|
|
131
|
+
return None
|
|
132
|
+
|
|
133
|
+
def spent_usd(self):
|
|
134
|
+
"""The run's spend: every metered call (navigation, extraction, digest, synthesis, hedges), plus what a
|
|
135
|
+
picked-up run spent before."""
|
|
136
|
+
nano = getattr(self.ledger, "cost_nanodollars", 0) if self.ledger is not None else 0
|
|
137
|
+
return round(nano / 1e9 + self.spent_offset, 6)
|
|
138
|
+
|
|
139
|
+
def reserve_usd(self):
|
|
140
|
+
"""What the report will cost (costs.synth_estimate on the kept items so far, at least the budget's)."""
|
|
141
|
+
from .costs import synth_estimate
|
|
142
|
+
model = getattr(getattr(self.models, "synth", None), "model", None) or (self.plan.get("models") or {}).get(
|
|
143
|
+
"report") or "claude-sonnet-5-5"
|
|
144
|
+
kept = self.notebook.watched()
|
|
145
|
+
target = 0 if self.budget.strict else (self.budget.items or 0)
|
|
146
|
+
return synth_estimate(max(kept, target), model)
|
|
147
|
+
|
|
148
|
+
def phone_seconds(self):
|
|
149
|
+
"""Seconds the run has held the phone: from its first action to now (or the release), less the time it
|
|
150
|
+
waited for the person (approvals, pauses), plus a picked-up run's earlier time."""
|
|
151
|
+
if self._first_action is None:
|
|
152
|
+
return round(self.phone_offset, 2)
|
|
153
|
+
end = self._released if self._released is not None else self.clock()
|
|
154
|
+
return round(max(0.0, end - self._first_action - self._waited) + self.phone_offset, 2)
|
|
155
|
+
|
|
156
|
+
def mark_action(self):
|
|
157
|
+
if self._first_action is None:
|
|
158
|
+
self._first_action = self.clock()
|
|
159
|
+
self.notebook.phone["first_action_at"] = round(self._first_action - self.started, 2)
|
|
160
|
+
|
|
161
|
+
def add_wait(self, seconds):
|
|
162
|
+
if seconds and seconds > 0 and self._first_action is not None:
|
|
163
|
+
self._waited += float(seconds)
|
|
164
|
+
self.notebook.phone["waited_s"] = round(self._waited, 2)
|
|
165
|
+
|
|
166
|
+
def budget_left(self):
|
|
167
|
+
kept = self.notebook.watched()
|
|
168
|
+
items = None if self.budget.items is None or self.budget.strict else max(0, self.budget.items - kept)
|
|
169
|
+
usd = (self.cap_usd - self.spent_usd() - self.reserve_usd()) if self.cap_usd is not None else float("inf")
|
|
170
|
+
return {"items": items,
|
|
171
|
+
"phone_seconds": round(max(0.0, self.budget.phone_seconds_cap - self.phone_seconds()), 2),
|
|
172
|
+
"usd": round(usd, 4) if usd != float("inf") else None}
|
|
173
|
+
|
|
174
|
+
def covered(self):
|
|
175
|
+
"""Why the budget is used up ("items", "phone_time", "spend"), or None."""
|
|
176
|
+
kept = self.notebook.watched()
|
|
177
|
+
if self.budget.items and not self.budget.strict and kept >= self.budget.items:
|
|
178
|
+
return "items"
|
|
179
|
+
if self.phone_seconds() >= self.budget.phone_seconds_cap:
|
|
180
|
+
return "phone_time"
|
|
181
|
+
if self.cap_usd is not None and self.spent_usd() + self.reserve_usd() >= self.cap_usd:
|
|
182
|
+
return "spend"
|
|
183
|
+
return None
|
|
184
|
+
|
|
185
|
+
def advice(self):
|
|
186
|
+
covered = self.covered()
|
|
187
|
+
if covered == "items":
|
|
188
|
+
return "The items are covered: wrap up now with DONE."
|
|
189
|
+
if covered == "phone_time":
|
|
190
|
+
return "The phone time is used up: wrap up now with DONE."
|
|
191
|
+
if covered == "spend":
|
|
192
|
+
return "The spend cap is reached: wrap up now with DONE."
|
|
193
|
+
if self.cap_usd is not None and self.spent_usd() + self.reserve_usd() >= WRAP_UP_SHARE * self.cap_usd:
|
|
194
|
+
return "Spend is nearly at the cap: wrap up."
|
|
195
|
+
if self.budget.items and not self.budget.strict:
|
|
196
|
+
return "Wrap up when the items are covered."
|
|
197
|
+
return "Keep going until the time is used, then wrap up."
|
|
198
|
+
|
|
199
|
+
def final_reason(self):
|
|
200
|
+
"""The frontier's reason to end the loop now without another model call, or None."""
|
|
201
|
+
covered = self.covered()
|
|
202
|
+
if covered is None:
|
|
203
|
+
self._covered_turns = 0
|
|
204
|
+
return self.stall_reason()
|
|
205
|
+
if covered in ("phone_time", "spend"):
|
|
206
|
+
return covered
|
|
207
|
+
self._covered_turns += 1
|
|
208
|
+
return covered if self._covered_turns > COVERED_GRACE_TURNS else None
|
|
209
|
+
|
|
210
|
+
def _work_signature(self):
|
|
211
|
+
book = self.notebook
|
|
212
|
+
with book.lock:
|
|
213
|
+
return (tuple(sorted((book.counters or {}).items())), len(book.facts),
|
|
214
|
+
tuple((step.get("id"), step.get("state")) for step in book.steps))
|
|
215
|
+
|
|
216
|
+
def _progress(self, screen):
|
|
217
|
+
"""One turn's progress: a new item, note or step state resets the count, and so does a screen not seen since
|
|
218
|
+
the last of those (navigating); the same screens again with nothing new count toward STALL_TURNS."""
|
|
219
|
+
work = self._work_signature()
|
|
220
|
+
if work != self._work:
|
|
221
|
+
self._work, self._screens, self._stall_turns = work, {screen}, 0
|
|
222
|
+
elif screen not in self._screens:
|
|
223
|
+
self._screens.add(screen)
|
|
224
|
+
self._stall_turns = 0
|
|
225
|
+
else:
|
|
226
|
+
self._stall_turns += 1
|
|
227
|
+
|
|
228
|
+
def on_refused(self):
|
|
229
|
+
"""An action was refused (a loop, a repeat, a gate): REFUSED_IN_A_ROW of them in a row end the run."""
|
|
230
|
+
self._refused_row += 1
|
|
231
|
+
|
|
232
|
+
def on_acted(self, noop=False):
|
|
233
|
+
"""An action was taken. A LAUNCH_APP of the app already in front is a no-op: it keeps the refusal streak."""
|
|
234
|
+
if not noop:
|
|
235
|
+
self._refused_row = 0
|
|
236
|
+
|
|
237
|
+
def on_macro(self, op, reason, saved):
|
|
238
|
+
"""A macro (WATCH_FEED, READ_EACH) returned: ``saved`` items, stopped for ``reason``."""
|
|
239
|
+
if saved or reason not in DRY_REASONS:
|
|
240
|
+
self._dry_macros = []
|
|
241
|
+
if saved:
|
|
242
|
+
self._refused_row = 0
|
|
243
|
+
return
|
|
244
|
+
self._dry_macros.append((op, reason))
|
|
245
|
+
|
|
246
|
+
def stall_reason(self):
|
|
247
|
+
""""stuck" (and ``stalled`` says why) once the run has stopped making progress; else None."""
|
|
248
|
+
if self.stalled is None:
|
|
249
|
+
if self._stall_turns >= STALL_TURNS:
|
|
250
|
+
self.stalled = f"nothing new in {self._stall_turns} turns"
|
|
251
|
+
elif self._refused_row >= REFUSED_IN_A_ROW:
|
|
252
|
+
self.stalled = f"{self._refused_row} actions in a row were refused"
|
|
253
|
+
elif len(self._dry_macros) >= DRY_MACROS:
|
|
254
|
+
op, reason = self._dry_macros[-1]
|
|
255
|
+
self.stalled = (f"{op} found nothing new {len(self._dry_macros)} times in a row "
|
|
256
|
+
f"({'the list ended' if reason == 'end' else reason.replace('_', ' ')})")
|
|
257
|
+
if self.stalled is None:
|
|
258
|
+
return None
|
|
259
|
+
return "list_end" if self._list_done() else "stuck"
|
|
260
|
+
|
|
261
|
+
def _list_done(self):
|
|
262
|
+
"""The macro said the list ended (every row read) and something was saved: the run is complete, not stuck."""
|
|
263
|
+
return bool(self._dry_macros) and all(reason == "end" for _, reason in self._dry_macros[-DRY_MACROS:]) \
|
|
264
|
+
and bool(self.notebook.counters.get("kept"))
|
|
265
|
+
|
|
266
|
+
def _frame_id(self, frame):
|
|
267
|
+
if not frame or self.frames is None:
|
|
268
|
+
return None
|
|
269
|
+
try:
|
|
270
|
+
return self.frames.put(self.run_id, bytes(frame))
|
|
271
|
+
except Exception: # noqa: BLE001
|
|
272
|
+
return None
|
|
273
|
+
|
|
274
|
+
def current_step_id(self):
|
|
275
|
+
step = self.notebook.current_step()
|
|
276
|
+
return step["id"] if step else None
|
|
277
|
+
|
|
278
|
+
def add_item(self, item, *, frame=None):
|
|
279
|
+
"""Add a captured item (dedupe and counters in the notebook); emits longform_item. Returns the stored Item."""
|
|
280
|
+
if item.step is None:
|
|
281
|
+
item.step = self.current_step_id()
|
|
282
|
+
if not item.app:
|
|
283
|
+
item.app = self.bundle
|
|
284
|
+
if not item.at:
|
|
285
|
+
item.at = round(self.clock() - self.started, 2)
|
|
286
|
+
item.frame_id = self._frame_id(frame) or item.frame_id
|
|
287
|
+
self.mark_action()
|
|
288
|
+
stored = self.notebook.add_item(item)
|
|
289
|
+
self._item_event(stored, "skipped" if stored.skip else "captured")
|
|
290
|
+
self.progress(self._phase if self._phase in ("watching", "reading") else "watching")
|
|
291
|
+
if stored.n % STORE_EVERY_ITEMS == 0:
|
|
292
|
+
self.save()
|
|
293
|
+
self._poke_digest()
|
|
294
|
+
return stored
|
|
295
|
+
|
|
296
|
+
def update_item(self, item_id, *, extract=None, skip=None, frame=None):
|
|
297
|
+
if extract is not None:
|
|
298
|
+
extract = dict(extract)
|
|
299
|
+
if extract.get("ad") is True and skip is None:
|
|
300
|
+
skip = "ad"
|
|
301
|
+
elif extract.get("relevant") is False and self.focus and skip is None:
|
|
302
|
+
skip = "off_topic"
|
|
303
|
+
frame_id = self._frame_id(frame)
|
|
304
|
+
stored = self.notebook.update_item(item_id, extract=extract, skip=skip, frame_id=frame_id)
|
|
305
|
+
if stored is None:
|
|
306
|
+
return None
|
|
307
|
+
self._item_event(stored, "skipped" if stored.skip else ("extracted" if stored.extract else "captured"))
|
|
308
|
+
self._poke_digest()
|
|
309
|
+
return stored
|
|
310
|
+
|
|
311
|
+
def extract_failed(self, item_id):
|
|
312
|
+
self.notebook.extract_failed(item_id)
|
|
313
|
+
|
|
314
|
+
def _item_event(self, item, status):
|
|
315
|
+
extract = item.extract or {}
|
|
316
|
+
self.emit({"event": "longform_item", "itemId": item.id, "n": item.n, "kind": item.kind, "status": status,
|
|
317
|
+
"skip": item.skip, "source": dict(item.source), "summary": item.summary() or None,
|
|
318
|
+
"tags": list(extract.get("tags") or [])[:8], "relevant": extract.get("relevant"),
|
|
319
|
+
"frameId": item.frame_id, "step": item.step})
|
|
320
|
+
|
|
321
|
+
def add_fact(self, text, item_ids=(), kind="fact", source="model"):
|
|
322
|
+
fact = self.notebook.add_fact(text, item_ids, kind=kind, source=source, turn=self.turn)
|
|
323
|
+
if fact is None:
|
|
324
|
+
return None
|
|
325
|
+
if kind != "step_result":
|
|
326
|
+
self.emit({"event": "longform_finding", "findingId": fact.id, "text": fact.text,
|
|
327
|
+
"itemIds": list(fact.item_ids), "kind": "insight" if kind == "insight" else
|
|
328
|
+
"theme" if kind == "theme" else "fact"})
|
|
329
|
+
step = self.current_step_id()
|
|
330
|
+
if step is not None and source == "model":
|
|
331
|
+
self._step_notes.setdefault(step, []).append(fact.text)
|
|
332
|
+
self._poke_digest()
|
|
333
|
+
return fact
|
|
334
|
+
|
|
335
|
+
def progress(self, phase, *, label=None, force=False):
|
|
336
|
+
"""longform_progress, at most 2 a second (a change of phase always goes out)."""
|
|
337
|
+
now = self.clock()
|
|
338
|
+
c = self.notebook.counters
|
|
339
|
+
tenth = c["kept"] + c["off_topic"]
|
|
340
|
+
changed = phase != self._phase or (tenth // 10 != self._last_tenth // 10)
|
|
341
|
+
self._phase = phase
|
|
342
|
+
if not force and not changed and now - self._last_progress < PROGRESS_INTERVAL:
|
|
343
|
+
return
|
|
344
|
+
self._last_progress = now
|
|
345
|
+
self._last_tenth = tenth
|
|
346
|
+
target = None if self.budget.strict else self.budget.items
|
|
347
|
+
left = max(0.0, self.budget.phone_seconds_cap - self.phone_seconds())
|
|
348
|
+
self.emit({"event": "longform_progress", "phase": phase, "stepId": self.current_step_id(),
|
|
349
|
+
"items": {"seen": c["seen"], "kept": c["kept"], "ads": c["ads"], "repeats": c["repeats"],
|
|
350
|
+
"offTopic": c["off_topic"], "watched": c["kept"] + c["off_topic"], "target": target},
|
|
351
|
+
"phoneSeconds": round(self.phone_seconds(), 1), "phoneSecondsLeft": round(left, 1),
|
|
352
|
+
"costUsd": round(self.spent_usd(), 4), "label": label})
|
|
353
|
+
|
|
354
|
+
def extract_pool(self):
|
|
355
|
+
"""The run's extraction pool (feed's extract.ExtractPool), made on first use."""
|
|
356
|
+
with self.lock:
|
|
357
|
+
if self._pool is None:
|
|
358
|
+
from .extract import ExtractPool
|
|
359
|
+
self._pool = ExtractPool(self, self.models.extract if self.models is not None else None)
|
|
360
|
+
return self._pool
|
|
361
|
+
|
|
362
|
+
def drain(self, timeout=20.0):
|
|
363
|
+
"""Wait for the extraction pool (at most ``timeout`` s); returns the items still pending."""
|
|
364
|
+
pool = self._pool
|
|
365
|
+
if pool is None:
|
|
366
|
+
return 0
|
|
367
|
+
try:
|
|
368
|
+
return int(pool.drain(timeout=timeout) or 0)
|
|
369
|
+
except Exception: # noqa: BLE001
|
|
370
|
+
return 0
|
|
371
|
+
|
|
372
|
+
def _poke_digest(self):
|
|
373
|
+
if self.models is None or getattr(self.models, "digest", None) is None or self.closed:
|
|
374
|
+
return
|
|
375
|
+
with self.lock:
|
|
376
|
+
if self._digester is None:
|
|
377
|
+
from .digest import Digester
|
|
378
|
+
self._digester = Digester(self)
|
|
379
|
+
self._digester.poke()
|
|
380
|
+
|
|
381
|
+
def on_screen(self, bundle):
|
|
382
|
+
"""The screen is in ``bundle`` (an item read without its own app takes it)."""
|
|
383
|
+
if bundle:
|
|
384
|
+
if bundle != getattr(self, "bundle", None):
|
|
385
|
+
self._subrun_for(bundle)
|
|
386
|
+
self.bundle = bundle
|
|
387
|
+
|
|
388
|
+
def _subrun_for(self, bundle):
|
|
389
|
+
run = getattr(self, "run", None)
|
|
390
|
+
apps = {a.get("bundleId"): a.get("name") for a in (self.plan or {}).get("apps") or () if isinstance(a, dict)}
|
|
391
|
+
start = getattr(run, "subrun_start", None)
|
|
392
|
+
if not callable(start) or len(apps) < 2 or bundle not in apps:
|
|
393
|
+
return
|
|
394
|
+
self._finish_subrun()
|
|
395
|
+
try:
|
|
396
|
+
self._subrun = start(app=apps[bundle] or bundle, bundle_id=bundle, title=f"{apps[bundle] or bundle}")
|
|
397
|
+
except Exception: # noqa: BLE001 -- rows are a view; the run goes on without them
|
|
398
|
+
self._subrun = None
|
|
399
|
+
|
|
400
|
+
def _finish_subrun(self, outcome="done"):
|
|
401
|
+
run, open_ = getattr(self, "run", None), getattr(self, "_subrun", None)
|
|
402
|
+
if open_ is None or not callable(getattr(run, "subrun_finish", None)):
|
|
403
|
+
return
|
|
404
|
+
counts = {"read": self.notebook.counters.get("kept", 0)}
|
|
405
|
+
try:
|
|
406
|
+
run.subrun_finish(open_, outcome=outcome, counts=counts)
|
|
407
|
+
except Exception: # noqa: BLE001
|
|
408
|
+
pass
|
|
409
|
+
self._subrun = None
|
|
410
|
+
|
|
411
|
+
def on_turn(self, turn, app_name, history=None, bundle=None, screen=None):
|
|
412
|
+
"""The frontier starts turn ``turn`` in ``app_name``: compaction of history that left the window, and the
|
|
413
|
+
turn's progress (``screen``: the screen's content fingerprint)."""
|
|
414
|
+
self.on_screen(bundle)
|
|
415
|
+
self._progress((bundle, screen))
|
|
416
|
+
self.turn = turn
|
|
417
|
+
if app_name:
|
|
418
|
+
self.apps_by_turn[turn] = app_name
|
|
419
|
+
if history is not None:
|
|
420
|
+
self.compactor.fold(history, self.notebook, self.apps_by_turn)
|
|
421
|
+
self.progress(self._phase if self._phase != "done" else "navigating")
|
|
422
|
+
|
|
423
|
+
def on_reply(self, step_id, turn):
|
|
424
|
+
"""The reply's ``step``: moving past a step marks it done (steps jumped over: skipped) and the new one now."""
|
|
425
|
+
if not isinstance(step_id, int) or isinstance(step_id, bool):
|
|
426
|
+
return []
|
|
427
|
+
target = self.notebook.step(step_id)
|
|
428
|
+
current = self.notebook.current_step()
|
|
429
|
+
if target is None or (current is not None and current["id"] == step_id):
|
|
430
|
+
return []
|
|
431
|
+
changes = []
|
|
432
|
+
with self.notebook.lock:
|
|
433
|
+
if current is not None:
|
|
434
|
+
if step_id > current["id"]:
|
|
435
|
+
changes.append(self._set_step(current, "done", turn))
|
|
436
|
+
for step in self.notebook.steps:
|
|
437
|
+
if current["id"] < step["id"] < step_id and step["state"] == "next":
|
|
438
|
+
changes.append(self._set_step(step, "skipped", turn))
|
|
439
|
+
else:
|
|
440
|
+
changes.append(self._set_step(current, "next", turn))
|
|
441
|
+
changes.append(self._set_step(target, "now", turn))
|
|
442
|
+
self.save()
|
|
443
|
+
return changes
|
|
444
|
+
|
|
445
|
+
def _set_step(self, step, state, turn):
|
|
446
|
+
step["state"] = state
|
|
447
|
+
if state == "now":
|
|
448
|
+
step["started_turn"] = turn
|
|
449
|
+
elif state in ("done", "skipped", "failed"):
|
|
450
|
+
step["ended_turn"] = turn
|
|
451
|
+
notes = self._step_notes.get(step["id"]) or []
|
|
452
|
+
if notes and state == "done":
|
|
453
|
+
result = cut("; ".join(notes), 300)
|
|
454
|
+
step["result"] = result
|
|
455
|
+
self.notebook.add_fact(result, (), kind="step_result", source="step", turn=turn)
|
|
456
|
+
self.notebook.revision += 1
|
|
457
|
+
self.emit({"event": "longform_step", "stepId": step["id"], "state": state, "result": step.get("result")})
|
|
458
|
+
return step["id"], state
|
|
459
|
+
|
|
460
|
+
def finish_steps(self, status):
|
|
461
|
+
"""The loop ended with ``status``: the step in progress and the ones left are settled."""
|
|
462
|
+
steps = self.notebook.steps
|
|
463
|
+
writeup = steps[-1] if steps and steps[-1].get("app") is None and len(steps) > 1 else None
|
|
464
|
+
for step in steps:
|
|
465
|
+
if step is writeup or step["state"] in ("done", "skipped", "failed"):
|
|
466
|
+
continue
|
|
467
|
+
if status == "completed":
|
|
468
|
+
self._set_step(step, "done", self.turn)
|
|
469
|
+
elif step["state"] == "now":
|
|
470
|
+
self._set_step(step, "failed" if status in ("error", "blocked") else "skipped", self.turn)
|
|
471
|
+
else:
|
|
472
|
+
self._set_step(step, "skipped", self.turn)
|
|
473
|
+
if writeup is not None and writeup["state"] == "next":
|
|
474
|
+
self._set_step(writeup, "now", self.turn)
|
|
475
|
+
|
|
476
|
+
def finish_writeup(self, ok=True):
|
|
477
|
+
steps = self.notebook.steps
|
|
478
|
+
if steps and steps[-1].get("app") is None and steps[-1]["state"] == "now":
|
|
479
|
+
self._set_step(steps[-1], "done" if ok else "failed", self.turn)
|
|
480
|
+
|
|
481
|
+
def checkpoint(self):
|
|
482
|
+
"""The checkpoint's ``longform`` key (DESIGN §4.3)."""
|
|
483
|
+
seconds = self.phone_seconds()
|
|
484
|
+
self.notebook.phone["cumulative_seconds"] = seconds
|
|
485
|
+
return {"planId": self.plan.get("id"), "step": self.current_step_id(),
|
|
486
|
+
"counters": dict(self.notebook.counters), "phoneSeconds": seconds}
|
|
487
|
+
|
|
488
|
+
def capture_contract(self, contract, *, final=False):
|
|
489
|
+
"""Persist what the task contract can show, separately from plan-step progress."""
|
|
490
|
+
from . import receipts
|
|
491
|
+
entry = receipts.snapshot(self.notebook, contract, final=final)
|
|
492
|
+
if entry is not None:
|
|
493
|
+
receipts.put(self.notebook, entry)
|
|
494
|
+
self.save()
|
|
495
|
+
return entry
|
|
496
|
+
|
|
497
|
+
def release_phone(self):
|
|
498
|
+
"""The loop is done with the phone: phone_released, the awake lease let go, the notebook saved."""
|
|
499
|
+
with self.lock:
|
|
500
|
+
if self._released is not None:
|
|
501
|
+
return False
|
|
502
|
+
self._released = self.clock()
|
|
503
|
+
self._finish_subrun()
|
|
504
|
+
self.notebook.phone["released_at"] = round(self._released - self.started, 2)
|
|
505
|
+
self.notebook.phone["cumulative_seconds"] = self.phone_seconds()
|
|
506
|
+
if self.awake is not None:
|
|
507
|
+
try:
|
|
508
|
+
self.awake.release(self.run_id)
|
|
509
|
+
except Exception: # noqa: BLE001
|
|
510
|
+
pass
|
|
511
|
+
self.emit({"event": "phone_released", "phoneSeconds": round(self.phone_seconds(), 1)})
|
|
512
|
+
self.progress("writing", force=True)
|
|
513
|
+
self.save()
|
|
514
|
+
return True
|
|
515
|
+
|
|
516
|
+
@property
|
|
517
|
+
def released(self):
|
|
518
|
+
return self._released is not None
|
|
519
|
+
|
|
520
|
+
def save(self, report=None, markdown=None):
|
|
521
|
+
"""The notebook (and the report) to the journal, on the store's writer thread."""
|
|
522
|
+
self.notebook.phone["cumulative_seconds"] = self.phone_seconds()
|
|
523
|
+
if self.store is None or self.journal is None:
|
|
524
|
+
return
|
|
525
|
+
try:
|
|
526
|
+
self.store.submit(self.journal, self.run_id, self.notebook, report=report, markdown=markdown)
|
|
527
|
+
except Exception: # noqa: BLE001 -- a journal that can't take it never breaks the run
|
|
528
|
+
pass
|
|
529
|
+
|
|
530
|
+
def close(self):
|
|
531
|
+
"""Stop the digest and the pool, write the notebook, and leave the registry."""
|
|
532
|
+
self.closed = True
|
|
533
|
+
digester, self._digester = self._digester, None
|
|
534
|
+
if digester is not None:
|
|
535
|
+
digester.stop()
|
|
536
|
+
pool = self._pool
|
|
537
|
+
if pool is not None:
|
|
538
|
+
try:
|
|
539
|
+
pool.close()
|
|
540
|
+
except Exception: # noqa: BLE001
|
|
541
|
+
pass
|
|
542
|
+
for client in (getattr(self.models, "digest", None), getattr(self.models, "synth", None),
|
|
543
|
+
getattr(self.models, "synth_fallback", None)):
|
|
544
|
+
close = getattr(client, "close", None)
|
|
545
|
+
if callable(close):
|
|
546
|
+
close()
|
|
547
|
+
self.save()
|
|
548
|
+
unbind(self.run_id)
|
|
549
|
+
|
|
550
|
+
|
|
551
|
+
_pickups = {} # run id -> what the long-run pick-up hook loaded for it (register.pickup)
|
|
552
|
+
|
|
553
|
+
|
|
554
|
+
def _elapsed(value):
|
|
555
|
+
if type(value) not in (int, float) or value < 0:
|
|
556
|
+
return None
|
|
557
|
+
try:
|
|
558
|
+
seconds = float(value)
|
|
559
|
+
except OverflowError:
|
|
560
|
+
return None
|
|
561
|
+
return seconds if math.isfinite(seconds) else None
|
|
562
|
+
|
|
563
|
+
|
|
564
|
+
def _pickup_phone_seconds(phone, picked, cap):
|
|
565
|
+
"""Restore trusted elapsed time; a damaged source consumes the remaining phone budget."""
|
|
566
|
+
checkpoint = _elapsed(picked["phone_seconds"]) if "phone_seconds" in picked else None
|
|
567
|
+
cumulative = _elapsed(phone["cumulative_seconds"]) if "cumulative_seconds" in phone else None
|
|
568
|
+
invalid = ("phone_seconds" in picked and checkpoint is None) or (
|
|
569
|
+
"cumulative_seconds" in phone and cumulative is None)
|
|
570
|
+
legacy = 0.0
|
|
571
|
+
if "cumulative_seconds" not in phone:
|
|
572
|
+
first = phone.get("first_action_at")
|
|
573
|
+
if first is not None:
|
|
574
|
+
end = phone.get("released_at")
|
|
575
|
+
if end is None:
|
|
576
|
+
end = phone.get("last_at")
|
|
577
|
+
if end is None:
|
|
578
|
+
# A crash has no segment end; its checkpoint already excludes approval waits.
|
|
579
|
+
invalid |= checkpoint is None or _elapsed(first) is None or _elapsed(phone.get("waited_s", 0)) is None
|
|
580
|
+
else:
|
|
581
|
+
first, end, waited = _elapsed(first), _elapsed(end), _elapsed(phone.get("waited_s", 0))
|
|
582
|
+
invalid |= first is None or end is None or waited is None or (
|
|
583
|
+
first is not None and end is not None and waited is not None and end - first < waited)
|
|
584
|
+
if not invalid:
|
|
585
|
+
legacy = end - first - waited
|
|
586
|
+
elif (phone.get("released_at") is not None or phone.get("last_at") is not None
|
|
587
|
+
or _elapsed(phone.get("waited_s", 0)) is None or phone.get("waited_s", 0) > 0):
|
|
588
|
+
invalid = True
|
|
589
|
+
return cap if invalid else max(legacy, cumulative or 0.0, checkpoint or 0.0)
|
|
590
|
+
|
|
591
|
+
|
|
592
|
+
def start(*, run_id, goal, plan, budget, origin, nav_client, key, inference, ledger, cap_usd, emit, frames=None,
|
|
593
|
+
journal=None, awake=None, model_factory=None, spent_offset=0.0, rules=(), earlier=(), run=None):
|
|
594
|
+
"""A bound LongSession for one run: the models routed (models.model_set), the conversation's standing rules
|
|
595
|
+
(``rules``, "only people in Brooklyn") as preferences next to the request, a picked-up run's notebook and budget,
|
|
596
|
+
the awake lease taken, and the ``longform_plan`` event."""
|
|
597
|
+
from ..harness_api import INTERACTIVE
|
|
598
|
+
from .budget import Budget
|
|
599
|
+
from .models import model_set
|
|
600
|
+
from .notebook import Notebook
|
|
601
|
+
from .store import writer
|
|
602
|
+
budget = budget if hasattr(budget, "phone_seconds_cap") else Budget.from_dict(budget)
|
|
603
|
+
kept = list(dict.fromkeys(" ".join(str(rule).split())[:MAX_RULE_CHARS] for rule in rules or ()
|
|
604
|
+
if str(rule).strip()))[:MAX_RULES]
|
|
605
|
+
models = model_set(nav_client, key, emit=inference, items_hint=budget.items or 60, kind=plan.get("kind", "feed"),
|
|
606
|
+
factory=model_factory)
|
|
607
|
+
picked = _pickups.pop(run_id, None)
|
|
608
|
+
notebook, phone_offset = None, 0.0
|
|
609
|
+
if picked is not None and picked.get("notebook"):
|
|
610
|
+
notebook = Notebook.from_dict(picked["notebook"])
|
|
611
|
+
notebook.run_id = run_id
|
|
612
|
+
phone = picked["notebook"].get("phone") or {}
|
|
613
|
+
phone_offset = _pickup_phone_seconds(phone, picked, budget.phone_seconds_cap)
|
|
614
|
+
# Segment timestamps belong to the source run. Keep only its cumulative elapsed budget in this run.
|
|
615
|
+
notebook.phone = {"first_action_at": None, "released_at": None, "waited_s": 0.0,
|
|
616
|
+
"cumulative_seconds": round(phone_offset, 2)}
|
|
617
|
+
spent_offset = max(float(spent_offset or 0.0), float(picked.get("cost_usd") or 0.0))
|
|
618
|
+
session = LongSession(run_id, goal, plan, budget,
|
|
619
|
+
models=models, interactive=origin in INTERACTIVE,
|
|
620
|
+
focus=plan.get("focus"), preferences=kept, ledger=ledger, cap_usd=cap_usd, emit=emit,
|
|
621
|
+
frames=frames, store=writer, journal=journal, notebook=notebook,
|
|
622
|
+
spent_offset=spent_offset, phone_offset=phone_offset, awake=awake)
|
|
623
|
+
session.notebook.phone["cumulative_seconds"] = session.phone_seconds()
|
|
624
|
+
session.notebook.plan = {k: v for k, v in plan.items() if not str(k).startswith("_")}
|
|
625
|
+
session.run = run # the Runtime's Run: sub-run rows per app, and the inbox's proposals (orchestrate)
|
|
626
|
+
if earlier:
|
|
627
|
+
# What an earlier long task in this conversation already showed is skipped ("12 new since last time").
|
|
628
|
+
session.notebook.remember_earlier(earlier)
|
|
629
|
+
if picked is not None:
|
|
630
|
+
session.picked_up_from = picked.get("source")
|
|
631
|
+
session.turns_used = int(picked.get("turns") or 0)
|
|
632
|
+
bind(session)
|
|
633
|
+
if awake is not None:
|
|
634
|
+
try:
|
|
635
|
+
awake.acquire(run_id)
|
|
636
|
+
except Exception: # noqa: BLE001
|
|
637
|
+
pass
|
|
638
|
+
session.emit({"event": "longform_plan", "plan": session.notebook.plan, "revision": 0})
|
|
639
|
+
current = session.notebook.current_step()
|
|
640
|
+
if current is not None:
|
|
641
|
+
session.emit({"event": "longform_step", "stepId": current["id"], "state": "now", "result": None})
|
|
642
|
+
session.progress("navigating", force=True)
|
|
643
|
+
session.save()
|
|
644
|
+
return session
|