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,546 @@
1
+ """Parts of saved programs, switched on by MOBSTER_PROGRAMS=on alone (`parts_on`)."""
2
+
3
+ import json
4
+ import os
5
+ import re
6
+ import time
7
+
8
+ from ..replay_core.path import TEXT_OPS
9
+ from .programs import BLANK, FREE_FIELDS, TRUSTED, Matched, crosses, filled_steps, fits, folded, holds_secret
10
+
11
+ # On only with MOBSTER_PROGRAMS=on set explicitly, until it is measured on a phone.
12
+
13
+ MAX_PART_RUNS = 6
14
+ ANCHOR, MAX_VALUE_TOKENS, MIN_VALUE = 2, 12, 2
15
+ EXIT_WAIT, EXIT_POLL, EXIT_SHOWN = 2.0, .25, .25
16
+ FAMILY = {"READ": "look", "REPORT": "look", "WRITE": "write", "COMMIT": "commit"}
17
+ # Controls whose tap sets a value rather than going somewhere: a part never holds one.
18
+ VALUE_ROLES = frozenset({"Switch", "Toggle", "Slider", "Stepper", "PickerWheel", "Picker", "SegmentedControl",
19
+ "Adjustable", "CheckBox", "RadioButton"})
20
+ TOKEN = re.compile(r"\w+(?:['’]\w+)*|[^\w\s]")
21
+ START, END, HERE, OTHER = "^", "|", "zzherezz", "zzotherzz"
22
+
23
+
24
+ def parts_on(env=None):
25
+ env = os.environ if env is None else env
26
+ return (env.get("MOBSTER_PROGRAMS") or "").strip().casefold() == "on"
27
+
28
+
29
+ def tokens(text):
30
+ text, out = text or "", []
31
+ for found in TOKEN.finditer(text):
32
+ token, after = folded(found.group(0)) or found.group(0), text[found.end():found.end() + 1]
33
+ out.append((token, found.start(), found.end(),
34
+ token in ",;:?!" or token == "." and (not after or after.isspace())))
35
+ return out
36
+
37
+
38
+ def cue(text, name):
39
+ toks = tokens(BLANK.sub(lambda found: HERE if found.group(1) == name else OTHER, text or ""))
40
+ at = next((index for index, token in enumerate(toks) if token[0] == HERE), None)
41
+ if at is None:
42
+ return None
43
+ left, right = [], []
44
+ for index in range(at - 1, at - 1 - ANCHOR, -1):
45
+ if index < 0 or toks[index][3]:
46
+ left.insert(0, START if index < 0 else END)
47
+ break
48
+ left.insert(0, toks[index][0])
49
+ for index in range(at + 1, at + 1 + ANCHOR):
50
+ if index >= len(toks) or toks[index][3]:
51
+ right.append(END)
52
+ break
53
+ right.append(toks[index][0])
54
+ return left, right
55
+
56
+
57
+ def _at(toks, index, mark):
58
+ if mark == START:
59
+ return index == -1
60
+ if mark == END:
61
+ return index == len(toks) or 0 <= index < len(toks) and toks[index][3]
62
+ return 0 <= index < len(toks) and toks[index][0] == mark
63
+
64
+
65
+ def proposals(text, anchors):
66
+ toks, (left, right) = tokens(text), anchors
67
+ for weight, lefts, rights in ((3, left, right), (2, left, right[:1]), (2, left[-1:], right),
68
+ (1, left[-1:], right[:1])):
69
+ if not any(mark not in (START, END) and re.match(r"\w", mark) for mark in lefts + rights) \
70
+ and not (weight == 3 and lefts == [START] and rights == [END]):
71
+ continue
72
+ out = []
73
+ for start in range(len(toks)):
74
+ if not all(_at(toks, start - len(lefts) + offset, mark) for offset, mark in enumerate(lefts)):
75
+ continue
76
+ for stop in range(start + 1, min(len(toks), start + MAX_VALUE_TOKENS) + 1):
77
+ if toks[stop - 1][3] or stop - 1 > start and toks[stop - 1][0] in ("'", '"'):
78
+ break
79
+ if all(_at(toks, stop + offset, mark) for offset, mark in enumerate(rights)):
80
+ value = text[toks[start][1]:toks[stop - 1][2]].strip(" '\"")
81
+ if len(value) >= MIN_VALUE:
82
+ out.append((value, weight))
83
+ break
84
+ if out:
85
+ return out
86
+ return []
87
+
88
+
89
+ def _starts_or_ends(short, long):
90
+ return len(short) <= len(long) and (long[:len(short)] == short or long[len(long) - len(short):] == short)
91
+
92
+
93
+ def _seq(text):
94
+ return [token[0] for token in tokens(text)]
95
+
96
+
97
+ def agreed(found):
98
+ """The value proposals agree on, or None."""
99
+ if not found:
100
+ return None
101
+ seqs = {folded(value): (value, _seq(value)) for value, _ in reversed(found)}
102
+ support = {key: sum(weight for value, weight in found if _starts_or_ends(seq, seqs[folded(value)][1]))
103
+ for key, (_, seq) in seqs.items()}
104
+ best = max(support, key=lambda key: (support[key], -len(seqs[key][1])))
105
+ rivals = [support[key] for key in support if key != best and not _starts_or_ends(seqs[best][1], seqs[key][1])
106
+ and not _starts_or_ends(seqs[key][1], seqs[best][1])]
107
+ return seqs[best][0] if support[best] >= 2 and not any(rival >= support[best] for rival in rivals) else None
108
+
109
+
110
+ def _words(what):
111
+ return {token[0] for token in tokens(BLANK.sub(" ", what or "")) if not token[3]}
112
+
113
+
114
+ def _about(taught, what):
115
+ """Whether an item's ``what`` is about what the program's item was: two thirds of its words (blanks aside)."""
116
+ want = _words(taught)
117
+ return not want or len(want & _words(what)) / len(want) >= 2 / 3
118
+
119
+
120
+ def _bundle_of(app, apps):
121
+ want = folded(app)
122
+ return next((bundle for bundle, name in (apps or {}).items() if want and want in (folded(bundle), folded(name))),
123
+ None)
124
+
125
+
126
+ def _blank(step):
127
+ return step.get("param") or step.get("label_param") or step.get("label_prefix")
128
+
129
+
130
+ def _writes(step):
131
+ from ..contract_screen import ACT_LABEL
132
+ if step.get("commit"):
133
+ return True
134
+ if step.get("op") in TEXT_OPS:
135
+ return not step.get("search")
136
+ return step.get("op") == "TAP" and bool(ACT_LABEL.search((step.get("target") or {}).get("label") or ""))
137
+
138
+
139
+ def _cut(step):
140
+ """A step no part holds: Home, a long press, a keypad key or a value control (never carried into another task)."""
141
+ from ..task_policy import is_keypad_key
142
+ target = step.get("target") or {}
143
+ return (step.get("op") in ("HOME", "LONG_PRESS") or target.get("role") in VALUE_ROLES
144
+ or is_keypad_key("TAP", target.get("role"), target.get("label"))
145
+ or step.get("op") != "LAUNCH_APP" and not (step.get("screen") or {}).get("shape"))
146
+
147
+
148
+ def _same_screen(before, after):
149
+ a, b = (before.get("screen") or {}), (after.get("screen") or {})
150
+ if a.get("hash") and a.get("hash") == b.get("hash"):
151
+ return True
152
+ ours, theirs = set(a.get("shape") or ()), set(b.get("shape") or ())
153
+ return bool(ours | theirs) and len(ours & theirs) / len(ours | theirs) >= .8
154
+
155
+
156
+ def parts_of(path):
157
+ steps, pieces, current = path.get("steps") or [], [], []
158
+ for index, step in enumerate(steps):
159
+ if _cut(step):
160
+ pieces, current = pieces + ([current] if current else []), []
161
+ continue
162
+ before = steps[current[-1]] if current else None
163
+ if step.get("op") == "LAUNCH_APP" or before is not None and step.get("app") != before.get("app") \
164
+ and before.get("op") != "LAUNCH_APP":
165
+ pieces, current = pieces + ([current] if current else []), []
166
+ current.append(index)
167
+ pieces += [current] if current else []
168
+ out = []
169
+ for piece in pieces:
170
+ reach = {}
171
+ for position, index in enumerate(piece):
172
+ if _blank(steps[index]):
173
+ reach[_blank(steps[index])] = (reach.get(_blank(steps[index]), (position, position))[0], position)
174
+ merged = []
175
+ for first, last in sorted(reach.values()):
176
+ if merged and first <= merged[-1][1]:
177
+ merged[-1][1] = max(merged[-1][1], last)
178
+ else:
179
+ merged.append([first, last])
180
+ if not merged:
181
+ out.append((piece[0], piece[-1]))
182
+ continue
183
+ merged[0][0], merged[-1][1] = 0, len(piece) - 1
184
+ for number in range(len(merged) - 1):
185
+ last, following = merged[number][1], merged[number + 1][0]
186
+ if following > last + 1 and steps[piece[last]].get("op") in TEXT_OPS:
187
+ merged[number][1] = last + 1
188
+ merged[number + 1][0] = merged[number][1] + 1
189
+ spans = []
190
+ for first, last in merged:
191
+ before = steps[piece[spans[-1][1]]] if spans else None
192
+ if first > last:
193
+ continue
194
+ if before is not None and (before.get("op") in TEXT_OPS and not (before.get("op") == "TYPE_SUBMIT"
195
+ and before.get("search"))
196
+ or _same_screen(before, steps[piece[first]])):
197
+ spans[-1][1] = last
198
+ else:
199
+ spans.append([first, last])
200
+ out += [(piece[first], piece[last]) for first, last in spans]
201
+ if len(spans) > 1:
202
+ out.append((piece[0], piece[-1]))
203
+ return list(dict.fromkeys(out))
204
+
205
+
206
+ def part_info(path, start, end):
207
+ steps = (path.get("steps") or [])[start:end + 1]
208
+ contract = [entry for entry in path.get("contract") or () if isinstance(entry, dict)]
209
+ blanks = list(dict.fromkeys(_blank(step) for step in steps if _blank(step)))
210
+ serves = [entry for entry in contract if any("{" + name + "}" in " ".join(str(entry.get(field) or "")
211
+ for field in FREE_FIELDS)
212
+ for name in blanks)] if blanks else list(contract)
213
+ for position, step in enumerate(steps):
214
+ if step.get("commit") and not (step.get("op") in ("TYPE_SUBMIT", "SUBMIT") and step.get("search")):
215
+ if not any(entry.get("kind") == "COMMIT" and entry.get("act") == step["commit"] for entry in serves):
216
+ steps, end = steps[:position], start + position - 1 # a commit only for a commit its run declared
217
+ break
218
+ if not serves or not steps or len(steps) < 2 and not any(step.get("op") in TEXT_OPS for step in steps):
219
+ return None
220
+ following = (path.get("steps") or [])[end + 1] if end + 1 < len(path.get("steps") or []) else None
221
+ screen = (following or {}).get("screen") or (path.get("final") if following is None else None) or {}
222
+ app = next((step.get("app") for step in steps if step.get("op") != "LAUNCH_APP"), steps[0].get("app"))
223
+ import hashlib
224
+ key = hashlib.sha1(json.dumps([[step.get("op"), (step.get("target") or {}).get("role"), _blank(step)
225
+ or (step.get("target") or {}).get("label")] for step in steps]).encode()
226
+ ).hexdigest()[:12]
227
+ return {"start": start, "end": end, "app": app, "blanks": blanks, "serves": serves, "key": key,
228
+ "writes": any(_writes(step) for step in steps), "launch": steps[0].get("op") == "LAUNCH_APP",
229
+ "commit": next((step.get("commit") for step in steps if step.get("commit") and not step.get("search")),
230
+ None),
231
+ "exit": {"app": (following or {}).get("app") if following and following.get("op") != "LAUNCH_APP"
232
+ else steps[-1].get("app"), "hash": screen.get("hash"), "shape": screen.get("shape"),
233
+ "shows": [name for name in (following or path.get("final") or {}).get("shows") or ()
234
+ if name in blanks]}}
235
+
236
+
237
+ def exit_holds(exit_, values, snapshot):
238
+ from ..replay_core.engine import WEAK
239
+ from ..replay_core.path import screen_shape, signature_hash
240
+ from ..state import signature
241
+ app, now = exit_.get("app"), getattr(snapshot, "bundle_id", "") or ""
242
+ if app and now and app != now:
243
+ return False, f"{now} is in front, not {app}"
244
+ labels = [folded(element.label) for element in getattr(snapshot, "elements", ()) or () if element.label]
245
+ shows = [name for name in exit_.get("shows") or () if values.get(name)]
246
+ for name in shows:
247
+ if not any(folded(values[name]) in label for label in labels):
248
+ return False, f'"{values[name]}" isn\'t on the screen'
249
+ if exit_.get("shape"):
250
+ pairs = signature(snapshot)
251
+ if not (exit_.get("hash") and signature_hash(pairs) == exit_["hash"]):
252
+ ours, theirs = set(exit_["shape"]), set(screen_shape(pairs))
253
+ likeness = len(ours & theirs) / len(ours | theirs) if ours | theirs else 1.0
254
+ if likeness < (EXIT_SHOWN if shows else WEAK):
255
+ return False, f"the screen is different ({round(likeness * 100)}% alike)"
256
+ return True, ""
257
+
258
+
259
+ class PartRun(Matched):
260
+ """A part filled for one request, run as the replay in progress (FrontierAgent's saved-program path)."""
261
+
262
+ kind, noun = "part", "a part of a saved program"
263
+
264
+ def __init__(self, path, info, values, item, launch=None):
265
+ super().__init__({**path, "steps": (path.get("steps") or [])[info["start"]:info["end"] + 1],
266
+ "contract": None}, values)
267
+ self.whole, self.info, self.item, self.outcome = path, info, item, None
268
+ for step in self.steps:
269
+ name = step.get("label_param") or step.get("label_prefix")
270
+ if name and not self.values.get(name):
271
+ step["target"] = {**step["target"], "label": "", "id": None} # never the run that taught it
272
+ if self.steps and self.steps[0].get("op") == "LAUNCH_APP" and launch is False:
273
+ self.steps = self.steps[1:]
274
+ elif launch:
275
+ self.steps.insert(0, {"op": "LAUNCH_APP", "app": launch, "target": None, "screen": None, "text": None,
276
+ "param": None, "commit": None, "point": None, "live": False})
277
+ self.trusted = path.get("status") == TRUSTED or \
278
+ ((path.get("parts") or {}).get(info["key"]) or {}).get("status") == TRUSTED
279
+
280
+ def unbound(self, index):
281
+ step = self.steps[index] if 0 <= index < len(self.steps) else {}
282
+ return bool(_blank(step)) and not self.values.get(_blank(step))
283
+
284
+
285
+ class Parts:
286
+
287
+ def __init__(self, store, *, device=None, run_id=None):
288
+ self.store, self.device, self.run_id = store, device, run_id
289
+ self.apps, self.pairs, self.last_pairs = {}, {}, None
290
+ self.active, self.runs, self.tried, self._bound, self._paths = None, [], set(), {}, None
291
+
292
+ def programs(self):
293
+ if self._paths is None:
294
+ try:
295
+ # A coordinate step (a taught tap on a spot with no label) never runs inside another task's words.
296
+ self._paths = [path for path in self.store.all()
297
+ if (path.get("identity") or {}).get("device") == (self.device or None)
298
+ and not any(step.get("op") == "TAP_XY" for step in path.get("steps") or ())]
299
+ except Exception:
300
+ self._paths = []
301
+ return self._paths
302
+
303
+ def candidates(self):
304
+ """(path, info) for every part of every program on this phone that isn't forgotten."""
305
+ for path in self.programs():
306
+ for start, end in parts_of(path):
307
+ info = part_info(path, start, end)
308
+ if info and ((path.get("parts") or {}).get(info["key"]) or {}).get("status") != "forgotten":
309
+ yield path, info
310
+
311
+ def _saw(self, agent, snapshot):
312
+ from ..replay_core.path import signature_hash
313
+ from ..state import signature
314
+ self.apps = dict(getattr(agent, "apps", {}) or {})
315
+ try:
316
+ pairs = signature(snapshot)
317
+ except Exception:
318
+ return
319
+ self.pairs[signature_hash(pairs)], self.last_pairs = pairs, pairs
320
+ while len(self.pairs) > 64:
321
+ self.pairs.pop(next(iter(self.pairs)))
322
+
323
+ def bind(self, path, info, request, item=None, peers=()):
324
+ """{blank: value} by the words around each blank: never a secret, never one value for two blanks."""
325
+ key = (info["key"], (path.get("identity") or {}).get("template"), request, getattr(item, "id", None),
326
+ tuple(getattr(peer, "id", None) for peer in peers))
327
+ if key in self._bound:
328
+ return self._bound[key]
329
+ template, out = (path.get("identity") or {}).get("template") or "", {}
330
+ for name in info["blanks"]:
331
+ said = proposals(request, cue(template, name)) if cue(template, name) else []
332
+ own, near = [], []
333
+ for entry in path.get("contract") or ():
334
+ for field in ("what", "payload", "quote"):
335
+ anchors = cue(entry.get(field), name)
336
+ if not anchors:
337
+ continue
338
+ for other in ([item] if item is not None else []) + [p for p in peers if p is not item]:
339
+ if other.kind == entry.get("kind") and _about(entry.get("what"), other.what):
340
+ (own if other is item else near).extend(proposals(getattr(other, field, ""), anchors))
341
+ value = agreed(own) if own else agreed(near) if near else agreed(said)
342
+ if value is None or folded(value) not in folded(request) or holds_secret(value) \
343
+ or crosses(template, name, value) or len(tokens(value)) > MAX_VALUE_TOKENS:
344
+ continue
345
+ if (info["writes"] or info["commit"]) and not any(_starts_or_ends(_seq(value), _seq(hit))
346
+ for hit, _ in said):
347
+ continue # what a part types, the request must say where its program's request said it
348
+ out[name] = value
349
+ twice = {folded(v) for v in out.values() if sum(folded(w) == folded(v) for w in out.values()) > 1}
350
+ self._bound[key] = {name: value for name, value in out.items() if folded(value) not in twice}
351
+ return self._bound[key]
352
+
353
+ def may_start(self, agent, snapshot, request):
354
+ self._saw(agent, snapshot)
355
+ if not parts_on():
356
+ return False
357
+ front = getattr(snapshot, "bundle_id", "") or ""
358
+ for path, info in self.candidates():
359
+ steps = (path.get("steps") or [])[info["start"]:info["end"] + 1]
360
+ if info["app"] != front or info["launch"]:
361
+ continue
362
+ template, guess = (path.get("identity") or {}).get("template") or "", {}
363
+ for name in _needed(info):
364
+ said = proposals(request, cue(template, name)) if cue(template, name) else []
365
+ if said:
366
+ guess[name] = said[0][0]
367
+ if _needed(info) <= set(guess) and fits(filled_steps({"steps": steps[:1]}, guess)[0], snapshot)[0]:
368
+ return True
369
+ return False
370
+
371
+ def point(self, agent, snapshot, request, calls, actions):
372
+ """(result, feedback, run, snapshot): a part that just ended is checked, then a new one may start."""
373
+ self._saw(agent, snapshot)
374
+ if not parts_on():
375
+ return None, "", None, snapshot
376
+ feedback, chain, run = "", True, self.active
377
+ if run is not None:
378
+ self.active = None
379
+ if run.stopped is None and run.at >= len(run.steps):
380
+ ok, why, snapshot = self._exit(agent, run, snapshot, calls)
381
+ run.stopped = "done" if ok else why
382
+ self._settle(agent, run, "hit" if ok else "miss")
383
+ if not ok:
384
+ return None, (f"Mobster ran {run.noun} ({len(run.steps)} steps, no model), but its end check "
385
+ f"failed: {why}. Carry on with the request from the screen you see."), None, snapshot
386
+ if run.trusted:
387
+ ended = self._finish(agent, actions, calls)
388
+ if ended is not None:
389
+ return ended, "", None, snapshot
390
+ chain = run.trusted # after a provisional part the model looks first
391
+ feedback = (f"Mobster ran {run.noun} with no model: {len(run.steps)} steps, its end checked on the "
392
+ "screen. Carry on with the request from the screen you see.")
393
+ else:
394
+ self._settle(agent, run, "partial" if run.unbound(run.at) else "miss")
395
+ if not chain or len(self.runs) >= MAX_PART_RUNS:
396
+ return None, feedback, None, snapshot
397
+ pick = self._pick(agent, snapshot, request)
398
+ if pick is not None:
399
+ self.active = pick
400
+ self.runs.append(pick)
401
+ self.tried.add((pick.info["key"], getattr(pick.item, "id", None)))
402
+ self._event(agent, phase="start", step=calls, part=pick.info["key"], steps=len(pick.steps),
403
+ trusted=pick.trusted, item=getattr(pick.item, "id", None), blanks=sorted(pick.values))
404
+ return None, feedback, pick, snapshot
405
+
406
+ def _event(self, agent, **fields):
407
+ try:
408
+ agent._emit({"event": "frontier_part", **fields})
409
+ except Exception:
410
+ pass
411
+
412
+ def _pick(self, agent, snapshot, request):
413
+ """The part to run now, or None: it serves the earliest open item (more steps, trusted, more hits first)."""
414
+ contract = getattr(agent, "_contract", None)
415
+ if contract is None:
416
+ return None
417
+ open_items, visited = [], set()
418
+ for item in contract.items:
419
+ if item.kind == "FORBID" or item.kind == "REPORT" and item.status != "open":
420
+ continue
421
+ try:
422
+ if item.kind != "REPORT" and contract.proof(item)[0]:
423
+ if item.kind != "READ":
424
+ continue
425
+ visited.add(item.id) # a READ a visit counts as done: a look while its value was never on screen
426
+ except Exception:
427
+ pass
428
+ open_items.append(item)
429
+ apps, front = dict(getattr(agent, "apps", {}) or {}), getattr(snapshot, "bundle_id", "") or ""
430
+ busy_here = not getattr(agent, "_actions", 0) or any(
431
+ _bundle_of(item.app, apps) in (front, None) for item in open_items if item.id not in visited)
432
+ order = {item.id: index for index, item in enumerate(open_items)}
433
+ best, best_rank = None, None
434
+ for path, info in self.candidates():
435
+ if info["app"] not in apps:
436
+ continue
437
+ item, values = self._serves(path, info, request, open_items, apps, contract, visited)
438
+ if item is None or (info["key"], item.id) in self.tried:
439
+ continue
440
+ if info["commit"] and not (item.kind == "COMMIT" and item.act == info["commit"]):
441
+ continue
442
+ if _written(info, (path.get("steps") or [])[info["start"]:info["end"] + 1], values, contract, apps):
443
+ continue # this run already wrote it: never twice
444
+ launch = None
445
+ first = filled_steps({"steps": (path.get("steps") or [])[info["start"]:info["start"] + 2]}, values)
446
+ if info["launch"]:
447
+ if front != info["app"] and busy_here:
448
+ continue
449
+ launch = False if front == info["app"] else None
450
+ elif front == info["app"]:
451
+ if not fits(first[0], snapshot)[0]:
452
+ continue
453
+ elif not busy_here:
454
+ launch = info["app"] # nothing open is left where the run is: open this app first
455
+ else:
456
+ continue
457
+ stats = (path.get("parts") or {}).get(info["key"]) or {}
458
+ rank = (order[item.id], -(info["end"] - info["start"]), path.get("status") != TRUSTED,
459
+ -int(stats.get("hits") or 0))
460
+ if best_rank is None or rank < best_rank:
461
+ best, best_rank = PartRun(path, info, values, item, launch=launch), rank
462
+ return best
463
+
464
+ def _serves(self, path, info, request, open_items, apps, contract, visited):
465
+ families = {FAMILY.get(entry.get("kind"), entry.get("kind")) for entry in info["serves"]}
466
+ mine = [item for item in open_items if _bundle_of(item.app, apps) in (info["app"], None)
467
+ and FAMILY.get(item.kind, item.kind) in families]
468
+ texts = {item.id: folded(" ".join((item.what, item.payload, item.quote))) for item in mine}
469
+ for item in mine:
470
+ if not info["blanks"]:
471
+ if item.id not in visited and any(_about(entry.get("what"), item.what) for entry in info["serves"]):
472
+ return item, {}
473
+ continue
474
+ values = self.bind(path, info, request, item, mine)
475
+ if item.id in visited and any(contract._seen(lambda text, *_: folded(v) in text, item.app) is not None
476
+ for v in values.values()):
477
+ continue
478
+ if values and _needed(info) <= set(values) and any(folded(v) in texts[item.id] for v in values.values()) \
479
+ and all(any(folded(v) in texts[other.id] for other in mine) for v in values.values()):
480
+ return item, values
481
+ return None, {}
482
+
483
+ def _exit(self, agent, run, snapshot, calls):
484
+ end, current = time.monotonic() + EXIT_WAIT, snapshot
485
+ while True:
486
+ ok, why = exit_holds(run.info["exit"], run.values, current)
487
+ if ok or time.monotonic() >= end:
488
+ return ok, why, current
489
+ time.sleep(EXIT_POLL)
490
+ try:
491
+ current = agent._read_once()
492
+ if getattr(agent, "_contract", None) is not None:
493
+ agent._contract.observe(agent._masked(current), agent._app_name(current), calls)
494
+ except Exception:
495
+ return ok, why, current
496
+
497
+ def _finish(self, agent, actions, calls):
498
+ import copy
499
+ contract = getattr(agent, "_contract", None)
500
+ if contract is None or actions < 1 or getattr(agent, "longform", None) is not None \
501
+ or any(item.kind == "REPORT" for item in contract.items):
502
+ return None
503
+ said = "Done: Mobster repeated parts of saved programs for this request."
504
+ if not copy.deepcopy(contract).done_gate(said, agent.max_steps - calls)[0]:
505
+ return None
506
+ accepted, _, answer = contract.done_gate(said, agent.max_steps - calls)
507
+ if not accepted:
508
+ return None
509
+ self._event(agent, phase="finish", step=calls)
510
+ return {"status": "completed", "answer": answer}
511
+
512
+ def _settle(self, agent, run, outcome):
513
+ run.outcome = outcome
514
+ self._event(agent, phase="end", part=run.info["key"], outcome=outcome, at=run.at, steps=len(run.steps),
515
+ why=agent._mask(run.stopped or "")[:200] if hasattr(agent, "_mask") else "")
516
+ if outcome == "miss" or outcome == "hit" and run.trusted:
517
+ self.store.note_part(run.whole, run.info["key"], outcome, trusted=run.trusted)
518
+
519
+ def settle(self, done):
520
+ for run in self.runs:
521
+ if run.outcome is None and run.stopped is None and run.at >= len(run.steps):
522
+ run.outcome = "hit" if done else "miss"
523
+ self.store.note_part(run.whole, run.info["key"], run.outcome, trusted=run.trusted)
524
+ elif run.outcome == "hit" and not run.trusted:
525
+ self.store.note_part(run.whole, run.info["key"], "hit" if done else "miss", trusted=False)
526
+
527
+ def report(self):
528
+ return {"started": len(self.runs), "hits": sum(run.outcome == "hit" for run in self.runs),
529
+ "misses": sum(run.outcome == "miss" for run in self.runs),
530
+ "partial": sum(run.outcome == "partial" for run in self.runs),
531
+ "steps": sum(min(run.at, len(run.steps)) for run in self.runs)}
532
+
533
+
534
+ def _needed(info):
535
+ """A part that commits needs every blank bound; another its first (one left unbound later hands the turn back)."""
536
+ return set(info["blanks"]) if info["commit"] else set(info["blanks"][:1])
537
+
538
+
539
+ def _written(info, steps, values, contract, apps):
540
+ typed = [values.get(step.get("param")) for step in steps if step.get("op") in TEXT_OPS]
541
+ before = [folded(entry.get("text")) for entry in getattr(contract, "typed", ()) or ()]
542
+ if any(value and folded(value) in before for value in typed):
543
+ return True
544
+ if typed or not info["writes"]:
545
+ return False
546
+ return folded(apps.get(info["app"]) or info["app"]) in (getattr(contract, "first_writes", {}) or {})