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.
Files changed (1009) hide show
  1. mobile_agent/__init__.py +4 -0
  2. mobile_agent/__main__.py +598 -0
  3. mobile_agent/accounts/__init__.py +1 -0
  4. mobile_agent/accounts/context.py +80 -0
  5. mobile_agent/accounts/mcp.py +55 -0
  6. mobile_agent/accounts/pacing.py +318 -0
  7. mobile_agent/accounts/register.py +78 -0
  8. mobile_agent/accounts/routes.py +212 -0
  9. mobile_agent/accounts/routing.py +69 -0
  10. mobile_agent/accounts/screen.py +273 -0
  11. mobile_agent/accounts/stats.py +54 -0
  12. mobile_agent/accounts/store.py +394 -0
  13. mobile_agent/agent_hooks.py +123 -0
  14. mobile_agent/ai_plans/__init__.py +7 -0
  15. mobile_agent/ai_plans/agents.py +96 -0
  16. mobile_agent/ai_plans/claude_code.py +208 -0
  17. mobile_agent/ai_plans/cli.py +400 -0
  18. mobile_agent/ai_plans/codex.py +258 -0
  19. mobile_agent/ai_plans/compat_chat.py +307 -0
  20. mobile_agent/ai_plans/device_login.py +164 -0
  21. mobile_agent/ai_plans/draft_client.py +68 -0
  22. mobile_agent/ai_plans/gemini.py +137 -0
  23. mobile_agent/ai_plans/instructions.py +65 -0
  24. mobile_agent/ai_plans/mcp_http.py +160 -0
  25. mobile_agent/ai_plans/phone_tools.py +892 -0
  26. mobile_agent/ai_plans/providers.py +235 -0
  27. mobile_agent/ai_plans/routes.py +160 -0
  28. mobile_agent/ai_plans/runner.py +400 -0
  29. mobile_agent/ai_plans/vault.py +114 -0
  30. mobile_agent/alerts.py +337 -0
  31. mobile_agent/android/__init__.py +18 -0
  32. mobile_agent/android/adb.py +268 -0
  33. mobile_agent/android/conditions.py +128 -0
  34. mobile_agent/android/doctor.py +54 -0
  35. mobile_agent/android/driver.py +137 -0
  36. mobile_agent/android/emulator.py +216 -0
  37. mobile_agent/android/logs.py +85 -0
  38. mobile_agent/android/manager.py +225 -0
  39. mobile_agent/android/records.py +31 -0
  40. mobile_agent/android/tree.py +96 -0
  41. mobile_agent/answer_shapes.py +230 -0
  42. mobile_agent/api_devices.py +333 -0
  43. mobile_agent/api_errors.py +17 -0
  44. mobile_agent/api_get.py +249 -0
  45. mobile_agent/api_http.py +467 -0
  46. mobile_agent/api_post.py +453 -0
  47. mobile_agent/api_routes.py +173 -0
  48. mobile_agent/app_api/__init__.py +4 -0
  49. mobile_agent/app_api/cli.py +151 -0
  50. mobile_agent/app_api/endpoints.py +193 -0
  51. mobile_agent/app_api/mcp.py +54 -0
  52. mobile_agent/app_api/openapi.py +146 -0
  53. mobile_agent/app_api/reading.py +172 -0
  54. mobile_agent/app_api/register.py +10 -0
  55. mobile_agent/app_api/screen.py +216 -0
  56. mobile_agent/app_api/service.py +382 -0
  57. mobile_agent/app_api/sessions.py +404 -0
  58. mobile_agent/app_api/tokens.py +167 -0
  59. mobile_agent/app_choice.py +131 -0
  60. mobile_agent/app_icons.py +183 -0
  61. mobile_agent/app_killer.py +69 -0
  62. mobile_agent/artifacts.py +606 -0
  63. mobile_agent/attach/__init__.py +13 -0
  64. mobile_agent/attach/cli.py +46 -0
  65. mobile_agent/attach/flutter.py +28 -0
  66. mobile_agent/attach/score.py +119 -0
  67. mobile_agent/attach/web.py +13 -0
  68. mobile_agent/attachments/__init__.py +1 -0
  69. mobile_agent/attachments/agent.py +418 -0
  70. mobile_agent/attachments/extract.py +217 -0
  71. mobile_agent/attachments/mcp.py +104 -0
  72. mobile_agent/attachments/register.py +73 -0
  73. mobile_agent/attachments/routes.py +326 -0
  74. mobile_agent/attachments/service.py +170 -0
  75. mobile_agent/attachments/sniff.py +152 -0
  76. mobile_agent/attachments/store.py +441 -0
  77. mobile_agent/audit_verify.py +436 -0
  78. mobile_agent/bench/README.md +410 -0
  79. mobile_agent/bench/__init__.py +0 -0
  80. mobile_agent/bench/__main__.py +394 -0
  81. mobile_agent/bench/ab.py +168 -0
  82. mobile_agent/bench/actuation.py +368 -0
  83. mobile_agent/bench/agents/__init__.py +9 -0
  84. mobile_agent/bench/agents/base.py +105 -0
  85. mobile_agent/bench/agents/plan_clients.py +245 -0
  86. mobile_agent/bench/agents/smart.py +533 -0
  87. mobile_agent/bench/checks.py +358 -0
  88. mobile_agent/bench/decide_gate.py +214 -0
  89. mobile_agent/bench/diagnose.py +181 -0
  90. mobile_agent/bench/evals/__init__.py +14 -0
  91. mobile_agent/bench/evals/build.py +565 -0
  92. mobile_agent/bench/evals/checkpoint.py +214 -0
  93. mobile_agent/bench/evals/irt.py +219 -0
  94. mobile_agent/bench/evals/l0.py +647 -0
  95. mobile_agent/bench/evals/l1.py +866 -0
  96. mobile_agent/bench/evals/models.py +124 -0
  97. mobile_agent/bench/evals/pairwise.py +189 -0
  98. mobile_agent/bench/evals/proxy.py +90 -0
  99. mobile_agent/bench/evals/sets.py +149 -0
  100. mobile_agent/bench/evals/traces.py +446 -0
  101. mobile_agent/bench/fixtures.py +176 -0
  102. mobile_agent/bench/guard_replay.py +129 -0
  103. mobile_agent/bench/h2-manifest.json +20 -0
  104. mobile_agent/bench/h3-manifest.json +20 -0
  105. mobile_agent/bench/heldout-manifest.json +32 -0
  106. mobile_agent/bench/heldout.py +375 -0
  107. mobile_agent/bench/iosworld.py +901 -0
  108. mobile_agent/bench/iosworld_stats.py +510 -0
  109. mobile_agent/bench/llm_units.py +116 -0
  110. mobile_agent/bench/manifest.json +93 -0
  111. mobile_agent/bench/metrics.py +66 -0
  112. mobile_agent/bench/phone.py +44 -0
  113. mobile_agent/bench/replay.py +172 -0
  114. mobile_agent/bench/report.py +207 -0
  115. mobile_agent/bench/runner.py +456 -0
  116. mobile_agent/bench/safety.py +310 -0
  117. mobile_agent/bench/stream_timing.py +213 -0
  118. mobile_agent/bench/suite.py +444 -0
  119. mobile_agent/bench/sweep.py +176 -0
  120. mobile_agent/bench/tape.py +798 -0
  121. mobile_agent/bench/tape_replay.py +88 -0
  122. mobile_agent/bench/trace_evals.py +128 -0
  123. mobile_agent/bench/trace_view.py +81 -0
  124. mobile_agent/bench/truth/iphone15pro.json +138 -0
  125. mobile_agent/bench/truth.py +340 -0
  126. mobile_agent/bench/tuning.py +543 -0
  127. mobile_agent/bench/tuning_analysis.py +656 -0
  128. mobile_agent/bench/vague-heldout-manifest.json +12 -0
  129. mobile_agent/bench/vague_rubric.py +135 -0
  130. mobile_agent/bench/vague_run.py +243 -0
  131. mobile_agent/brand.py +454 -0
  132. mobile_agent/brand_fonts.py +756 -0
  133. mobile_agent/build_identity.py +21 -0
  134. mobile_agent/cable_bt.py +623 -0
  135. mobile_agent/cable_bt_runtime.py +128 -0
  136. mobile_agent/catalog.py +69 -0
  137. mobile_agent/chart_svg.py +415 -0
  138. mobile_agent/chat_here.py +189 -0
  139. mobile_agent/cli_constants.py +5 -0
  140. mobile_agent/cli_parser.py +611 -0
  141. mobile_agent/cli_run.py +115 -0
  142. mobile_agent/compiled_read/__init__.py +21 -0
  143. mobile_agent/compiled_read/delta.py +89 -0
  144. mobile_agent/compiled_read/extractors.py +202 -0
  145. mobile_agent/compiled_read/routes.py +56 -0
  146. mobile_agent/completion.py +267 -0
  147. mobile_agent/compose.py +48 -0
  148. mobile_agent/config.py +114 -0
  149. mobile_agent/connectors/__init__.py +1 -0
  150. mobile_agent/connectors/api_session.py +76 -0
  151. mobile_agent/connectors/callback_page.py +146 -0
  152. mobile_agent/connectors/client.py +582 -0
  153. mobile_agent/connectors/github_app.py +55 -0
  154. mobile_agent/connectors/graph_api.py +396 -0
  155. mobile_agent/connectors/imports.py +170 -0
  156. mobile_agent/connectors/issue.py +66 -0
  157. mobile_agent/connectors/keystore.py +85 -0
  158. mobile_agent/connectors/marks.py +222 -0
  159. mobile_agent/connectors/oauth.py +476 -0
  160. mobile_agent/connectors/picker.py +228 -0
  161. mobile_agent/connectors/presets.py +591 -0
  162. mobile_agent/connectors/register.py +48 -0
  163. mobile_agent/connectors/routes.py +153 -0
  164. mobile_agent/connectors/service.py +966 -0
  165. mobile_agent/connectors/shared.py +136 -0
  166. mobile_agent/connectors/skill.py +446 -0
  167. mobile_agent/connectors/slack_api.py +269 -0
  168. mobile_agent/connectors/steps.py +92 -0
  169. mobile_agent/connectors/store.py +144 -0
  170. mobile_agent/connectors/vault.py +72 -0
  171. mobile_agent/console.py +178 -0
  172. mobile_agent/content_queue/__init__.py +1 -0
  173. mobile_agent/content_queue/client.py +263 -0
  174. mobile_agent/content_queue/importing.py +153 -0
  175. mobile_agent/content_queue/media_check.py +224 -0
  176. mobile_agent/content_queue/prompt.py +64 -0
  177. mobile_agent/content_queue/register.py +91 -0
  178. mobile_agent/content_queue/routes.py +370 -0
  179. mobile_agent/content_queue/runner.py +475 -0
  180. mobile_agent/content_queue/store.py +521 -0
  181. mobile_agent/contract.py +996 -0
  182. mobile_agent/contract_asks.py +248 -0
  183. mobile_agent/contract_prompt.py +45 -0
  184. mobile_agent/contract_screen.py +981 -0
  185. mobile_agent/contract_view.py +84 -0
  186. mobile_agent/corpus/__init__.py +26 -0
  187. mobile_agent/corpus/cli.py +63 -0
  188. mobile_agent/corpus/consent.py +55 -0
  189. mobile_agent/corpus/listener.py +76 -0
  190. mobile_agent/corpus/register.py +9 -0
  191. mobile_agent/corpus/skeleton.py +72 -0
  192. mobile_agent/corpus/store.py +143 -0
  193. mobile_agent/costs.py +161 -0
  194. mobile_agent/decider_scope.py +40 -0
  195. mobile_agent/decision_provider.py +189 -0
  196. mobile_agent/deeplinks.py +352 -0
  197. mobile_agent/device_info.py +116 -0
  198. mobile_agent/device_manager.py +978 -0
  199. mobile_agent/device_status.py +195 -0
  200. mobile_agent/device_targets.py +217 -0
  201. mobile_agent/devices.py +676 -0
  202. mobile_agent/devkit/__init__.py +64 -0
  203. mobile_agent/devkit/account_cli.py +90 -0
  204. mobile_agent/devkit/asc.py +562 -0
  205. mobile_agent/devkit/asc_cli.py +214 -0
  206. mobile_agent/devkit/ci.py +737 -0
  207. mobile_agent/devkit/ci_cli.py +196 -0
  208. mobile_agent/devkit/ci_hosts.py +194 -0
  209. mobile_agent/devkit/es256.py +197 -0
  210. mobile_agent/devkit/expo.py +440 -0
  211. mobile_agent/devkit/expo_cli.py +405 -0
  212. mobile_agent/devkit/expo_dev.py +203 -0
  213. mobile_agent/devkit/expo_routes.py +90 -0
  214. mobile_agent/devkit/fixtures_cli.py +133 -0
  215. mobile_agent/devkit/fixtures_routes.py +82 -0
  216. mobile_agent/devkit/links.py +375 -0
  217. mobile_agent/devkit/links_cli.py +123 -0
  218. mobile_agent/devkit/links_routes.py +83 -0
  219. mobile_agent/devkit/nightly.py +152 -0
  220. mobile_agent/devkit/shots.py +203 -0
  221. mobile_agent/devkit/testflight.py +155 -0
  222. mobile_agent/devkit/testflight_cli.py +127 -0
  223. mobile_agent/devkit/testflight_routes.py +111 -0
  224. mobile_agent/devkit/xcode.py +236 -0
  225. mobile_agent/devkit/xcode_cli.py +101 -0
  226. mobile_agent/devtools.py +419 -0
  227. mobile_agent/doctor.py +509 -0
  228. mobile_agent/doctor_fix.py +467 -0
  229. mobile_agent/downloads.py +55 -0
  230. mobile_agent/drivers.py +793 -0
  231. mobile_agent/dry_run.py +102 -0
  232. mobile_agent/effect_ledger.py +83 -0
  233. mobile_agent/engine_health.py +378 -0
  234. mobile_agent/engine_hooks.py +96 -0
  235. mobile_agent/engines.py +408 -0
  236. mobile_agent/env_file.py +63 -0
  237. mobile_agent/env_secrets.py +149 -0
  238. mobile_agent/errors.py +15 -0
  239. mobile_agent/evals/__init__.py +3 -0
  240. mobile_agent/evals/app_survey.py +149 -0
  241. mobile_agent/evals/development/__init__.py +1 -0
  242. mobile_agent/evals/development/score.py +276 -0
  243. mobile_agent/evals/latency_report.py +312 -0
  244. mobile_agent/evals/offline_report.py +374 -0
  245. mobile_agent/evals/oracles.py +298 -0
  246. mobile_agent/evals/redteam/__init__.py +15 -0
  247. mobile_agent/evals/redteam/__main__.py +72 -0
  248. mobile_agent/evals/redteam/cases.py +730 -0
  249. mobile_agent/evals/redteam/harness.py +604 -0
  250. mobile_agent/evals/redteam/live.py +139 -0
  251. mobile_agent/evals/redteam/plain.py +85 -0
  252. mobile_agent/evals/vision/__init__.py +0 -0
  253. mobile_agent/evals/vision/images/dog_001.jpg +0 -0
  254. mobile_agent/evals/vision/images/dog_002.jpg +0 -0
  255. mobile_agent/evals/vision/images/dog_003.jpg +0 -0
  256. mobile_agent/evals/vision/images/dog_004.jpg +0 -0
  257. mobile_agent/evals/vision/images/dog_005.jpg +0 -0
  258. mobile_agent/evals/vision/images/dog_006.jpg +0 -0
  259. mobile_agent/evals/vision/images/dog_007.jpg +0 -0
  260. mobile_agent/evals/vision/images/dog_010.jpg +0 -0
  261. mobile_agent/evals/vision/images/dog_011.jpg +0 -0
  262. mobile_agent/evals/vision/images/dog_013.jpg +0 -0
  263. mobile_agent/evals/vision/images/dog_014.jpg +0 -0
  264. mobile_agent/evals/vision/images/dog_015.jpg +0 -0
  265. mobile_agent/evals/vision/images/dog_016.jpg +0 -0
  266. mobile_agent/evals/vision/images/dog_017.jpg +0 -0
  267. mobile_agent/evals/vision/images/dog_019.jpg +0 -0
  268. mobile_agent/evals/vision/images/dog_020.jpg +0 -0
  269. mobile_agent/evals/vision/images/dog_021.jpg +0 -0
  270. mobile_agent/evals/vision/images/dog_022.jpg +0 -0
  271. mobile_agent/evals/vision/images/dog_023.jpg +0 -0
  272. mobile_agent/evals/vision/images/dog_024.jpg +0 -0
  273. mobile_agent/evals/vision/images/dog_026.jpg +0 -0
  274. mobile_agent/evals/vision/images/dog_027.jpg +0 -0
  275. mobile_agent/evals/vision/images/dog_034.jpg +0 -0
  276. mobile_agent/evals/vision/images/dog_035.jpg +0 -0
  277. mobile_agent/evals/vision/images/dog_038.jpg +0 -0
  278. mobile_agent/evals/vision/images/dog_042.jpg +0 -0
  279. mobile_agent/evals/vision/images/dog_047.jpg +0 -0
  280. mobile_agent/evals/vision/images/dog_052.jpg +0 -0
  281. mobile_agent/evals/vision/images/dog_058.jpg +0 -0
  282. mobile_agent/evals/vision/images/dog_062.jpg +0 -0
  283. mobile_agent/evals/vision/images/eyes_000.jpg +0 -0
  284. mobile_agent/evals/vision/images/eyes_010.jpg +0 -0
  285. mobile_agent/evals/vision/images/eyes_011.jpg +0 -0
  286. mobile_agent/evals/vision/images/eyes_015.jpg +0 -0
  287. mobile_agent/evals/vision/images/eyes_016.jpg +0 -0
  288. mobile_agent/evals/vision/images/eyes_017.jpg +0 -0
  289. mobile_agent/evals/vision/images/eyes_019.jpg +0 -0
  290. mobile_agent/evals/vision/images/eyes_021.jpg +0 -0
  291. mobile_agent/evals/vision/images/eyes_025.jpg +0 -0
  292. mobile_agent/evals/vision/images/eyes_026.jpg +0 -0
  293. mobile_agent/evals/vision/images/eyes_027.jpg +0 -0
  294. mobile_agent/evals/vision/images/eyes_030.jpg +0 -0
  295. mobile_agent/evals/vision/images/eyes_032.jpg +0 -0
  296. mobile_agent/evals/vision/images/eyes_033.jpg +0 -0
  297. mobile_agent/evals/vision/images/eyes_034.jpg +0 -0
  298. mobile_agent/evals/vision/images/eyes_035.jpg +0 -0
  299. mobile_agent/evals/vision/images/eyes_036.jpg +0 -0
  300. mobile_agent/evals/vision/images/eyes_037.jpg +0 -0
  301. mobile_agent/evals/vision/images/eyes_039.jpg +0 -0
  302. mobile_agent/evals/vision/images/eyes_040.jpg +0 -0
  303. mobile_agent/evals/vision/images/eyes_041.jpg +0 -0
  304. mobile_agent/evals/vision/images/eyes_042.jpg +0 -0
  305. mobile_agent/evals/vision/images/eyes_043.jpg +0 -0
  306. mobile_agent/evals/vision/images/eyes_044.jpg +0 -0
  307. mobile_agent/evals/vision/images/eyes_046.jpg +0 -0
  308. mobile_agent/evals/vision/images/eyes_047.jpg +0 -0
  309. mobile_agent/evals/vision/images/eyes_048.jpg +0 -0
  310. mobile_agent/evals/vision/images/eyes_049.jpg +0 -0
  311. mobile_agent/evals/vision/images/eyes_051.jpg +0 -0
  312. mobile_agent/evals/vision/images/eyes_053.jpg +0 -0
  313. mobile_agent/evals/vision/images/eyes_054.jpg +0 -0
  314. mobile_agent/evals/vision/images/eyes_055.jpg +0 -0
  315. mobile_agent/evals/vision/images/eyes_058.jpg +0 -0
  316. mobile_agent/evals/vision/images/eyes_059.jpg +0 -0
  317. mobile_agent/evals/vision/images/receipt_002.jpg +0 -0
  318. mobile_agent/evals/vision/images/receipt_003.jpg +0 -0
  319. mobile_agent/evals/vision/images/receipt_004.jpg +0 -0
  320. mobile_agent/evals/vision/images/receipt_007.jpg +0 -0
  321. mobile_agent/evals/vision/images/receipt_009.jpg +0 -0
  322. mobile_agent/evals/vision/images/receipt_010.jpg +0 -0
  323. mobile_agent/evals/vision/images/receipt_011.jpg +0 -0
  324. mobile_agent/evals/vision/images/receipt_012.jpg +0 -0
  325. mobile_agent/evals/vision/images/receipt_013.jpg +0 -0
  326. mobile_agent/evals/vision/images/receipt_014.jpg +0 -0
  327. mobile_agent/evals/vision/images/receipt_016.jpg +0 -0
  328. mobile_agent/evals/vision/images/receipt_017.jpg +0 -0
  329. mobile_agent/evals/vision/images/receipt_023.jpg +0 -0
  330. mobile_agent/evals/vision/images/receipt_027.jpg +0 -0
  331. mobile_agent/evals/vision/images/receipt_029.jpg +0 -0
  332. mobile_agent/evals/vision/images/receipt_032.jpg +0 -0
  333. mobile_agent/evals/vision/images/receipt_033.jpg +0 -0
  334. mobile_agent/evals/vision/images/receipt_036.jpg +0 -0
  335. mobile_agent/evals/vision/images/receipt_041.jpg +0 -0
  336. mobile_agent/evals/vision/images/receipt_043.jpg +0 -0
  337. mobile_agent/evals/vision/images/receipt_047.jpg +0 -0
  338. mobile_agent/evals/vision/images/receipt_049.jpg +0 -0
  339. mobile_agent/evals/vision/images/receipt_051.jpg +0 -0
  340. mobile_agent/evals/vision/images/receipt_053.jpg +0 -0
  341. mobile_agent/evals/vision/images/receipt_056.jpg +0 -0
  342. mobile_agent/evals/vision/images/receipt_057.jpg +0 -0
  343. mobile_agent/evals/vision/images/receipt_058.jpg +0 -0
  344. mobile_agent/evals/vision/images/receipt_059.jpg +0 -0
  345. mobile_agent/evals/vision/images/receipt_060.jpg +0 -0
  346. mobile_agent/evals/vision/images/receipt_061.jpg +0 -0
  347. mobile_agent/evals/vision/images/receipt_062.jpg +0 -0
  348. mobile_agent/evals/vision/images/receipt_063.jpg +0 -0
  349. mobile_agent/evals/vision/images/receipt_064.jpg +0 -0
  350. mobile_agent/evals/vision/images/receipt_065.jpg +0 -0
  351. mobile_agent/evals/vision/images/receipt_066.jpg +0 -0
  352. mobile_agent/evals/vision/images/receipt_067.jpg +0 -0
  353. mobile_agent/evals/vision/images/receipt_068.jpg +0 -0
  354. mobile_agent/evals/vision/images/receipt_069.jpg +0 -0
  355. mobile_agent/evals/vision/images/receipt_070.jpg +0 -0
  356. mobile_agent/evals/vision/images/receipt_071.jpg +0 -0
  357. mobile_agent/evals/vision/images/sunglasses_000.jpg +0 -0
  358. mobile_agent/evals/vision/images/sunglasses_001.jpg +0 -0
  359. mobile_agent/evals/vision/images/sunglasses_002.jpg +0 -0
  360. mobile_agent/evals/vision/images/sunglasses_004.jpg +0 -0
  361. mobile_agent/evals/vision/images/sunglasses_005.jpg +0 -0
  362. mobile_agent/evals/vision/images/sunglasses_008.jpg +0 -0
  363. mobile_agent/evals/vision/images/sunglasses_009.jpg +0 -0
  364. mobile_agent/evals/vision/images/sunglasses_010.jpg +0 -0
  365. mobile_agent/evals/vision/images/sunglasses_012.jpg +0 -0
  366. mobile_agent/evals/vision/images/sunglasses_013.jpg +0 -0
  367. mobile_agent/evals/vision/images/sunglasses_015.jpg +0 -0
  368. mobile_agent/evals/vision/images/sunglasses_016.jpg +0 -0
  369. mobile_agent/evals/vision/images/sunglasses_017.jpg +0 -0
  370. mobile_agent/evals/vision/images/sunglasses_018.jpg +0 -0
  371. mobile_agent/evals/vision/images/sunglasses_021.jpg +0 -0
  372. mobile_agent/evals/vision/images/sunglasses_022.jpg +0 -0
  373. mobile_agent/evals/vision/images/sunglasses_023.jpg +0 -0
  374. mobile_agent/evals/vision/images/sunglasses_024.jpg +0 -0
  375. mobile_agent/evals/vision/images/sunglasses_030.jpg +0 -0
  376. mobile_agent/evals/vision/images/sunglasses_031.jpg +0 -0
  377. mobile_agent/evals/vision/images/sunglasses_032.jpg +0 -0
  378. mobile_agent/evals/vision/images/sunglasses_033.jpg +0 -0
  379. mobile_agent/evals/vision/images/sunglasses_034.jpg +0 -0
  380. mobile_agent/evals/vision/images/sunglasses_036.jpg +0 -0
  381. mobile_agent/evals/vision/images/sunglasses_038.jpg +0 -0
  382. mobile_agent/evals/vision/images/sunglasses_039.jpg +0 -0
  383. mobile_agent/evals/vision/images/sunglasses_040.jpg +0 -0
  384. mobile_agent/evals/vision/images/sunglasses_041.jpg +0 -0
  385. mobile_agent/evals/vision/manifest.json +2355 -0
  386. mobile_agent/eventbus.py +110 -0
  387. mobile_agent/explain.py +34 -0
  388. mobile_agent/failure_line.py +197 -0
  389. mobile_agent/fanout.py +663 -0
  390. mobile_agent/feedback/__init__.py +2 -0
  391. mobile_agent/feedback/bundle.py +204 -0
  392. mobile_agent/feedback/outbox.py +196 -0
  393. mobile_agent/feedback/register.py +9 -0
  394. mobile_agent/feedback/routes.py +177 -0
  395. mobile_agent/fleet.py +841 -0
  396. mobile_agent/fleet_health.py +729 -0
  397. mobile_agent/fleet_phone.py +186 -0
  398. mobile_agent/frontier/__init__.py +79 -0
  399. mobile_agent/frontier/act.py +380 -0
  400. mobile_agent/frontier/actions.py +70 -0
  401. mobile_agent/frontier/agent.py +256 -0
  402. mobile_agent/frontier/approvals.py +366 -0
  403. mobile_agent/frontier/hooks.py +199 -0
  404. mobile_agent/frontier/interrupts.py +125 -0
  405. mobile_agent/frontier/observe.py +331 -0
  406. mobile_agent/frontier/phases.py +381 -0
  407. mobile_agent/frontier/pipeline.py +61 -0
  408. mobile_agent/frontier/prompt.py +384 -0
  409. mobile_agent/frontier/protocol.py +91 -0
  410. mobile_agent/frontier/safety.py +467 -0
  411. mobile_agent/frontier/saved.py +375 -0
  412. mobile_agent/frontier/screen.py +419 -0
  413. mobile_agent/frontier/skills.py +147 -0
  414. mobile_agent/frontier/stages/__init__.py +1 -0
  415. mobile_agent/frontier/stages/after.py +192 -0
  416. mobile_agent/frontier/stages/approve.py +138 -0
  417. mobile_agent/frontier/stages/controls.py +115 -0
  418. mobile_agent/frontier/stages/ends.py +102 -0
  419. mobile_agent/frontier/stages/gate.py +92 -0
  420. mobile_agent/frontier/stages/guards.py +132 -0
  421. mobile_agent/frontier/stages/points.py +155 -0
  422. mobile_agent/frontier/stages/send.py +177 -0
  423. mobile_agent/frontier/turn.py +151 -0
  424. mobile_agent/full_disk_access.py +83 -0
  425. mobile_agent/gemini.py +69 -0
  426. mobile_agent/genuine.py +120 -0
  427. mobile_agent/graph/__init__.py +4 -0
  428. mobile_agent/graph/catalog.py +789 -0
  429. mobile_agent/graph/chain.py +306 -0
  430. mobile_agent/graph/dispatch.py +98 -0
  431. mobile_agent/graph/draft.py +373 -0
  432. mobile_agent/graph/edit.py +76 -0
  433. mobile_agent/graph/estimate.py +189 -0
  434. mobile_agent/graph/expr.py +562 -0
  435. mobile_agent/graph/functions.py +271 -0
  436. mobile_agent/graph/infer.py +144 -0
  437. mobile_agent/graph/nodes.py +936 -0
  438. mobile_agent/graph/phone_steps.py +178 -0
  439. mobile_agent/graph/rerun.py +327 -0
  440. mobile_agent/graph/run.py +899 -0
  441. mobile_agent/graph/schema.py +842 -0
  442. mobile_agent/graph/state.py +161 -0
  443. mobile_agent/graph/values.py +198 -0
  444. mobile_agent/harness/__init__.py +2 -0
  445. mobile_agent/harness/checkpoints.py +468 -0
  446. mobile_agent/harness/control.py +101 -0
  447. mobile_agent/harness/prewarm.py +213 -0
  448. mobile_agent/harness/register.py +43 -0
  449. mobile_agent/harness/routes.py +114 -0
  450. mobile_agent/harness/settings.py +66 -0
  451. mobile_agent/harness/tools/__init__.py +67 -0
  452. mobile_agent/harness/tools/ask_user.py +102 -0
  453. mobile_agent/harness/tools/watch.py +120 -0
  454. mobile_agent/harness_api.py +815 -0
  455. mobile_agent/hedge.py +203 -0
  456. mobile_agent/help_topics.py +305 -0
  457. mobile_agent/hints.py +248 -0
  458. mobile_agent/history.py +69 -0
  459. mobile_agent/home.py +214 -0
  460. mobile_agent/http_pool.py +175 -0
  461. mobile_agent/image_policy.py +139 -0
  462. mobile_agent/inbox.py +148 -0
  463. mobile_agent/installed_apps.py +344 -0
  464. mobile_agent/integrations/__init__.py +1 -0
  465. mobile_agent/integrations/cli.py +472 -0
  466. mobile_agent/integrations/client_files.py +65 -0
  467. mobile_agent/integrations/clients.py +998 -0
  468. mobile_agent/integrations/doctor.py +185 -0
  469. mobile_agent/integrations/jsonc.py +383 -0
  470. mobile_agent/integrations/routes.py +135 -0
  471. mobile_agent/integrations/rules.py +112 -0
  472. mobile_agent/integrations/server_options.py +46 -0
  473. mobile_agent/integrations/skill.py +152 -0
  474. mobile_agent/integrations/skill_files.py +10 -0
  475. mobile_agent/integrations/textedit.py +309 -0
  476. mobile_agent/intents/__init__.py +17 -0
  477. mobile_agent/intents/atlas.py +216 -0
  478. mobile_agent/intents/callback.py +44 -0
  479. mobile_agent/intents/cli.py +55 -0
  480. mobile_agent/intents/harvest.py +91 -0
  481. mobile_agent/intents/plan.py +93 -0
  482. mobile_agent/ios/__init__.py +1 -0
  483. mobile_agent/ios/guide.py +16 -0
  484. mobile_agent/ios/kb.py +212 -0
  485. mobile_agent/ios/kb_data.py +1588 -0
  486. mobile_agent/ios/mcp.py +44 -0
  487. mobile_agent/ios/register.py +8 -0
  488. mobile_agent/ios/system.py +369 -0
  489. mobile_agent/journal.py +531 -0
  490. mobile_agent/keychain_acl.py +374 -0
  491. mobile_agent/keys.py +395 -0
  492. mobile_agent/latency_overlap.py +36 -0
  493. mobile_agent/latency_trace.py +170 -0
  494. mobile_agent/learned/__init__.py +15 -0
  495. mobile_agent/learned/app_path.py +160 -0
  496. mobile_agent/learned/code_read.py +38 -0
  497. mobile_agent/learned/parts.py +546 -0
  498. mobile_agent/learned/programs.py +691 -0
  499. mobile_agent/learned/register.py +171 -0
  500. mobile_agent/learned/routes.py +61 -0
  501. mobile_agent/learned/taught.py +329 -0
  502. mobile_agent/learned/teach.py +538 -0
  503. mobile_agent/learned/workflows.py +647 -0
  504. mobile_agent/learned/zero.py +41 -0
  505. mobile_agent/links.py +37 -0
  506. mobile_agent/listen.py +607 -0
  507. mobile_agent/lock_spike.py +278 -0
  508. mobile_agent/lockscreen.py +526 -0
  509. mobile_agent/login.py +354 -0
  510. mobile_agent/longform/__init__.py +15 -0
  511. mobile_agent/longform/ads.py +191 -0
  512. mobile_agent/longform/blocks.py +117 -0
  513. mobile_agent/longform/budget.py +265 -0
  514. mobile_agent/longform/context.py +285 -0
  515. mobile_agent/longform/costs.py +93 -0
  516. mobile_agent/longform/digest.py +145 -0
  517. mobile_agent/longform/extract.py +508 -0
  518. mobile_agent/longform/feed.py +505 -0
  519. mobile_agent/longform/followup.py +168 -0
  520. mobile_agent/longform/frames.py +82 -0
  521. mobile_agent/longform/identity.py +87 -0
  522. mobile_agent/longform/inbox.py +221 -0
  523. mobile_agent/longform/items.py +425 -0
  524. mobile_agent/longform/models.py +115 -0
  525. mobile_agent/longform/notebook.py +329 -0
  526. mobile_agent/longform/plan.py +614 -0
  527. mobile_agent/longform/recall.py +195 -0
  528. mobile_agent/longform/receipts.py +81 -0
  529. mobile_agent/longform/register.py +124 -0
  530. mobile_agent/longform/report.py +343 -0
  531. mobile_agent/longform/routes.py +102 -0
  532. mobile_agent/longform/session.py +644 -0
  533. mobile_agent/longform/store.py +146 -0
  534. mobile_agent/longform/synthesis.py +347 -0
  535. mobile_agent/mac_tools.py +223 -0
  536. mobile_agent/manual_control.py +143 -0
  537. mobile_agent/maps.py +149 -0
  538. mobile_agent/marks.py +92 -0
  539. mobile_agent/mcp_server/__init__.py +3 -0
  540. mobile_agent/mcp_server/__main__.py +29 -0
  541. mobile_agent/mcp_server/action_journal.py +24 -0
  542. mobile_agent/mcp_server/action_tools.py +638 -0
  543. mobile_agent/mcp_server/approvals.py +219 -0
  544. mobile_agent/mcp_server/catalog.py +92 -0
  545. mobile_agent/mcp_server/cli.py +194 -0
  546. mobile_agent/mcp_server/codes.py +147 -0
  547. mobile_agent/mcp_server/device_tools.py +306 -0
  548. mobile_agent/mcp_server/direct.py +272 -0
  549. mobile_agent/mcp_server/expo_tools.py +105 -0
  550. mobile_agent/mcp_server/phone_tools.py +510 -0
  551. mobile_agent/mcp_server/protocol.py +364 -0
  552. mobile_agent/mcp_server/registry.py +55 -0
  553. mobile_agent/mcp_server/run_tools.py +531 -0
  554. mobile_agent/mcp_server/schemas.py +425 -0
  555. mobile_agent/mcp_server/screen_tools.py +168 -0
  556. mobile_agent/mcp_server/session.py +469 -0
  557. mobile_agent/mcp_server/support.py +145 -0
  558. mobile_agent/mcp_server/tools.py +493 -0
  559. mobile_agent/memory/__init__.py +9 -0
  560. mobile_agent/memory/cli.py +207 -0
  561. mobile_agent/memory/extract.py +191 -0
  562. mobile_agent/memory/listener.py +100 -0
  563. mobile_agent/memory/model.py +144 -0
  564. mobile_agent/memory/project.py +96 -0
  565. mobile_agent/memory/provider.py +127 -0
  566. mobile_agent/memory/register.py +21 -0
  567. mobile_agent/memory/retrieval.py +172 -0
  568. mobile_agent/memory/routes.py +216 -0
  569. mobile_agent/memory/runs.py +124 -0
  570. mobile_agent/memory/store.py +806 -0
  571. mobile_agent/memory/writer.py +278 -0
  572. mobile_agent/mirroring.py +585 -0
  573. mobile_agent/mirroring_runtime.py +221 -0
  574. mobile_agent/model_clients.py +604 -0
  575. mobile_agent/narrate.py +813 -0
  576. mobile_agent/native_helpers.py +84 -0
  577. mobile_agent/netxray/__init__.py +1 -0
  578. mobile_agent/netxray/analyze.py +322 -0
  579. mobile_agent/netxray/attribution.py +62 -0
  580. mobile_agent/netxray/capture.py +88 -0
  581. mobile_agent/netxray/cli.py +142 -0
  582. mobile_agent/netxray/job.py +219 -0
  583. mobile_agent/netxray/mobster-pcap.c +560 -0
  584. mobile_agent/netxray/packets.py +410 -0
  585. mobile_agent/netxray/pcapng.py +223 -0
  586. mobile_agent/netxray/register.py +93 -0
  587. mobile_agent/netxray/report.py +214 -0
  588. mobile_agent/netxray/store.py +48 -0
  589. mobile_agent/netxray/summary.py +157 -0
  590. mobile_agent/netxray/target.py +67 -0
  591. mobile_agent/netxray/tool.py +66 -0
  592. mobile_agent/netxray/trackers.py +194 -0
  593. mobile_agent/notify_triggers/__init__.py +1 -0
  594. mobile_agent/notify_triggers/rules.py +39 -0
  595. mobile_agent/notify_triggers/sources.py +315 -0
  596. mobile_agent/notify_triggers/watch.py +135 -0
  597. mobile_agent/offers.py +86 -0
  598. mobile_agent/orchestrate.py +215 -0
  599. mobile_agent/outlets/__init__.py +1 -0
  600. mobile_agent/outlets/connect.py +249 -0
  601. mobile_agent/outlets/folder.py +180 -0
  602. mobile_agent/outlets/hooks.py +453 -0
  603. mobile_agent/outlets/inbound.py +472 -0
  604. mobile_agent/outlets/inbound_reply.py +57 -0
  605. mobile_agent/outlets/inbound_routes.py +81 -0
  606. mobile_agent/outlets/kinds.py +308 -0
  607. mobile_agent/outlets/mail.py +110 -0
  608. mobile_agent/outlets/net.py +86 -0
  609. mobile_agent/outlets/notion.py +244 -0
  610. mobile_agent/outlets/register.py +56 -0
  611. mobile_agent/outlets/routes.py +135 -0
  612. mobile_agent/outlets/service.py +580 -0
  613. mobile_agent/outlets/sheets.py +169 -0
  614. mobile_agent/outlets/slack.py +177 -0
  615. mobile_agent/outlets/store.py +194 -0
  616. mobile_agent/outlets/vault.py +80 -0
  617. mobile_agent/outlets/webhook.py +316 -0
  618. mobile_agent/output_contract.py +141 -0
  619. mobile_agent/owner_presence.py +211 -0
  620. mobile_agent/ownership.py +219 -0
  621. mobile_agent/packs/__init__.py +1 -0
  622. mobile_agent/packs/calendar.py +243 -0
  623. mobile_agent/packs/context.py +68 -0
  624. mobile_agent/packs/core.py +757 -0
  625. mobile_agent/packs/front_door.py +316 -0
  626. mobile_agent/packs/heal.py +214 -0
  627. mobile_agent/packs/health.py +122 -0
  628. mobile_agent/packs/insights.py +76 -0
  629. mobile_agent/packs/instagram.py +640 -0
  630. mobile_agent/packs/linkedin.py +106 -0
  631. mobile_agent/packs/maps.py +196 -0
  632. mobile_agent/packs/mcp.py +149 -0
  633. mobile_agent/packs/messages.py +270 -0
  634. mobile_agent/packs/notes.py +226 -0
  635. mobile_agent/packs/photos.py +166 -0
  636. mobile_agent/packs/pins.py +47 -0
  637. mobile_agent/packs/reddit.py +110 -0
  638. mobile_agent/packs/register.py +276 -0
  639. mobile_agent/packs/rows.py +108 -0
  640. mobile_agent/packs/safari.py +143 -0
  641. mobile_agent/packs/settings.py +253 -0
  642. mobile_agent/packs/social.py +300 -0
  643. mobile_agent/packs/threads.py +97 -0
  644. mobile_agent/packs/tiktok.py +455 -0
  645. mobile_agent/packs/tools.py +290 -0
  646. mobile_agent/packs/x.py +127 -0
  647. mobile_agent/packs/youtube.py +199 -0
  648. mobile_agent/parity.py +270 -0
  649. mobile_agent/paths.py +99 -0
  650. mobile_agent/perception.py +470 -0
  651. mobile_agent/permissions/__init__.py +32 -0
  652. mobile_agent/permissions/consequence.py +535 -0
  653. mobile_agent/permissions/exceptions.py +99 -0
  654. mobile_agent/permissions/gate.py +316 -0
  655. mobile_agent/permissions/outside.py +316 -0
  656. mobile_agent/permissions/policy.py +288 -0
  657. mobile_agent/permissions/project_policy.py +411 -0
  658. mobile_agent/permissions/register.py +148 -0
  659. mobile_agent/permissions/rules.py +291 -0
  660. mobile_agent/phone_io/__init__.py +146 -0
  661. mobile_agent/phone_io/cli.py +427 -0
  662. mobile_agent/phone_io/clipboard.py +78 -0
  663. mobile_agent/phone_io/controls.py +78 -0
  664. mobile_agent/phone_io/files.py +289 -0
  665. mobile_agent/phone_io/handoff.py +123 -0
  666. mobile_agent/phone_io/icloud.py +118 -0
  667. mobile_agent/phone_io/install.py +188 -0
  668. mobile_agent/phone_io/network_capture.py +188 -0
  669. mobile_agent/phone_io/network_cli.py +138 -0
  670. mobile_agent/phone_io/network_profiles.py +138 -0
  671. mobile_agent/phone_io/photos.py +512 -0
  672. mobile_agent/phone_io/recording.py +115 -0
  673. mobile_agent/phone_io/secrets.py +146 -0
  674. mobile_agent/phone_io/transfer.py +895 -0
  675. mobile_agent/phone_io/tunnel.py +129 -0
  676. mobile_agent/phone_ready.py +91 -0
  677. mobile_agent/pinning.py +158 -0
  678. mobile_agent/plan_gate.py +807 -0
  679. mobile_agent/plugins/__init__.py +3 -0
  680. mobile_agent/plugins/install.py +163 -0
  681. mobile_agent/plugins/manifest.py +242 -0
  682. mobile_agent/plugins/register.py +15 -0
  683. mobile_agent/plugins/routes.py +82 -0
  684. mobile_agent/plugins/service.py +322 -0
  685. mobile_agent/plugins/skills.py +76 -0
  686. mobile_agent/plugins/store.py +79 -0
  687. mobile_agent/pool.py +285 -0
  688. mobile_agent/power_watch.py +275 -0
  689. mobile_agent/premium_model.py +22 -0
  690. mobile_agent/project_env.py +291 -0
  691. mobile_agent/project_env_cli.py +74 -0
  692. mobile_agent/project_env_routes.py +55 -0
  693. mobile_agent/projects/__init__.py +27 -0
  694. mobile_agent/projects/ci.py +332 -0
  695. mobile_agent/projects/clone.py +339 -0
  696. mobile_agent/projects/drafts.py +158 -0
  697. mobile_agent/projects/folder.py +436 -0
  698. mobile_agent/projects/github.py +131 -0
  699. mobile_agent/projects/register.py +31 -0
  700. mobile_agent/projects/revisions.py +253 -0
  701. mobile_agent/projects/routes.py +234 -0
  702. mobile_agent/projects/runs.py +297 -0
  703. mobile_agent/projects/service.py +695 -0
  704. mobile_agent/projects/setup.py +84 -0
  705. mobile_agent/projects/store.py +379 -0
  706. mobile_agent/projects/sync.py +380 -0
  707. mobile_agent/projects/workflow_files.py +270 -0
  708. mobile_agent/replay_core/__init__.py +26 -0
  709. mobile_agent/replay_core/checks.py +594 -0
  710. mobile_agent/replay_core/engine.py +596 -0
  711. mobile_agent/replay_core/heal.py +364 -0
  712. mobile_agent/replay_core/path.py +240 -0
  713. mobile_agent/replay_core/recorder.py +206 -0
  714. mobile_agent/replay_core/store.py +171 -0
  715. mobile_agent/result_blocks.py +941 -0
  716. mobile_agent/run.py +642 -0
  717. mobile_agent/run_delivery.py +123 -0
  718. mobile_agent/run_executor.py +200 -0
  719. mobile_agent/run_export.py +284 -0
  720. mobile_agent/run_gif.py +891 -0
  721. mobile_agent/runner_hints.py +86 -0
  722. mobile_agent/runner_renewal.py +205 -0
  723. mobile_agent/runtime_create.py +549 -0
  724. mobile_agent/runtime_driver.py +458 -0
  725. mobile_agent/runtime_limits.py +319 -0
  726. mobile_agent/runtime_queue.py +394 -0
  727. mobile_agent/runtime_runs.py +383 -0
  728. mobile_agent/runtime_status.py +181 -0
  729. mobile_agent/runtime_teach.py +295 -0
  730. mobile_agent/runtime_work.py +599 -0
  731. mobile_agent/screen_text.py +101 -0
  732. mobile_agent/secret_filter.py +113 -0
  733. mobile_agent/serve.py +160 -0
  734. mobile_agent/server.py +363 -0
  735. mobile_agent/service_support.py +42 -0
  736. mobile_agent/settle/__init__.py +1 -0
  737. mobile_agent/settle/predict.py +102 -0
  738. mobile_agent/settle/replay.py +92 -0
  739. mobile_agent/settle/store.py +93 -0
  740. mobile_agent/settle/stream.py +43 -0
  741. mobile_agent/setup_cli.py +225 -0
  742. mobile_agent/setup_errors.py +87 -0
  743. mobile_agent/setup_service.py +527 -0
  744. mobile_agent/share.py +104 -0
  745. mobile_agent/signing.py +184 -0
  746. mobile_agent/sim/__init__.py +6 -0
  747. mobile_agent/sim/__main__.py +26 -0
  748. mobile_agent/sim/api.py +952 -0
  749. mobile_agent/sim/apps.py +172 -0
  750. mobile_agent/sim/biometrics.py +84 -0
  751. mobile_agent/sim/cli.py +271 -0
  752. mobile_agent/sim/doctor.py +293 -0
  753. mobile_agent/sim/governor.py +90 -0
  754. mobile_agent/sim/holders.py +88 -0
  755. mobile_agent/sim/models.py +62 -0
  756. mobile_agent/sim/registry.py +161 -0
  757. mobile_agent/sim/simctl.py +269 -0
  758. mobile_agent/sim/wda.py +618 -0
  759. mobile_agent/sim/xcode.py +74 -0
  760. mobile_agent/skills/__init__.py +12 -0
  761. mobile_agent/skills/codes.py +597 -0
  762. mobile_agent/skills/notifications.py +281 -0
  763. mobile_agent/skills/open_url.py +127 -0
  764. mobile_agent/smart_events.py +588 -0
  765. mobile_agent/smart_result.py +490 -0
  766. mobile_agent/smart_run.py +413 -0
  767. mobile_agent/state.py +662 -0
  768. mobile_agent/states.py +217 -0
  769. mobile_agent/steering.py +70 -0
  770. mobile_agent/style.py +178 -0
  771. mobile_agent/supervised.py +234 -0
  772. mobile_agent/swift/helper_bt_hid.swift +534 -0
  773. mobile_agent/swift/helper_doc_text.swift +184 -0
  774. mobile_agent/swift/helper_draft_reply.swift +170 -0
  775. mobile_agent/swift/helper_map.swift +110 -0
  776. mobile_agent/swift/helper_mirroring.swift +1099 -0
  777. mobile_agent/swift/helper_mjpeg_mp4.swift +103 -0
  778. mobile_agent/swift/helper_pdf.swift +100 -0
  779. mobile_agent/swift/helper_phone_audio.swift +993 -0
  780. mobile_agent/swift/helper_phone_screen.swift +416 -0
  781. mobile_agent/swift/helper_screen_text.swift +24 -0
  782. mobile_agent/swift/helper_transfer.swift +246 -0
  783. mobile_agent/swift/ocr.swift +22 -0
  784. mobile_agent/task_policy.py +337 -0
  785. mobile_agent/team_signing/__init__.py +34 -0
  786. mobile_agent/team_signing/apple_ca.py +37 -0
  787. mobile_agent/team_signing/bundle.py +136 -0
  788. mobile_agent/team_signing/cli.py +208 -0
  789. mobile_agent/team_signing/identity.py +248 -0
  790. mobile_agent/team_signing/ledger.py +105 -0
  791. mobile_agent/team_signing/prebuilt.py +61 -0
  792. mobile_agent/team_signing/resign.py +256 -0
  793. mobile_agent/team_signing/service.py +382 -0
  794. mobile_agent/team_signing/tooling.py +243 -0
  795. mobile_agent/team_signing/writes.py +268 -0
  796. mobile_agent/terminal_image.py +81 -0
  797. mobile_agent/testkit/__init__.py +1 -0
  798. mobile_agent/testkit/a11y_baseline.py +132 -0
  799. mobile_agent/testkit/api.py +403 -0
  800. mobile_agent/testkit/capsule.py +400 -0
  801. mobile_agent/testkit/capsule_cli.py +57 -0
  802. mobile_agent/testkit/cli.py +948 -0
  803. mobile_agent/testkit/cli_arguments.py +193 -0
  804. mobile_agent/testkit/comparison.py +288 -0
  805. mobile_agent/testkit/comparison_cli.py +41 -0
  806. mobile_agent/testkit/discover.py +257 -0
  807. mobile_agent/testkit/editor.py +243 -0
  808. mobile_agent/testkit/execute.py +515 -0
  809. mobile_agent/testkit/github_checks.py +248 -0
  810. mobile_agent/testkit/gitref.py +66 -0
  811. mobile_agent/testkit/heal_review.py +176 -0
  812. mobile_agent/testkit/health.py +480 -0
  813. mobile_agent/testkit/history.py +255 -0
  814. mobile_agent/testkit/hosted_results.py +321 -0
  815. mobile_agent/testkit/input_files.py +173 -0
  816. mobile_agent/testkit/jobs.py +285 -0
  817. mobile_agent/testkit/junit_verdict.py +33 -0
  818. mobile_agent/testkit/maestro.py +802 -0
  819. mobile_agent/testkit/mcp.py +562 -0
  820. mobile_agent/testkit/mcp_comparison.py +88 -0
  821. mobile_agent/testkit/minimize.py +471 -0
  822. mobile_agent/testkit/minimizer_cli.py +48 -0
  823. mobile_agent/testkit/notify.py +212 -0
  824. mobile_agent/testkit/phone_video.py +191 -0
  825. mobile_agent/testkit/pr.py +491 -0
  826. mobile_agent/testkit/pr_report.py +562 -0
  827. mobile_agent/testkit/prbuild.py +148 -0
  828. mobile_agent/testkit/proof_report.py +55 -0
  829. mobile_agent/testkit/provenance.py +119 -0
  830. mobile_agent/testkit/prrun.py +244 -0
  831. mobile_agent/testkit/recent.py +166 -0
  832. mobile_agent/testkit/recipes.py +146 -0
  833. mobile_agent/testkit/record.py +204 -0
  834. mobile_agent/testkit/register.py +288 -0
  835. mobile_agent/testkit/reports.py +998 -0
  836. mobile_agent/testkit/repro.py +432 -0
  837. mobile_agent/testkit/scenario.py +440 -0
  838. mobile_agent/testkit/scenario_cli.py +37 -0
  839. mobile_agent/testkit/snapshots_cli.py +60 -0
  840. mobile_agent/testkit/suite.py +670 -0
  841. mobile_agent/testkit/targets.py +497 -0
  842. mobile_agent/testkit/xctest.py +378 -0
  843. mobile_agent/threads/__init__.py +1 -0
  844. mobile_agent/threads/chat_once.py +264 -0
  845. mobile_agent/threads/cli.py +740 -0
  846. mobile_agent/threads/client.py +326 -0
  847. mobile_agent/threads/context.py +246 -0
  848. mobile_agent/threads/mcp.py +739 -0
  849. mobile_agent/threads/recent.py +34 -0
  850. mobile_agent/threads/register.py +54 -0
  851. mobile_agent/threads/routes.py +188 -0
  852. mobile_agent/threads/service.py +975 -0
  853. mobile_agent/threads/store.py +361 -0
  854. mobile_agent/threads/summary.py +117 -0
  855. mobile_agent/threads/term.py +101 -0
  856. mobile_agent/threads/workflow_cli.py +104 -0
  857. mobile_agent/tls.py +36 -0
  858. mobile_agent/traces.py +271 -0
  859. mobile_agent/tracks.py +235 -0
  860. mobile_agent/transport.py +271 -0
  861. mobile_agent/triggers/__init__.py +1 -0
  862. mobile_agent/triggers/actions.py +204 -0
  863. mobile_agent/triggers/model.py +146 -0
  864. mobile_agent/triggers/register.py +29 -0
  865. mobile_agent/triggers/routes.py +101 -0
  866. mobile_agent/triggers/service.py +387 -0
  867. mobile_agent/triggers/store.py +142 -0
  868. mobile_agent/tui/__init__.py +89 -0
  869. mobile_agent/tui/app.py +582 -0
  870. mobile_agent/tui/chrome.py +321 -0
  871. mobile_agent/tui/commands.py +375 -0
  872. mobile_agent/tui/demo_phone.py +369 -0
  873. mobile_agent/tui/images.py +29 -0
  874. mobile_agent/tui/keys.py +64 -0
  875. mobile_agent/tui/prompt.py +195 -0
  876. mobile_agent/tui/render.py +395 -0
  877. mobile_agent/tui/screens.py +180 -0
  878. mobile_agent/tui/session.py +308 -0
  879. mobile_agent/tui/tasks.py +287 -0
  880. mobile_agent/tui/theme.py +118 -0
  881. mobile_agent/tui/widgets.py +272 -0
  882. mobile_agent/update_check.py +213 -0
  883. mobile_agent/usage.py +184 -0
  884. mobile_agent/verify/__init__.py +0 -0
  885. mobile_agent/verify/__main__.py +41 -0
  886. mobile_agent/verify/a11y.py +189 -0
  887. mobile_agent/verify/actions.py +290 -0
  888. mobile_agent/verify/assertions.py +570 -0
  889. mobile_agent/verify/audit.py +312 -0
  890. mobile_agent/verify/business.py +221 -0
  891. mobile_agent/verify/business_run.py +131 -0
  892. mobile_agent/verify/business_state.py +168 -0
  893. mobile_agent/verify/check_schema.py +398 -0
  894. mobile_agent/verify/checks.py +614 -0
  895. mobile_agent/verify/cli.py +423 -0
  896. mobile_agent/verify/conditions.py +483 -0
  897. mobile_agent/verify/crashes.py +321 -0
  898. mobile_agent/verify/device_audit.py +246 -0
  899. mobile_agent/verify/dialogs.py +287 -0
  900. mobile_agent/verify/evidence.py +705 -0
  901. mobile_agent/verify/fixtures.py +305 -0
  902. mobile_agent/verify/flow.py +213 -0
  903. mobile_agent/verify/l10n.py +141 -0
  904. mobile_agent/verify/netcerts.py +67 -0
  905. mobile_agent/verify/netproxy.py +356 -0
  906. mobile_agent/verify/netrun.py +81 -0
  907. mobile_agent/verify/netshim.m +28 -0
  908. mobile_agent/verify/netshim.py +48 -0
  909. mobile_agent/verify/netstubs.py +132 -0
  910. mobile_agent/verify/overlay.py +174 -0
  911. mobile_agent/verify/perf.py +239 -0
  912. mobile_agent/verify/proof.py +192 -0
  913. mobile_agent/verify/replay_admission.py +48 -0
  914. mobile_agent/verify/report.py +952 -0
  915. mobile_agent/verify/run_cleanup.py +67 -0
  916. mobile_agent/verify/run_links.py +48 -0
  917. mobile_agent/verify/run_perf.py +44 -0
  918. mobile_agent/verify/run_secrets.py +39 -0
  919. mobile_agent/verify/runner.py +976 -0
  920. mobile_agent/verify/runs.py +57 -0
  921. mobile_agent/verify/screen_coverage.py +181 -0
  922. mobile_agent/verify/smart.py +106 -0
  923. mobile_agent/verify/snapshot_run.py +114 -0
  924. mobile_agent/verify/snapshots.py +306 -0
  925. mobile_agent/verify/sound.py +335 -0
  926. mobile_agent/verify/source_observation.py +130 -0
  927. mobile_agent/verify/system_surfaces/__init__.py +7 -0
  928. mobile_agent/verify/system_surfaces/play.py +106 -0
  929. mobile_agent/verify/system_surfaces/spec.py +139 -0
  930. mobile_agent/verify/system_surfaces/stage.py +138 -0
  931. mobile_agent/verify/system_surfaces/surfaces.py +121 -0
  932. mobile_agent/verify/tree.py +223 -0
  933. mobile_agent/verify/verdict.py +377 -0
  934. mobile_agent/verify/wire.py +392 -0
  935. mobile_agent/verify/wirestore.py +176 -0
  936. mobile_agent/waiting/__init__.py +1 -0
  937. mobile_agent/waiting/contacts.py +73 -0
  938. mobile_agent/waiting/drafts.py +136 -0
  939. mobile_agent/waiting/job.py +145 -0
  940. mobile_agent/waiting/messages.py +174 -0
  941. mobile_agent/waiting/rank.py +115 -0
  942. mobile_agent/waiting/register.py +26 -0
  943. mobile_agent/watch.py +502 -0
  944. mobile_agent/wda/__init__.py +2 -0
  945. mobile_agent/wda/frame_clock.py +646 -0
  946. mobile_agent/wda/frame_source.py +325 -0
  947. mobile_agent/wda/frame_store.py +115 -0
  948. mobile_agent/wda/gate.py +163 -0
  949. mobile_agent/wda/gestures.py +291 -0
  950. mobile_agent/wda/guards.py +94 -0
  951. mobile_agent/wda/identity.py +55 -0
  952. mobile_agent/wda/observe.py +884 -0
  953. mobile_agent/wda/patches.py +856 -0
  954. mobile_agent/wda/point_gestures.py +150 -0
  955. mobile_agent/wda/source.py +32 -0
  956. mobile_agent/wda/system_dialogs.py +571 -0
  957. mobile_agent/wda/text.py +694 -0
  958. mobile_agent/wda/timeouts.py +77 -0
  959. mobile_agent/wda/video.py +505 -0
  960. mobile_agent/wda/watchdog.py +243 -0
  961. mobile_agent/wireless/__init__.py +15 -0
  962. mobile_agent/wireless/api.py +354 -0
  963. mobile_agent/wireless/cli.py +287 -0
  964. mobile_agent/wireless/devicectl.py +229 -0
  965. mobile_agent/wireless/exposure.py +33 -0
  966. mobile_agent/wireless/mobster-wifi-pair.c +122 -0
  967. mobile_agent/wireless/monitor.py +332 -0
  968. mobile_agent/wireless/network.py +334 -0
  969. mobile_agent/wireless/onboarding.py +293 -0
  970. mobile_agent/wireless/pair.py +124 -0
  971. mobile_agent/wireless/probe.py +322 -0
  972. mobile_agent/wireless/register.py +8 -0
  973. mobile_agent/wireless/relay.py +179 -0
  974. mobile_agent/wireless/result.py +87 -0
  975. mobile_agent/wireless/store.py +85 -0
  976. mobile_agent/wireless/switch.py +77 -0
  977. mobile_agent/wireless/transport.py +118 -0
  978. mobile_agent/wireless/words.py +81 -0
  979. mobile_agent/wireless/xctestrun.py +83 -0
  980. mobile_agent/workflow_tools.py +59 -0
  981. mobile_agent/workflows/__init__.py +2 -0
  982. mobile_agent/workflows/brief.py +201 -0
  983. mobile_agent/workflows/cron.py +110 -0
  984. mobile_agent/workflows/health.py +257 -0
  985. mobile_agent/workflows/history.py +126 -0
  986. mobile_agent/workflows/records.py +442 -0
  987. mobile_agent/workflows/runs.py +1000 -0
  988. mobile_agent/workflows/scheduler.py +134 -0
  989. mobile_agent/workflows/service.py +973 -0
  990. mobile_agent/workflows/starters.py +285 -0
  991. mobile_agent/workflows/triggers.py +248 -0
  992. mobile_agent/xray/__init__.py +1 -0
  993. mobile_agent/xray/app_cli.py +116 -0
  994. mobile_agent/xray/cli.py +21 -0
  995. mobile_agent/xray/crawl.py +754 -0
  996. mobile_agent/xray/diff.py +171 -0
  997. mobile_agent/xray/diff_cli.py +43 -0
  998. mobile_agent/xray/job.py +184 -0
  999. mobile_agent/xray/paywall.py +188 -0
  1000. mobile_agent/xray/policy.py +305 -0
  1001. mobile_agent/xray/register.py +43 -0
  1002. mobile_agent/xray/report.py +283 -0
  1003. mobile_agent/xray/screens.py +117 -0
  1004. mobster_cli-0.4.3.dist-info/METADATA +93 -0
  1005. mobster_cli-0.4.3.dist-info/RECORD +1009 -0
  1006. mobster_cli-0.4.3.dist-info/WHEEL +5 -0
  1007. mobster_cli-0.4.3.dist-info/entry_points.txt +3 -0
  1008. mobster_cli-0.4.3.dist-info/licenses/LICENSE +21 -0
  1009. mobster_cli-0.4.3.dist-info/top_level.txt +1 -0
