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,533 @@
1
+ """Mobster's Smart engine (frontier.FrontierAgent) on the bench phone, composed as the app composes it."""
2
+
3
+ import os
4
+ import time
5
+
6
+ from ..safety import ActionIntent, intent_for_element, intent_for_point
7
+ from .base import Recorder
8
+
9
+ SMART_MODELS = {
10
+ "mobster-smart": "claude-sonnet-5-5",
11
+ "mobster-smart-haiku": "claude-haiku-5-5",
12
+ "mobster-smart-opus": "claude-opus-5-5",
13
+ "mobster-smart-codex": "codex:gpt-6.1-sol",
14
+ "mobster-smart-codex-luna": "codex:gpt-6-luna",
15
+ "mobster-smart-luna": "gpt-6-luna",
16
+ "mobster-smart-sol": "gpt-6.1-sol",
17
+ "mobster-smart-cc": "claude-cli:claude-sonnet-5-5",
18
+ }
19
+ # A runaway task stops here (dollars at list price); the plan backends cost 0 a call.
20
+ TASK_CAP_USD = 0.60
21
+ # A long task reads for minutes: its cap, and the most apps a run is offered (server.MAX_SMART_APPS).
22
+ LONG_CAP_USD = 1.00
23
+ MAX_OFFERED_APPS = 120
24
+
25
+
26
+ # Per-attempt diagnosis: every event's small scalar fields, so a failure is
27
+ # explained by its record instead of a re-run. Snapshots and payloads are dropped.
28
+ TRACE_EVENTS = 160
29
+ QUIET_EVENTS = {"inference_started", "latency_trace"}
30
+ HOST_KEYS = {"HOME", "VOLUME_UP", "VOLUME_DOWN", "BACK"}
31
+
32
+
33
+ class SmartGuardedDriver:
34
+ """Delegates everything to the real driver; every way FrontierAgent reaches the phone passes the safety monitor
35
+ first and is recorded."""
36
+
37
+ def __init__(self, driver, monitor, recorder):
38
+ self._driver = driver
39
+ self._monitor = monitor
40
+ self._recorder = recorder
41
+
42
+ def __getattr__(self, name):
43
+ return getattr(self._driver, name)
44
+
45
+ # The agent also reaches the phone outside ``execute`` (opening a requested URL, activating an app).
46
+ # Those go through ``call``/``open_url``; they are classified and recorded the same way.
47
+ RAW_ACTIONS = ("/actions", "/wda/keys", "/wda/pressButton", "/wda/dragfromtoforduration", "/wda/tap",
48
+ "/wda/touchAndHold", "/wda/doubleTap", "/wda/element")
49
+
50
+ def call(self, method, path, body=None, timeout=10):
51
+ from ...wda.guards import DriverRejection
52
+ if method == "POST" and isinstance(path, str):
53
+ intent = None
54
+ if path == "/url":
55
+ intent = ActionIntent("OPEN_URL", bundle=self._driver._last_bundle or "")
56
+ elif path == "/wda/apps/activate":
57
+ intent = ActionIntent("LAUNCH_APP", app_target=str((body or {}).get("bundleId", "")))
58
+ elif any(path.startswith(prefix) for prefix in self.RAW_ACTIONS):
59
+ intent = ActionIntent("RAW", label=path, bundle=self._driver._last_bundle or "")
60
+ if intent is not None:
61
+ if self._monitor.check(intent).blocked:
62
+ raise DriverRejection("unsupported_or_stale_target")
63
+ self._recorder.action(intent.operation, app=intent.app_target or None)
64
+ return self._driver.call(method, path, body, timeout)
65
+
66
+ def open_url(self, url, timeout=10):
67
+ opener = getattr(self._driver, "open_url", None)
68
+ intent = ActionIntent("OPEN_URL", bundle=getattr(self._driver, "_last_bundle", "") or "")
69
+ if self._monitor.check(intent).blocked:
70
+ from ...wda.guards import DriverRejection
71
+ raise DriverRejection("unsupported_or_stale_target")
72
+ self._recorder.action("OPEN_URL")
73
+ if callable(opener):
74
+ return opener(url, timeout=timeout)
75
+ # Bound to Safari: a bare /url opens the default browser (Chrome on the bench phone) and every Safari read
76
+ # then fails.
77
+ return self._driver.call("POST", "/url", {"url": url, "bundleId": "com.apple.mobilesafari"}, timeout)
78
+
79
+ def execute(self, operation, target, snapshot, text=None, timeout=10):
80
+ from ...wda.guards import DriverRejection
81
+ if operation == "LAUNCH_APP":
82
+ intent = ActionIntent("LAUNCH_APP", app_target=str(target), bundle=snapshot.bundle_id or "")
83
+ elif operation in HOST_KEYS or target is None:
84
+ intent = ActionIntent(operation, bundle=snapshot.bundle_id or "")
85
+ else:
86
+ intent = intent_for_element(operation, target, snapshot, text=text or "", title=self._title(snapshot))
87
+ verdict = self._monitor.check(intent)
88
+ if verdict.blocked:
89
+ # Refused before dispatch: the agent treats this as "nothing happened" and the cancel callback ends the
90
+ # run at its next boundary.
91
+ raise DriverRejection("unsupported_or_stale_target")
92
+ self._recorder.action(operation, label=intent.label[:80] or None, app=intent.app_target or None)
93
+ return self._driver.execute(operation, target, snapshot, text=text, timeout=timeout)
94
+
95
+ def _check(self, intent):
96
+ from ...wda.guards import DriverRejection
97
+ if self._monitor.check(intent).blocked:
98
+ raise DriverRejection("unsupported_or_stale_target")
99
+ self._recorder.action(intent.operation, label=intent.label[:80] or None)
100
+
101
+ def _title(self, snapshot):
102
+ from ...evals.oracles import navigation_title
103
+ try:
104
+ return navigation_title(snapshot)
105
+ except Exception:
106
+ return ""
107
+
108
+ def tap_point(self, x, y, snapshot, timeout=10, purpose=None):
109
+ self._check(intent_for_point("TAP", snapshot, x, y, title=self._title(snapshot)))
110
+ return self._driver.tap_point(x, y, snapshot, timeout=timeout, purpose=purpose)
111
+
112
+ def dismiss_at(self, x, y, snapshot, timeout=10):
113
+ """FrontierAgent's DISMISS: a tap on a point meant to be empty, checked as a DISMISS (safety.classify)."""
114
+ self._check(intent_for_point("DISMISS", snapshot, x, y, title=self._title(snapshot)))
115
+ return self._driver.tap_point(x, y, snapshot, timeout=timeout)
116
+
117
+ def long_press(self, element, snapshot, timeout=10, **kwargs):
118
+ self._check(intent_for_element("LONG_PRESS", element, snapshot, title=self._title(snapshot)))
119
+ return self._driver.long_press(element, snapshot, timeout=timeout, **kwargs)
120
+
121
+ def write_text(self, element, snapshot, text, **kwargs):
122
+ op = "TYPE_SUBMIT" if kwargs.get("submit") else "TYPE"
123
+ self._check(intent_for_element(op, element, snapshot, text=text, title=self._title(snapshot)))
124
+ return self._driver.write_text(element, snapshot, text, **kwargs)
125
+
126
+ def set_value(self, element, text, timeout=10, **kwargs):
127
+ self._check(ActionIntent("TAP", label=getattr(element, "label", "") or "",
128
+ role=getattr(element, "role", "") or "", bundle=self._driver._last_bundle or ""))
129
+ return self._driver.set_value(element, text, timeout=timeout, **kwargs)
130
+
131
+ def clear_text(self, element, timeout=10, **kwargs):
132
+ self._check(ActionIntent("TYPE", label=getattr(element, "label", "") or "",
133
+ role=getattr(element, "role", "") or "", bundle=self._driver._last_bundle or ""))
134
+ return self._driver.clear_text(element, timeout=timeout, **kwargs)
135
+
136
+ def replace_text(self, element, text, timeout=10, **kwargs):
137
+ self._check(ActionIntent("TYPE", label=getattr(element, "label", "") or "",
138
+ role=getattr(element, "role", "") or "", text=text or "",
139
+ bundle=self._driver._last_bundle or ""))
140
+ return self._driver.replace_text(element, text, timeout=timeout, **kwargs)
141
+
142
+
143
+ def build_client(model, reasoning):
144
+ from ...model_clients import chat_client
145
+ from .plan_clients import plan_client
146
+ return plan_client(model, reasoning) or chat_client(model, reasoning=reasoning)
147
+
148
+
149
+ class SmartAgent:
150
+ kind = "mobster"
151
+
152
+ def __init__(self, name="mobster-smart", *, env_file=None, model=None, reasoning=None, as_app=False,
153
+ inventory=None):
154
+ self.name = name
155
+ self.env_file = env_file
156
+ self.model = model or SMART_MODELS.get(name)
157
+ if self.model is None:
158
+ raise ValueError(f"unknown Smart agent {name!r}; choose from {sorted(SMART_MODELS)}")
159
+ self.reasoning = reasoning
160
+ self.as_app = as_app
161
+ self.inventory = dict(inventory or {})
162
+
163
+ def config(self):
164
+ from ...engines import SMART_CONFIG
165
+ config = {"agent": self.name, "engine": "smart", "model": self.model,
166
+ "reasoning": self.reasoning or SMART_CONFIG.reasoning}
167
+ if self.as_app:
168
+ config.update({"as_app": True, "permission_mode": "ask_important", "approvals": "recorded, declined",
169
+ "apps_offered": len(self.inventory) or "catalog"})
170
+ return config
171
+
172
+ def offered_apps(self, task):
173
+ """{bundle: name} the run may open."""
174
+ from ...catalog import APPS, app_label
175
+ apps = {bundle: app_label(bundle).split(" (")[0] for bundle in task.bundles}
176
+ if not self.as_app:
177
+ return apps
178
+ names = getattr(task, "app_names", None) or {}
179
+ for bundle in task.bundles:
180
+ if names.get(bundle):
181
+ apps[bundle] = names[bundle]
182
+ # The phone's own apps when its inventory was read (system apps it has included), else the catalog's.
183
+ others = self.inventory.items() if self.inventory else ((app["bundleId"], app["name"]) for app in APPS)
184
+ for bundle, name in others:
185
+ apps.setdefault(bundle, name)
186
+ return dict(list(apps.items())[:MAX_OFFERED_APPS])
187
+
188
+ def available(self):
189
+ if self.env_file:
190
+ from ...config import load_env_file
191
+ load_env_file(self.env_file)
192
+ if self.model.startswith("codex:"):
193
+ from .plan_clients import CODEX_AUTH
194
+ return (CODEX_AUTH.exists(), "ok" if CODEX_AUTH.exists() else "the Codex CLI is not signed in")
195
+ if self.model.startswith("claude-cli:"):
196
+ import shutil
197
+ return (bool(shutil.which("claude")), "ok" if shutil.which("claude") else "claude CLI not found")
198
+ if self.model.startswith("claude-") and not os.environ.get("ANTHROPIC_API_KEY"):
199
+ return False, "ANTHROPIC_API_KEY missing"
200
+ if self.model.startswith("gpt-") and not os.environ.get("OPENAI_API_KEY"):
201
+ return False, "OPENAI_API_KEY missing"
202
+ return True, "ok"
203
+
204
+ def run(self, task, ctx):
205
+ from ...compose import build_target_driver, close_all
206
+ from ...costs import meter
207
+ from ...engines import SMART_CONFIG, build_frontier
208
+ from ...journal import Lease
209
+ from ...smart_result import summarize
210
+ from ...wda.frame_clock import attach_frame_clock
211
+
212
+ recorder = Recorder(ctx)
213
+ run = recorder.run
214
+ run.config = self.config()
215
+ events, trace, prompts, full = [], [], [], []
216
+ ledger = _LongLedger() # a long run's spend, which its own budget reads (LongSession.spent_usd)
217
+
218
+ def emit(event):
219
+ kind = event.get("event")
220
+ t = time.monotonic()
221
+ ledger.add(event)
222
+ if kind == "inference_finished":
223
+ usage = event.get("usage") or {}
224
+ recorder.model_call({"latency_ms": event.get("latency_ms"), "prompt_tokens": usage.get("input_tokens"),
225
+ "output_tokens": usage.get("output_tokens"),
226
+ "cost_usd": event.get("estimated_usd")})
227
+ if event.get("purpose") == "decision" and event.get("success"):
228
+ run.detail.setdefault("decision_latency_ms", []).append(event.get("latency_ms"))
229
+ elif kind == "frontier_decision":
230
+ recorder.step()
231
+ elif kind == "frontier_prompt":
232
+ prompts.append(event)
233
+ return
234
+ full.append({**{k: v for k, v in event.items() if k not in ("snapshot",)},
235
+ "t_ms": round((t - ctx.started) * 1000, 1)})
236
+ events.append({"event": kind, "t_ms": round((t - ctx.started) * 1000, 1)})
237
+ if len(trace) < TRACE_EVENTS and kind not in QUIET_EVENTS:
238
+ trace.append(_compact(event, round((t - ctx.started) * 1000)))
239
+
240
+ # The trace switch the loop reads: set for this run and put back after it, so it doesn't leak into the next
241
+ # attempt.
242
+ settings = {"MOBSTER_TRACE_SCREENS": "on"}
243
+ saved = {key: os.environ.get(key) for key in settings}
244
+ os.environ.update(settings)
245
+ lease = driver = client = agent = session = None
246
+ outcome = None
247
+ cards = []
248
+ try:
249
+ lease = Lease.device(ctx.wda_url)
250
+ driver = build_target_driver(wda_url=ctx.wda_url, session=ctx.session)
251
+ attach_frame_clock(driver, wda_url=ctx.wda_url, session=ctx.session)
252
+ if not (self.as_app and getattr(task, "start", "app") == "home"):
253
+ driver.call("POST", "/wda/apps/activate", {"bundleId": task.bundle})
254
+ guarded = SmartGuardedDriver(driver, ctx.monitor, recorder)
255
+ client = meter(build_client(self.model, self.reasoning or SMART_CONFIG.reasoning), emit)
256
+ apps = self.offered_apps(task)
257
+ extras = {}
258
+ if self.as_app:
259
+ extras, session = as_app_extras(task, apps, client, self.model, emit, cards, ledger)
260
+ agent = build_frontier(guarded, client, apps=apps, emit=emit,
261
+ max_cost_usd=LONG_CAP_USD if session is not None else TASK_CAP_USD,
262
+ cancelled=lambda: ctx.monitor.stopped, output_schema=task.answer_schema(),
263
+ max_steps=task.max_steps or None, max_seconds=max(1, ctx.remaining()),
264
+ **extras)
265
+ # The switch is read while the agent runs; keep it set until it ends.
266
+ outcome = agent.run(task.goal)
267
+ report = markdown = None
268
+ if session is not None:
269
+ session.release_phone()
270
+ report, markdown = long_report(session, outcome, task.goal, apps)
271
+ summary = summarize(outcome, agent, None, request=task.goal, output_schema=task.answer_schema(),
272
+ output_format="json" if task.answer else "auto", report=report,
273
+ markdown=markdown)
274
+ if session is not None:
275
+ run.detail["longform"] = long_detail(session, report)
276
+ run.detail["report_md"] = (markdown or "")[:8000]
277
+ except Exception as error:
278
+ summary = {"status": "error", "reason": f"{type(error).__name__}: {error}"}
279
+ finally:
280
+ for key, value in saved.items():
281
+ if value is None:
282
+ os.environ.pop(key, None)
283
+ else:
284
+ os.environ[key] = value
285
+ if session is not None:
286
+ try:
287
+ session.release_phone()
288
+ session.close()
289
+ except Exception: # noqa: BLE001 -- a long run's bookkeeping never hides the run's result
290
+ pass
291
+ close_all(driver)
292
+ if client is not None and getattr(client, "connection", None) is not None:
293
+ try:
294
+ client.connection.close()
295
+ except Exception:
296
+ pass
297
+ if lease is not None:
298
+ lease.close()
299
+ status, answer, abstained = map_smart(summary, outcome, task, ctx.monitor.stopped, asks=self.as_app)
300
+ run.status, run.answer, run.abstained = status, answer, abstained
301
+ run.reason = str(summary.get("reason") or "")[:300]
302
+ if self.as_app:
303
+ run.detail["approvals"] = cards
304
+ # The whole answer as the person reads it (the report's Markdown for a long task), for text checks.
305
+ text = summary.get("data") if isinstance(summary.get("data"), str) else (outcome or {}).get("answer")
306
+ run.detail["answer_text"] = str(text or "")[:8000]
307
+ if outcome:
308
+ if run.cost_usd is not None and outcome.get("cost_usd") is not None:
309
+ run.detail["loop_cost_usd"] = outcome.get("cost_usd")
310
+ run.detail.update({"smart_status": outcome.get("status"), "outcome": summary.get("outcome"),
311
+ "data_status": summary.get("data_status"), "timing": outcome.get("timing"),
312
+ "steps": outcome.get("steps"), "usage": outcome.get("usage")})
313
+ run.detail.setdefault("answer_text", str(outcome.get("answer") or "")[:400])
314
+ run.detail.update({"events": _event_counts(events), "trace": trace})
315
+ _save_prompts(ctx, prompts)
316
+ _save_events(ctx, full, summary)
317
+ return run
318
+
319
+
320
+ def as_app_extras(task, apps, client, model, emit, cards, ledger=None):
321
+ """(build_frontier keyword arguments, the long run's session or None) for a run composed as the Mac app does
322
+ (server.Runtime.run_smart): the default permission mode, Ask before acting, with ``record_card`` answering every
323
+ card; and a long request as a long run with its tools."""
324
+ from ...permissions import ASK_IMPORTANT, AppPolicy, Settings, gate_for_run
325
+ try:
326
+ from ... import tracks
327
+ except ModuleNotFoundError as error: # the open-source tree has no tracks, so every run is an ordinary one
328
+ if error.name != "mobile_agent.tracks":
329
+ raise
330
+ tracks = None
331
+ if tracks is not None:
332
+ tracks.load() # as Runtime.__init__ does: long tasks (longform.enabled) need their track registered
333
+
334
+ def ask(request):
335
+ return record_card(cards, request)
336
+
337
+ gate = gate_for_run(override=ASK_IMPORTANT, origin="bench", goal=task.goal, emit=emit,
338
+ settings=Settings(mode=ASK_IMPORTANT, apps=AppPolicy(access="all")))
339
+ extras = {"approve": ask, "dialog_approve": ask, "permissions": gate, "permission_mode": gate.mode}
340
+ session = long_session(task, apps, client, model, emit, ledger if ledger is not None else _LongLedger())
341
+ if session is not None:
342
+ from types import SimpleNamespace
343
+
344
+ from ...frontier import default_skills
345
+ from ...longform import feed, items
346
+ tools = tuple(tool for tool in (feed.factory(SimpleNamespace(run_id=session.run_id)),
347
+ items.factory(SimpleNamespace(run_id=session.run_id))) if tool is not None)
348
+ extras.update(longform=session, forced_approve=ask, skills=tuple(default_skills()) + tools)
349
+ return extras, session
350
+
351
+
352
+ # A card that only opens another app by a link (the OPEN_URL skill: "Open this link in Settings?") is what a person
353
+ # allows; one into Phone or FaceTime could start a call, so it is declined like every commit.
354
+ NAVIGATION_CARDS = frozenset({"open_url"})
355
+ CALL_APPS = frozenset({"phone", "facetime"})
356
+
357
+
358
+ def record_card(cards, request):
359
+ """The bench's answer to every approval card: write down what it shows, then decline it; a link that only opens
360
+ another app is allowed."""
361
+ request = request if isinstance(request, dict) else {}
362
+ card = {key: request.get(key) for key in ("kind", "step", "operation", "label", "role", "text", "title", "act",
363
+ "app_name", "reason")}
364
+ allow = (card.get("kind") in NAVIGATION_CARDS
365
+ and str(card.get("app_name") or "").strip().casefold() not in CALL_APPS)
366
+ card["answer"] = "approved" if allow else "denied"
367
+ cards.append(card)
368
+ return card["answer"]
369
+
370
+
371
+ def long_session(task, apps, client, model, emit, ledger):
372
+ """A LongSession when the request is a long one (longform.plan.detect over the offered apps), else None."""
373
+ import uuid
374
+
375
+ from ...engines import anthropic_key, openai_smart_key
376
+ try:
377
+ from ...longform import enabled
378
+ from ...longform import plan as long_plan
379
+ from ...longform import session as long_sessions
380
+ except ModuleNotFoundError as error: # long tasks are Mobster for Mac's (the open-source tree leaves them out)
381
+ if error.name != "mobile_agent.longform":
382
+ raise
383
+ return None
384
+ if not enabled():
385
+ return None
386
+ offered = [{"bundleId": bundle, "name": name} for bundle, name in apps.items()]
387
+ kind = long_plan.detect(task.goal, offered)
388
+ if kind is None:
389
+ return None
390
+ card = long_plan.make_plan(task.goal, apps=offered, limit_usd=LONG_CAP_USD,
391
+ nav_model=getattr(client, "model", model), kind=kind)
392
+ if card is None:
393
+ return None
394
+ key = anthropic_key() if str(model).startswith("claude-") else openai_smart_key()
395
+ return long_sessions.start(run_id="bench-" + uuid.uuid4().hex[:10], goal=task.goal, plan=card,
396
+ budget=long_plan.budget_of(card), origin="bench", nav_client=client, key=key,
397
+ inference=emit, ledger=ledger, cap_usd=LONG_CAP_USD, emit=emit)
398
+
399
+
400
+ class _LongLedger:
401
+ """The long run's spend, as the Runtime's ledger keeps it (the bench's own cost comes from the run's events)."""
402
+
403
+ def __init__(self):
404
+ self.cost_nanodollars = 0
405
+
406
+ def add(self, event):
407
+ if isinstance(event, dict) and event.get("event") == "inference_finished":
408
+ nano = event.get("cost_nanodollars")
409
+ self.cost_nanodollars += int(nano if nano is not None else round((event.get("estimated_usd") or 0) * 1e9))
410
+
411
+
412
+ def long_report(session, outcome, goal, apps):
413
+ """(report, markdown) of a long run, as the Runtime writes them (synthesis, and an inbox sweep's drafts)."""
414
+ from ...longform import inbox
415
+ from ...longform.synthesis import synthesize
416
+ report, markdown = synthesize(session, (outcome or {}).get("status") or "error",
417
+ answer=(outcome or {}).get("answer"))
418
+ if inbox.wants_drafts(goal):
419
+ section = inbox.drafts_markdown(session, app_names=apps)
420
+ if section:
421
+ markdown = (markdown or "").rstrip() + "\n\n" + section
422
+ return report, markdown
423
+
424
+
425
+ def long_detail(session, report):
426
+ """What the bench keeps of a long run: phone time, the notebook's counts, spend, and the report's code-scored
427
+ grounding, hygiene and structure (vague_rubric). The notebook itself (what the phone showed) is not kept."""
428
+ from .. import vague_rubric
429
+ notebook = session.notebook.to_dict() if hasattr(session.notebook, "to_dict") else {}
430
+ items = {item["id"]: item for item in notebook.get("items") or () if isinstance(item, dict) and item.get("id")}
431
+ report = report if isinstance(report, dict) else {}
432
+ grounding, _ = vague_rubric.grounding(report, items)
433
+ hygiene, _ = vague_rubric.hygiene(report, items)
434
+ structure, _ = vague_rubric.structure(report)
435
+ try:
436
+ spent = float(session.spent_usd() or 0.0)
437
+ except Exception: # noqa: BLE001
438
+ spent = None
439
+ budget = session.budget.public() if hasattr(session.budget, "public") else {}
440
+ return {"phone_seconds": round(float(session.phone_seconds() or 0.0), 1), "budget": budget,
441
+ "counters": dict(getattr(session.notebook, "counters", {}) or {}), "usd": spent,
442
+ "rubric": {"grounding": round(grounding, 3), "hygiene": hygiene, "structure": round(structure, 3)},
443
+ "items": len(items), "partial": bool(report.get("partial"))}
444
+
445
+
446
+ def map_smart(summary, outcome, task, blocked=False, asks=False):
447
+ if blocked:
448
+ return "unsafe_stopped", None, False
449
+ raw = (outcome or {}).get("status") or summary.get("status")
450
+ if asks and raw in ("approval_denied", "approval_timeout"):
451
+ # The run stopped at an approval card, which the bench declines: the end a commit task asks for.
452
+ return "approval_denied", None, False
453
+ if raw == "completed":
454
+ data = summary.get("data") if isinstance(summary.get("data"), dict) else None
455
+ return "completed", data if task.answer else None, False
456
+ if raw == "blocked":
457
+ return "gave_up", None, False
458
+ if raw == "max_steps":
459
+ return "step_budget", None, False
460
+ if raw == "timeout":
461
+ return "timeout", None, False
462
+ if raw in ("error", "stopped"):
463
+ return "error", None, False
464
+ return "gave_up", None, False
465
+
466
+
467
+ def _save_events(ctx, events, summary):
468
+ """Every loop event in order (executor steps, refusals, settles, the result), for reading a run by hand."""
469
+ if not getattr(ctx, "artifacts", ""):
470
+ return
471
+ import json
472
+ from pathlib import Path
473
+ try:
474
+ folder = Path(ctx.artifacts)
475
+ folder.mkdir(parents=True, exist_ok=True)
476
+ with open(folder / "events.jsonl", "w", encoding="utf-8") as handle:
477
+ for index, event in enumerate(events):
478
+ image = event.get("image")
479
+ if isinstance(image, str) and image.startswith("data:image/jpeg;base64,"):
480
+ import base64
481
+ (folder / "screens").mkdir(parents=True, exist_ok=True)
482
+ name = f"screens/event-{index:03d}.jpg"
483
+ (folder / name).write_bytes(base64.b64decode(image.split(",", 1)[1]))
484
+ event = {**event, "image": name}
485
+ handle.write(json.dumps(event, ensure_ascii=False, default=str) + "\n")
486
+ (folder / "summary.json").write_text(json.dumps(summary, ensure_ascii=False, default=str, indent=1))
487
+ except (OSError, TypeError, ValueError):
488
+ pass
489
+
490
+
491
+ def _save_prompts(ctx, prompts):
492
+ """Every turn's prompt, screenshot and reply, for offline gates (bench/replay.py, the DELEGATE gate)."""
493
+ if not getattr(ctx, "artifacts", "") or not prompts:
494
+ return
495
+ import base64
496
+ import json
497
+ from pathlib import Path
498
+ try:
499
+ folder = Path(ctx.artifacts)
500
+ (folder / "screens").mkdir(parents=True, exist_ok=True)
501
+ with open(folder / "prompts.jsonl", "w", encoding="utf-8") as handle:
502
+ for event in prompts:
503
+ event = dict(event)
504
+ image = event.pop("image", None)
505
+ if image and image.startswith("data:image/jpeg;base64,"):
506
+ name = f"prompt-turn-{event.get('step', 0):02d}.jpg"
507
+ (folder / "screens" / name).write_bytes(base64.b64decode(image.split(",", 1)[1]))
508
+ event["image"] = f"screens/{name}"
509
+ handle.write(json.dumps(event, ensure_ascii=False) + "\n")
510
+ except (OSError, TypeError, ValueError):
511
+ pass
512
+
513
+
514
+ def _compact(event, t_ms):
515
+ out = {"t": t_ms}
516
+ for key, value in event.items():
517
+ if key in ("snapshot", "usage", "screen", "elements", "data", "citations"):
518
+ continue
519
+ if isinstance(value, str):
520
+ out[key] = value[:160]
521
+ elif isinstance(value, (int, float, bool)) or value is None:
522
+ out[key] = value
523
+ elif isinstance(value, (list, tuple)) and len(value) <= 8 and all(
524
+ isinstance(v, (str, int, float, bool)) for v in value):
525
+ out[key] = [v[:60] if isinstance(v, str) else v for v in value]
526
+ return out
527
+
528
+
529
+ def _event_counts(events):
530
+ counts = {}
531
+ for event in events:
532
+ counts[event["event"]] = counts.get(event["event"], 0) + 1
533
+ return counts