@@ -0,0 +1,132 @@
1
+ """The stages that refuse an action for what came before it: an unknown outcome, a loop, a request that did not ask
2
+ for it."""
3
+
4
+ import time
5
+
6
+ from ...task_policy import is_keypad_key
7
+ from ..act import DISMISS_POINTS, SUBMIT_WINDOW_SECONDS
8
+ from ..protocol import HOUSEKEEPING_OPS, TYPED_OPS
9
+ from ..safety import LOOP_REPEATS, REPEAT_ACT_AGAIN, REPEAT_ACT_FEEDBACK, guard
10
+
11
+
12
+ def unknown_outcome(agent, attempt, turn):
13
+ index = turn.index
14
+ op, app = attempt.op, attempt.app
15
+ effect = agent._effect(op, attempt.element, app, attempt.text, agent._app_name(turn.snapshot), turn.snapshot)
16
+ attempt.unknown_repeat = None # "retry" or "ask": this action repeats one whose outcome was unknown
17
+ attempt.held = agent._held(effect, turn.snapshot)
18
+ if attempt.held is not None:
19
+ # An action that may already have run is never sent again blind: a fresh read settles it first.
20
+ what = attempt.element.label[:60] if attempt.element else (app or "")
21
+ attempt.unknown_repeat, fresh, found = agent._recheck_unknown(op, attempt.element, attempt.held, what)
22
+ if attempt.unknown_repeat == "refuse":
23
+ turn.feedback = fresh
24
+ turn.history.append(f"{turn.step}.{index}: {op} {what!r} refused (its earlier outcome was unknown)")
25
+ agent._emit({"event": "frontier_refused", "step": turn.step, "label": "outcome_unknown"})
26
+ return "stop"
27
+ turn.snapshot, attempt.element = fresh, found
28
+
29
+
30
+ def loops(agent, attempt, turn):
31
+ index = turn.index
32
+ op, text, app, element = attempt.op, attempt.text, attempt.app, attempt.element
33
+ attempt.key = (op, element.label if element else app, text)
34
+ # A loop is the same control, text and screen: another value or another list row is not one.
35
+ attempt.spot = (op, (element.locator or element.label) if element else app,
36
+ text if op in TYPED_OPS or op == "SCROLL_TO" else None, turn.snapshot.content_fingerprint)
37
+ keypad = element is not None and is_keypad_key(op, element.role, element.label)
38
+ if (not op.startswith("SWIPE_") and op != "DISMISS" and not keypad
39
+ and turn.recent.count(attempt.spot) >= LOOP_REPEATS - 1):
40
+ turn.feedback = (f"Refused: this is the {LOOP_REPEATS}rd time recently you {op} "
41
+ f"{(element.label if element else app)!r}; you are going in circles. Use a different "
42
+ "route: the app's search, another tab or menu, scrolling, or skip this part and move "
43
+ "on to the next part of the request.")
44
+ turn.history.append(f"{turn.step}.{index}: {op} {(element.label[:60] if element else app)!r} refused (loop)")
45
+ agent._emit({"event": "frontier_refused", "step": turn.step, "label": "loop"})
46
+ return "stop"
47
+ if op == "DISMISS" and agent.run_state.dismissed == (turn.snapshot.content_fingerprint, len(DISMISS_POINTS) - 1):
48
+ turn.feedback = ("Refused: tapping outside was tried everywhere and closed nothing. Use the overlay's "
49
+ "own controls (Close, Cancel, Done, Back) or choose an item in it.")
50
+ turn.history.append(f"{turn.step}.{index}: DISMISS refused (tried every point)")
51
+ agent._emit({"event": "frontier_refused", "step": turn.step, "label": "dismiss_exhausted"})
52
+ return "stop"
53
+ # A repeat is the same action on the screen it left (typing) or was made on (a tap).
54
+ turn.repeats = (
55
+ turn.repeats + 1
56
+ if turn.last_done and turn.last_done[0] == attempt.key
57
+ and turn.snapshot.content_fingerprint == (turn.last_done[2] if op in TYPED_OPS else turn.last_done[1])
58
+ else 0)
59
+ if op not in HOUSEKEEPING_OPS and (op in TYPED_OPS and turn.repeats >= 1 or turn.repeats >= 2):
60
+ turn.feedback = ("Refused: you just did exactly this action. Its effect is already on screen (for "
61
+ "typed text, see the field's value). Take the next step instead, e.g. save or confirm.")
62
+ turn.history.append(f"{turn.step}.{index}: repeat of the previous action refused")
63
+ agent._emit({"event": "frontier_refused", "step": turn.step, "label": "repeat"})
64
+ return "stop"
65
+ act_key = agent._act_key(op, element, turn.snapshot)
66
+ if act_key is not None and act_key in agent.run_state.acts_done:
67
+ before_refusal = agent.run_state.act_refused.get(act_key)
68
+ known = before_refusal if before_refusal is not None else {act_key[2], agent.run_state.act_left[act_key]}
69
+ new_look = bool(agent.run_state.act_seen[act_key] - known)
70
+ else:
71
+ new_look = True
72
+ if not new_look:
73
+ # Fails closed: the repeat waits until the run has looked somewhere the act could show.
74
+ template = REPEAT_ACT_FEEDBACK if act_key not in agent.run_state.act_refused else REPEAT_ACT_AGAIN
75
+ agent.run_state.act_refused[act_key] = frozenset(agent.run_state.act_seen[act_key] | {act_key[2]})
76
+ turn.feedback = template.format(label=element.label[:60], step=agent.run_state.acts_done[act_key])
77
+ turn.history.append(f"{turn.step}.{index}: TAP {element.label[:60]!r} refused (done before on this screen)")
78
+ agent._emit({"event": "frontier_refused", "step": turn.step, "label": "repeat_act"})
79
+ return "stop"
80
+
81
+
82
+ def requested(agent, attempt, turn):
83
+ index = turn.index
84
+ element = attempt.element
85
+ refused = guard(turn.request, attempt.op, element) if agent._contract is None else None
86
+ if refused:
87
+ turn.feedback = refused
88
+ turn.history.append(f"{turn.step}.{index}: TAP {element.label[:60]!r} refused (not requested)")
89
+ agent._emit({"event": "frontier_refused", "step": turn.step, "label": element.label[:120]})
90
+ return "stop"
91
+
92
+
93
+ def refresh(agent, attempt, turn):
94
+ index = turn.index
95
+ attempt.refresh_ms = 0
96
+ if attempt.element is not None and index == 0:
97
+ # The model took seconds: act on the screen as it is now, or the driver refuses a stale read (splitpay-001).
98
+ t_refresh = time.monotonic()
99
+ fresh, reused = agent._refreshed(turn.snapshot)
100
+ turn.counts["reused"] += reused
101
+ attempt.refresh_ms = agent._spend("refresh", t_refresh)
102
+ moved = agent._find(fresh, attempt.element.label, attempt.element.role, attempt.element.locator)
103
+ if moved is not None:
104
+ turn.snapshot, attempt.element = fresh, moved
105
+
106
+
107
+ def results_still_moving(agent, attempt, turn):
108
+ index = turn.index
109
+ op = attempt.op
110
+ if (op in ("TAP", "LONG_PRESS", "TAP_XY") and agent.run_state.submitted_at is not None
111
+ and time.monotonic() - agent.run_state.submitted_at < SUBMIT_WINDOW_SECONDS):
112
+ # Results of a search just submitted may still be moving: tap only on two agreeing reads.
113
+ t_still = time.monotonic()
114
+ still, moved_to = agent._still_after_submit(attempt.element)
115
+ agent._spend("settle", t_still)
116
+ if still is None:
117
+ turn.feedback = ("The search results were still changing, so Mobster did not tap. Look at the "
118
+ "screen again and choose from what it shows now.")
119
+ turn.history.append(f"{turn.step}.{index}: {op} held: results still changing after the search")
120
+ agent._emit({"event": "frontier_chunk_stop", "step": turn.step, "index": index,
121
+ "reason": "results_moving"})
122
+ turn.snapshot, turn.unproven = agent._read_once(), False
123
+ return "stop"
124
+ turn.snapshot = still
125
+ if attempt.element is not None:
126
+ if moved_to is None:
127
+ agent._emit({"event": "frontier_chunk_stop", "step": turn.step, "index": index,
128
+ "reason": "target_missing", "wanted": (attempt.element.label or "")[:80]})
129
+ turn.history.append(f"{turn.step}.{index}: {op} target {attempt.element.label[:60]!r} gone once the "
130
+ "results settled; stopped the sequence")
131
+ return "stop"
132
+ attempt.element = moved_to
@@ -0,0 +1,155 @@
1
+ """The stages for the passcode keypad, a skill, and a point or gesture on the screen."""
2
+
3
+ import dataclasses
4
+ import json
5
+
6
+ from ...agent_hooks import RESUME_AFTER_SECONDS, GuardVerdict
7
+ from ...task_policy import springboard_keypad
8
+ from ...wda.point_gestures import GESTURE_OPS, validate_gesture_request
9
+ from ..act import LEAVING_OPS
10
+ from ..interrupts import Where, check
11
+ from ..protocol import ELEMENT_OPS
12
+ from ..safety import KEYPAD_REFUSAL, PHONE_LOCKED, SAME_FAILURES_TO_CHECK, GuardStop, declined_result
13
+
14
+
15
+ def keypad(agent, attempt, turn):
16
+ index = turn.index
17
+ op = attempt.op
18
+ if op in ELEMENT_OPS or op == "TAP_XY" or op in GESTURE_OPS or op in agent._skill_ops:
19
+ if springboard_keypad(turn.snapshot):
20
+ # The lock screen's keypad is never the model's (B2): the guard may unlock or stop.
21
+ turn.keypad_refusals += 1
22
+ turn.feedback = KEYPAD_REFUSAL
23
+ turn.history.append(f"{turn.step}.{index}: {op} refused (passcode screen)")
24
+ agent._emit({"event": "frontier_refused", "step": turn.step, "label": "passcode_keypad"})
25
+ verdict = agent._guard_check("lock_suspected", turn.history)
26
+ if verdict is not None and verdict.state == "unlocked":
27
+ turn.keypad_refusals, turn.snapshot, turn.unproven = 0, agent._observe(), False
28
+ elif agent.guard is None or turn.keypad_refusals >= SAME_FAILURES_TO_CHECK:
29
+ raise GuardStop(GuardVerdict("stop", "phone_locked", PHONE_LOCKED + (
30
+ "" if turn.actions else " No action was taken.")))
31
+ return "stop"
32
+
33
+
34
+ def skill(agent, attempt, turn):
35
+ index = turn.index
36
+ if attempt.op in LEAVING_OPS:
37
+ agent._reveal_found(turn)
38
+ op = attempt.op
39
+ chosen = agent._skill_ops.get(op)
40
+ if chosen is not None and op == "OPEN_URL" and attempt.act.app and attempt.act.app not in agent.apps:
41
+ # A web page opens in a listed app only, else in the browser on screen or the default one.
42
+ attempt.act = dataclasses.replace(attempt.act, app=None)
43
+ if chosen is not None:
44
+ if turn.session is not None:
45
+ turn.session.mark_action()
46
+ ended, said_back, turn.snapshot, changed, waited = agent._run_skill(
47
+ chosen, attempt.act, index, turn.aliases, turn.snapshot, turn.request, turn.step, turn.history, turn.after)
48
+ turn.started += waited
49
+ agent._waited(waited)
50
+ turn.unproven = False
51
+ if ended is None and turn.snapshot is not None:
52
+ # A link can land in any app: the app in front is checked before anything there is read or done.
53
+ agent._check_app(turn.snapshot)
54
+ if ended is not None and "redirect" in ended:
55
+ instruction = ended["redirect"]
56
+ turn.request = f"{turn.request}\n\nThe user's later instruction: {instruction}"
57
+ turn.feedback = f"The user declined {op} and asked instead: {instruction!r}. Do that now."
58
+ turn.history.append(f"{turn.step}.{index}: {op} declined; user redirected")
59
+ return "stop"
60
+ if ended is not None:
61
+ turn.result = ended
62
+ return "end"
63
+ turn.feedback = said_back or ""
64
+ if changed:
65
+ turn.actions += 1
66
+ agent._actions = turn.actions
67
+ turn.acted = True
68
+ return "stop" # a skill's result is looked at before anything else is done
69
+
70
+
71
+ def gesture_points(agent, attempt, turn):
72
+ index = turn.index
73
+ op = attempt.op
74
+ if op in GESTURE_OPS:
75
+ # One gesture, on the screen and picture the model chose its points from, with nothing chained after it.
76
+ try:
77
+ attempt.point = validate_gesture_request(GESTURE_OPS[op], json.loads(attempt.text or ""))
78
+ turn.snapshot = agent.gesture_screen(turn.snapshot, turn.image, attempt.point, turn.request)
79
+ except (TypeError, ValueError) as error:
80
+ if check(agent, turn, Where.WAITED) is not None:
81
+ return "end"
82
+ turn.feedback = str(error)
83
+ turn.history.append(f"{turn.step}.{index}: {op} refused")
84
+ agent._emit({"event": "frontier_refused", "step": turn.step, "label": op.lower(),
85
+ "detail": turn.feedback[:160]})
86
+ return "stop"
87
+ del turn.chunk[index + 1:]
88
+
89
+
90
+ def point_approval(agent, attempt, turn):
91
+ index = turn.index
92
+ op = attempt.op
93
+ if op == "TAP_XY" or op in GESTURE_OPS:
94
+ if op == "TAP_XY":
95
+ if agent._image_mode == "tree" and turn.image is None:
96
+ attempt.point, why = None, "Read a screenshot before choosing screen points."
97
+ else:
98
+ attempt.point, why = agent._tap_xy(turn.request, attempt.act, turn.aliases, turn.snapshot)
99
+ if why:
100
+ turn.feedback = why
101
+ turn.history.append(f"{turn.step}.{index}: TAP_XY {(attempt.text or '')[:40]!r} refused")
102
+ agent._emit({"event": "frontier_refused", "step": turn.step, "label": "tap_xy",
103
+ "detail": why[:160]})
104
+ return "stop"
105
+ named = " ".join((attempt.text or "").split())[:40]
106
+ attempt.app = f"{named} at {attempt.point[0] * 100:.0f},{attempt.point[1] * 100:.0f}"
107
+ else:
108
+ named = attempt.app = attempt.point.label
109
+ attempt.text = None
110
+ verdict, waited, asked = agent._point_approval(
111
+ attempt.point.start if op in GESTURE_OPS else attempt.point, turn.snapshot, turn.request, turn.chain_typed,
112
+ turn.step, turn.session, gesture=attempt.point if op in GESTURE_OPS else None)
113
+ turn.started += waited # waiting for the user never spends the task's time
114
+ agent._waited(waited)
115
+ if verdict is None and asked and check(agent, turn, Where.WAITED) is not None:
116
+ return "end"
117
+ if verdict is None and asked:
118
+ # The person approved the spot as the screen was: one that moved since is not tapped.
119
+ if waited >= RESUME_AFTER_SECONDS:
120
+ agent._guard_check("resume", turn.history)
121
+ if op in GESTURE_OPS:
122
+ try:
123
+ fresh = agent.gesture_screen(turn.snapshot, turn.image, attempt.point, turn.request)
124
+ matched = True
125
+ except ValueError:
126
+ fresh, matched = turn.snapshot, False
127
+ else:
128
+ fresh = agent._observe()
129
+ matched = fresh.content_fingerprint == turn.snapshot.content_fingerprint
130
+ if check(agent, turn, Where.WAITED) is not None:
131
+ return "end"
132
+ if not matched:
133
+ turn.feedback = "The screen changed while Mobster waited for your OK: look again."
134
+ turn.history.append(f"{turn.step}.{index}: {op} {named!r} not "
135
+ + ("tapped" if op == "TAP_XY" else "done") + " (the screen changed)")
136
+ turn.snapshot = fresh
137
+ return "stop"
138
+ turn.snapshot = fresh
139
+ if verdict is not None and verdict.startswith(("refused:", "blocked:")):
140
+ turn.feedback = (f"Refused: {verdict.split(':', 1)[1]}, or finish with what you found (BLOCKED if "
141
+ "the request needs this).")
142
+ turn.history.append(f"{turn.step}.{index}: TAP_XY {named!r} refused (needs the user's OK)")
143
+ agent._emit({"event": "frontier_refused", "step": turn.step, "label": "needs_approval"})
144
+ return "stop"
145
+ if verdict is not None and verdict.startswith("redirected:"):
146
+ agent.run_state.asked = "redirected"
147
+ instruction = " ".join(verdict.split(":", 1)[1].split())[:500]
148
+ turn.request = f"{turn.request}\n\nThe user's later instruction: {instruction}"
149
+ turn.feedback = (f"The user declined the tap at that spot and asked instead: {instruction!r}. "
150
+ "Do that now.")
151
+ turn.history.append(f"{turn.step}.{index}: TAP_XY {named!r} declined; user redirected")
152
+ return "stop"
153
+ if verdict is not None:
154
+ turn.result = declined_result(verdict)
155
+ return "end"
@@ -0,0 +1,177 @@
1
+ """The stages that send an action to the phone and answer its failure."""
2
+
3
+ import time
4
+
5
+ from ... import contract_asks as proof_asks
6
+ from ...model_clients import http_detail
7
+ from ...wda.guards import DriverRejection
8
+ from ...wda.point_gestures import GESTURE_OPS
9
+ from ..interrupts import Where, check
10
+ from ..protocol import ELEMENT_OPS, TYPED_OPS
11
+ from ..safety import (
12
+ CONNECTION_LOST,
13
+ NOTHING_SENT,
14
+ SAME_FAILURES_TO_CHECK,
15
+ SAME_FAILURES_TO_STOP,
16
+ UNKNOWN_REASON,
17
+ GuardStop,
18
+ consequential,
19
+ failure_feedback,
20
+ outcome_unknown,
21
+ stuck_sentence,
22
+ unknown_advice,
23
+ )
24
+ from ..screen import names_act, reads_back
25
+
26
+
27
+ def execute(agent, attempt, turn):
28
+ index = turn.index
29
+ op, text, app, point, held, unknown_repeat, gate = (
30
+ attempt.op, attempt.text, attempt.app, attempt.point, attempt.held, attempt.unknown_repeat, attempt.gate)
31
+ attempt.before = turn.snapshot
32
+ attempt.effect = agent._effect(op, attempt.element, app, text, agent._app_name(turn.snapshot), turn.snapshot)
33
+ attempt.before_unknown = (
34
+ agent._before_unknown(attempt.element, turn.snapshot) if attempt.effect is not None else None)
35
+ if unknown_repeat == "retry":
36
+ # A fresh read showed the earlier attempt did nothing: this is its one more attempt.
37
+ agent.run_state.effects.settle(held[0], None, effect=held[1], happened=False)
38
+ agent._emit({"event": "frontier_retry", "step": turn.step, "index": index, "operation": op,
39
+ "reason": "outcome_unknown_unchanged"})
40
+ attempt.t_exec = time.monotonic()
41
+ agent.run_state.last_receipt = None
42
+ if turn.session is not None:
43
+ turn.session.mark_action()
44
+ if op in GESTURE_OPS:
45
+ # Journaled before it is sent: after a restart the gesture counts as one that may have run.
46
+ if check(agent, turn, Where.WAITED) is not None:
47
+ return "end"
48
+ agent._emit({"event": "action_started", "operation": op, "target": app})
49
+ if check(agent, turn, Where.WAITED) is not None:
50
+ return "end"
51
+ try:
52
+ try:
53
+ attempt.before = agent._execute(op, attempt.element, app, text, turn.snapshot, point)
54
+ except Exception as error:
55
+ # Resent only when the request never ran (refused, a dropped session); never a gesture or a point.
56
+ if op in GESTURE_OPS or op == "TAP_XY" or not agent._recover(error) or not any(
57
+ mark in str(error) for mark in NOTHING_SENT):
58
+ raise
59
+ turn.snapshot = agent._observe()
60
+ if attempt.element is not None:
61
+ attempt.element = agent._find(
62
+ turn.snapshot, attempt.element.label, attempt.element.role, attempt.element.locator)
63
+ if attempt.element is None:
64
+ raise
65
+ attempt.before = agent._execute(op, attempt.element, app, text, turn.snapshot, point)
66
+ if op in GESTURE_OPS:
67
+ agent._emit({"event": "action_acknowledged", "step": turn.step, "operation": op})
68
+ turn.actions += 1
69
+ agent._actions = turn.actions
70
+ if attempt.commit_now:
71
+ agent.run_state.commits += 1
72
+ if unknown_repeat is not None:
73
+ agent.run_state.effects.resolve(held[0], None, effect=held[1])
74
+ turn.acted_since = turn.acted_since or names_act(op, attempt.element, text)
75
+ if attempt.element is not None:
76
+ turn.touched.append(attempt.element)
77
+ if op == "READ_LIST" and agent.run_state.listing:
78
+ turn.read_rows = list(agent.run_state.listing)
79
+ agent.run_state.submitted_at = time.monotonic() if op in ("TYPE_SUBMIT", "SUBMIT") else (
80
+ agent.run_state.submitted_at if op not in ("TAP", "LONG_PRESS", "TAP_XY") else None)
81
+ if turn.session is not None:
82
+ # Opening the app already in front is no progress (P1-1).
83
+ turn.session.on_acted(noop=op == "LAUNCH_APP" and app == getattr(turn.snapshot, "bundle_id", None))
84
+ if agent._contract is not None:
85
+ where = agent._app_name(attempt.before)
86
+ agent._contract.record_action(where, op, getattr(attempt.element, "label", "") or "")
87
+ if op in TYPED_OPS and text:
88
+ agent._contract.record_typed(where, attempt.element, text, turn.step)
89
+ turn.chain_typed.append(text)
90
+ if gate is not None and gate.item is not None:
91
+ agent._contract.committed(gate, attempt.element, attempt.before, where, turn.step)
92
+ if op == "READ_LIST" and agent.run_state.listing:
93
+ agent._contract.saw_lines(where, turn.step, agent.run_state.listing)
94
+ turn.acted = turn.acted or op in ELEMENT_OPS or op == "TAP_XY" or op in GESTURE_OPS
95
+ if agent.run_state.macro_feedback:
96
+ turn.feedback, agent.run_state.macro_feedback = agent.run_state.macro_feedback, None
97
+ except GuardStop:
98
+ raise
99
+ except Exception as error:
100
+ attempt.error = error
101
+
102
+
103
+ def failure(agent, attempt, turn):
104
+ error = attempt.error
105
+ if error is None:
106
+ return None
107
+ index = turn.index
108
+ op, text, app, element, gate, before_unknown, effect = (
109
+ attempt.op, attempt.text, attempt.app, attempt.element, attempt.gate, attempt.before_unknown, attempt.effect)
110
+ if op in GESTURE_OPS and not (isinstance(error, DriverRejection) and error.code in error.PRE_DISPATCH):
111
+ kind = GESTURE_OPS[op]
112
+ turn.result = {"status": "blocked", "answer": None, "code": "unknown_outcome",
113
+ "reason": f"The {kind} outcome is unknown. Inspect the phone before starting another "
114
+ "task; it was not retried."}
115
+ agent._emit({"event": "frontier_failed", "step": turn.step, "index": index, "operation": op,
116
+ "error": f"{kind} outcome unknown", "outcome": "unknown"})
117
+ return "end"
118
+ # A request on a dropped session never ran: its outcome is known.
119
+ lost = agent.run_state.recoveries and any(name in str(error) for name in CONNECTION_LOST if name != "StaleSession")
120
+ # A tap at a point has no control to read back: any failure past dispatch is held as unknown.
121
+ unknown = bool(lost) or outcome_unknown(error, op) or (
122
+ op == "TAP_XY" and not (isinstance(error, DriverRejection) and error.code in error.PRE_DISPATCH))
123
+ if unknown and effect is not None:
124
+ agent.run_state.effects.record_outcome(effect[0], None, outcome="unknown", effect=effect[1],
125
+ context=before_unknown)
126
+ if unknown and gate is not None and gate.item is not None and proof_asks.asks(gate.item):
127
+ # Any control of this act, on any screen, now asks with the warning, in every mode.
128
+ agent.run_state.held_items[gate.item.id] = UNKNOWN_REASON
129
+ turn.feedback = (("The connection to the phone dropped during this action and was restored: it may or "
130
+ "may not have happened. " + unknown_advice(op)) if lost
131
+ else failure_feedback(op, element, text, error))
132
+ detail = http_detail(error)
133
+ what = element.label[:60] if element else (app or "")
134
+ # The line names the target and the status: a bare one repeated a failed launch fifty times (B2).
135
+ turn.history.append(f"{turn.step}.{index}: {op} {what!r} failed ({detail})")
136
+ agent._spend("exec", attempt.t_exec)
137
+ agent._emit({"event": "frontier_failed", "step": turn.step, "index": index, "operation": op,
138
+ "error": f"{type(error).__name__}: {str(error)[:160]}",
139
+ "outcome": "unknown" if unknown else "failed",
140
+ # A picked-up run holds this act too (checkpoints.held_acts).
141
+ **({"held_act": gate.item.act} if unknown and gate is not None and gate.item is not None
142
+ and proof_asks.asks(gate.item) else {})})
143
+ t_observe = time.monotonic()
144
+ turn.snapshot, turn.unproven = agent._observe(), False
145
+ agent._spend("observe", t_observe)
146
+ if unknown and effect is not None and element is not None:
147
+ # The look after it: a screen that moved on, or a control that changed, says it happened.
148
+ if not agent._unchanged(before_unknown, element, turn.snapshot)[0]:
149
+ agent.run_state.effects.settle(effect[0], None, effect=effect[1], happened=True)
150
+ turn.feedback += (" The screen changed after it, so Mobster counts it as done and won't send it "
151
+ "again.")
152
+ elif consequential(op, element, turn.snapshot):
153
+ turn.feedback += " The screen looks unchanged; sending it again needs the user's OK."
154
+ else:
155
+ turn.feedback += (" The screen looks unchanged, so it probably didn't happen: if it still looks "
156
+ "the same, you may send it once more.")
157
+ if op in TYPED_OPS and text and element is not None and agent._contract is not None:
158
+ # Typing that timed out may have landed: a field reading it back holds the agent's text.
159
+ field = agent._find(turn.snapshot, element.label, element.role, element.locator)
160
+ if field is not None and field.editable and reads_back(field.value or "", text):
161
+ agent._contract.record_typed(agent._app_name(turn.snapshot), field, text, turn.step)
162
+ turn.feedback += " The field now reads back exactly the text you wrote: don't type it again."
163
+ same = (op, (element.locator or element.label) if element else app, text)
164
+ turn.failures[same] = turn.failures.get(same, 0) + 1
165
+ if op == "LAUNCH_APP" and detail == "HTTP 400" or turn.failures[same] >= SAME_FAILURES_TO_CHECK:
166
+ # iOS refuses to bring an app forward over the lock screen or a passcode sheet.
167
+ verdict = agent._guard_check("launch_failed" if op == "LAUNCH_APP" else "lock_suspected",
168
+ turn.history)
169
+ if verdict is not None and verdict.state == "unlocked":
170
+ turn.failures.clear()
171
+ turn.snapshot = agent._observe()
172
+ if turn.failures.get(same, 0) >= SAME_FAILURES_TO_STOP:
173
+ name = agent.apps.get(app, app) if op == "LAUNCH_APP" else what
174
+ sentence = stuck_sentence(op, name, detail, turn.actions)
175
+ turn.result = {"status": "blocked", "answer": None, "reason": sentence}
176
+ return "end"
177
+ return "stop"
@@ -0,0 +1,151 @@
1
+ """The two records a run keeps: Turn, what the turn loop and its actions share, and RunState, what the run has done."""
2
+
3
+ import dataclasses
4
+ import threading
5
+ from collections import deque
6
+ from typing import Any
7
+
8
+ from ..effect_ledger import EffectLedger
9
+ from ..harness_api import InitialState
10
+ from ..state import Element, Snapshot
11
+ from .actions import Action
12
+ from .screen import Provenance, _Verify
13
+
14
+
15
+ # The turn loop's locals that the per-action step shares; slots turn a misspelt name into an AttributeError.
16
+ @dataclasses.dataclass(slots=True, kw_only=True)
17
+ class Turn:
18
+ request: str
19
+ started: float
20
+ recent: deque
21
+ session: Any # a longform session; the engine does not import longform
22
+ data_json: bool
23
+ stopped: dict
24
+ acted: bool = False
25
+ acted_since: bool = False
26
+ actions: int = 0
27
+ after: str | None = None
28
+ aliases: dict[str, Element] = dataclasses.field(default_factory=dict)
29
+ calls: int = 0
30
+ chain_typed: list[str] = dataclasses.field(default_factory=list)
31
+ checked_front: bool = False
32
+ chunk: list[Action] = dataclasses.field(default_factory=list)
33
+ counts: dict[str, int] = dataclasses.field(
34
+ default_factory=lambda: {"reused": 0, "verified": 0, "verify_changed": 0})
35
+ covered: int = 0
36
+ failures: dict[tuple, int] = dataclasses.field(default_factory=dict)
37
+ feedback: str = ""
38
+ finished: bool = False
39
+ history: list[str] = dataclasses.field(default_factory=list)
40
+ idle_waits: int = 0
41
+ image: dict | None = None
42
+ index: int = 0
43
+ keypad_refusals: int = 0
44
+ last_done: tuple | None = None
45
+ out: dict = dataclasses.field(default_factory=dict)
46
+ read_rows: list[str] | None = None
47
+ refusals: dict[str, int] = dataclasses.field(default_factory=dict)
48
+ repeats: int = 0
49
+ request_checked: bool = False
50
+ reserve: float = 0.0
51
+ result: dict = dataclasses.field(default_factory=lambda: {"status": "max_steps", "answer": None})
52
+ snapshot: Snapshot | None = None
53
+ spent: float | None = None
54
+ step: int = 0
55
+ touched: list[Element] = dataclasses.field(default_factory=list)
56
+ turn_rows: set[str] | None = None
57
+ turn_screen: Snapshot | None = None
58
+ unproven: bool = False
59
+ untaken: tuple[str, str] | None = None
60
+ plan: str = ""
61
+ notes: list[str] = dataclasses.field(default_factory=list)
62
+ max_actions: int = 0
63
+ source_reads: tuple = (None, None)
64
+ compiled: list = dataclasses.field(default_factory=list)
65
+ worker: threading.Thread | None = None
66
+ pending: bool = False
67
+ unpriced: str = ""
68
+ unrunnable: int = 0
69
+ final: str | None = None
70
+ capped: bool = False
71
+ operations: tuple[str, ...] = ()
72
+ lines: list[str] = dataclasses.field(default_factory=list)
73
+ prep_ms: int = 0
74
+ messages: list | None = None
75
+ verify: _Verify | None = None
76
+ programmed: bool = False
77
+ proposed: list[Action] = dataclasses.field(default_factory=list)
78
+ unrun: str | None = None
79
+ usage: dict = dataclasses.field(default_factory=dict)
80
+ latency_ms: int = 0
81
+
82
+
83
+ # What one run has done so far; run() starts a new one, so nothing carries over from an earlier run.
84
+ @dataclasses.dataclass(slots=True, kw_only=True)
85
+ class RunState:
86
+ started: float
87
+ initial: InitialState | None = None
88
+ held_acts: frozenset = frozenset()
89
+ notes: list[str] = dataclasses.field(default_factory=list)
90
+ plan: str = ""
91
+ compile_worker: threading.Thread | None = None
92
+ calls: int = 0
93
+ timing: dict[str, float] = dataclasses.field(default_factory=dict)
94
+ cost_offset: float = 0.0
95
+ recovery_spent: float = 0.0
96
+ stall_spent: float = 0.0
97
+ unlocks: int = 0
98
+ secrets: set[str] = dataclasses.field(default_factory=set)
99
+ secret_rects: list[tuple] = dataclasses.field(default_factory=list)
100
+ skill_images: list[tuple] = dataclasses.field(default_factory=list)
101
+ imaged_app: str | None = None
102
+ blind: int = 0
103
+ image_why: str = "off"
104
+ cards: int = 0
105
+ commits: int = 0
106
+ held_items: dict[str, str] = dataclasses.field(default_factory=dict)
107
+ injected: bool = False
108
+ ask_reason: str | None = None
109
+ acting_as: str | None = None
110
+ media_checked: bool | None = None # a commit_check preview's verdict on the commit's screen; None: none asked
111
+ approved_send: dict | None = None
112
+ declined_on: Snapshot | None = None
113
+ revealed: int | None = None # the turn whose recorded values were checked for a hidden row (_reveal_found)
114
+ redirect: tuple[str, str] | None = None
115
+ part: Any = None # a learned.parts.PartRun; the engine does not import learned
116
+ dialog_checked: tuple | None = None
117
+ battery_pause: bool = False
118
+ submitted_at: float | None = None
119
+ dismissed: tuple = (None, 0)
120
+ receipts: dict = dataclasses.field(default_factory=dict)
121
+ recoveries: int = 0
122
+ last_receipt: dict | None = None
123
+ acts_done: dict = dataclasses.field(default_factory=dict)
124
+ act_left: dict = dataclasses.field(default_factory=dict)
125
+ act_seen: dict = dataclasses.field(default_factory=dict)
126
+ act_refused: dict = dataclasses.field(default_factory=dict)
127
+ effects: EffectLedger = dataclasses.field(default_factory=EffectLedger)
128
+ timed_out_reads: int = 0
129
+ relaunches: int = 0
130
+ read_bundle: str = ""
131
+ start_resets: dict = dataclasses.field(default_factory=dict)
132
+ fresh_launch: tuple | None = None
133
+ listing: list | None = None
134
+ macro_feedback: str | None = None
135
+ list_read: tuple | None = None
136
+ passed: list[str] = dataclasses.field(default_factory=list)
137
+ turn_extra: str = ""
138
+ steered: list = dataclasses.field(default_factory=list)
139
+ asked: str | None = None
140
+ turn_log: list[str] = dataclasses.field(default_factory=list)
141
+ turn_notes: dict[int, list[str]] = dataclasses.field(default_factory=dict)
142
+ turn_marks: dict[int, list[str]] = dataclasses.field(default_factory=dict)
143
+ turn_plans: dict[int, str] = dataclasses.field(default_factory=dict)
144
+ turn_reads: dict[int, str] = dataclasses.field(default_factory=dict)
145
+ notes_before: list[str] = dataclasses.field(default_factory=list)
146
+ provenance: Provenance | None = None
147
+ readback: bool = False
148
+ kept: bool = False
149
+ lists: dict = dataclasses.field(default_factory=dict)
150
+ system: str = ""
151
+ stable_extra: str = ""