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,129 @@
1
+ """Offline check of a change to the commit guard's families: what would it newly allow, on recorded runs?
2
+
3
+ python -m mobile_agent.bench.guard_replay --runs DIR... --model claude-sonnet-5-5 --compiles 2 --out OUT.json \\
4
+ --env-file bench.env
5
+
6
+ For each recorded task, the request's contract is compiled ``--compiles`` times (contract.compile_contract, as the
7
+ loop does). Every recorded gate decision is then replayed under the old and the new family rule: each control the
8
+ guard refused, each commit-class control the run tapped, and every commit-class control on every screen the model
9
+ read. The guard's other checks (amount bounds, typed text, object names, approvals) are unchanged by a family change
10
+ and are not replayed: the question is only which controls' verbs would now count as declared.
11
+
12
+ A control newly allowed is listed with whether the request asks for its act (iosworld_stats.commit_requested, an
13
+ approximation) so each can be read by hand. The change passes when no newly allowed control is unrequested.
14
+ """
15
+
16
+ import argparse
17
+ import collections
18
+ import json
19
+ import pathlib
20
+ import re
21
+
22
+ from .. import contract as K
23
+ from .. import contract_screen as KS
24
+ from .iosworld_stats import NON_GUARD_REFUSALS, commit_requested
25
+
26
+ ROW = re.compile(r'^e\d+ (\w+) "([^"]*)"', re.M)
27
+ OTHER_REFUSALS = NON_GUARD_REFUSALS | {"tap_xy", "untargeted_swipe", "disabled", "passcode_keypad", "needs_approval"}
28
+
29
+
30
+ def old_family(item):
31
+ """The family rule before the request's own word for the act counted (contract.py at e24a7f9f)."""
32
+ fam = set(KS.FAMILIES.get(item.act, ()))
33
+ if item.act == "other":
34
+ fam |= {v.casefold() for v in KS.COMMIT_WORDS.findall(item.quote)}
35
+ return fam
36
+
37
+
38
+ def declared(contract, verbs, app, family):
39
+ return any(i.kind == "COMMIT" and K.Contract._app_ok(i, app) and verbs <= family(i) for i in contract.items)
40
+
41
+
42
+ def forbidden(contract, verbs, app):
43
+ return any(i.kind == "FORBID" and K.Contract._app_ok(i, app) and verbs & i.family() for i in contract.items)
44
+
45
+
46
+ def decisions(folder):
47
+ """(goal, app names, [(kind, label, role, app)]) from one recorded run."""
48
+ record = json.loads((folder / "task.json").read_text())
49
+ prompts = [json.loads(line) for line in (folder / "prompts.jsonl").read_text().splitlines() if line.strip()]
50
+ app_at, names, out = {}, [], []
51
+ for p in prompts:
52
+ text = p.get("text") or ""
53
+ current = text.split("Current app: ", 1)[1].split("\n", 1)[0].strip() if "Current app: " in text else ""
54
+ app_at[p.get("step")] = current
55
+ if not names and "Apps you may use:\n" in text:
56
+ names = [line.split(": ")[0].lstrip("- ").strip() for line in
57
+ text.split("Apps you may use:\n", 1)[1].split("\n\n")[0].splitlines()]
58
+ at = text.find("Screen elements:")
59
+ for role, label in ROW.findall(text[at:] if at >= 0 else ""):
60
+ if KS.label_verbs(label, role):
61
+ out.append(("on screen", label, role, current))
62
+ for e in (record.get("mobster") or {}).get("events") or ():
63
+ if e.get("event") == "frontier_refused" and e.get("label") not in OTHER_REFUSALS:
64
+ out.append(("refused", e.get("label") or "", "Button", app_at.get(e.get("step"), "")))
65
+ elif e.get("event") == "frontier_action" and e.get("operation") in ("TAP", "LONG_PRESS") and KS.label_verbs(
66
+ e.get("target_label") or ""):
67
+ out.append(("tapped", e.get("target_label") or "", "Button", e.get("app_name")
68
+ or app_at.get(e.get("step"), "")))
69
+ return record.get("goal") or "", names, out
70
+
71
+
72
+ def main(argv=None):
73
+ from ..model_clients import chat_client, cost_usd
74
+ from .iosworld import load_envs
75
+ parser = argparse.ArgumentParser(prog="python -m mobile_agent.bench.guard_replay")
76
+ parser.add_argument("--runs", nargs="+", required=True, help="run folders (each holding NNN-task/ folders)")
77
+ parser.add_argument("--model", default="claude-sonnet-5-5")
78
+ parser.add_argument("--compiles", type=int, default=2)
79
+ parser.add_argument("--env-file", action="append")
80
+ parser.add_argument("--out", required=True)
81
+ args = parser.parse_args(argv)
82
+ load_envs(args.env_file)
83
+ client = chat_client(args.model, reasoning="low")
84
+ by_task = collections.defaultdict(list)
85
+ for root in args.runs:
86
+ for task_json in sorted(pathlib.Path(root).glob("*/task.json")):
87
+ if (task_json.parent / "prompts.jsonl").exists():
88
+ by_task[task_json.parent.name.split("-", 1)[1]].append(task_json.parent)
89
+ newly, counts, compiled = [], collections.Counter(), {}
90
+ for task, folders in sorted(by_task.items()):
91
+ goal, names, _ = decisions(folders[0])
92
+ contracts = []
93
+ for _ in range(args.compiles):
94
+ c = K.compile_contract(client, goal, names)
95
+ if c is not None:
96
+ contracts.append(c)
97
+ compiled[task] = [[f"{i.kind} {i.act} ({i.app}): {i.what} | quote={i.quote!r}" for i in c.items if i.kind in
98
+ ("COMMIT", "FORBID")] for c in contracts]
99
+ seen = set()
100
+ for folder in folders:
101
+ _, _, decided = decisions(folder)
102
+ for kind, label, role, app in decided:
103
+ verbs = frozenset(KS.label_verbs(label, role))
104
+ if not verbs:
105
+ continue
106
+ for index, c in enumerate(contracts):
107
+ counts["decisions"] += 1
108
+ old = declared(c, verbs, app, old_family) and not forbidden(c, verbs, app)
109
+ new = declared(c, verbs, app, lambda i: i.family()) and not forbidden(c, verbs, app)
110
+ counts[f"{kind}: old {'allow' if old else 'refuse'} -> new {'allow' if new else 'refuse'}"] += 1
111
+ if new and not old and (task, label, app, index) not in seen:
112
+ seen.add((task, label, app, index))
113
+ newly.append({"task": task, "kind": kind, "label": label, "app": app, "compile": index,
114
+ "verbs": sorted(verbs), "requested": commit_requested(label, goal),
115
+ "goal": goal})
116
+ usd = cost_usd(client.model, client.usage) or 0.0
117
+ result = {"tasks": len(by_task), "counts": dict(counts), "newly_allowed": newly,
118
+ "newly_allowed_unrequested": [n for n in newly if n["requested"] is not True],
119
+ "compiled": compiled, "usd": round(usd, 4)}
120
+ pathlib.Path(args.out).write_text(json.dumps(result, indent=1, ensure_ascii=False))
121
+ print(json.dumps({k: result[k] for k in ("tasks", "counts", "usd")}, indent=1))
122
+ for n in newly:
123
+ print(f"NEWLY ALLOWED {n['task']:14} {n['kind']:9} {n['app']:10} {n['label'][:60]!r} "
124
+ f"requested={n['requested']}")
125
+ return 0
126
+
127
+
128
+ if __name__ == "__main__":
129
+ raise SystemExit(main())
@@ -0,0 +1,20 @@
1
+ {
2
+ "suite": "MobsterBench-iOS H2",
3
+ "version": "1.0",
4
+ "sha256": "a5d20b227eab62cfdbf39c4a80adbebcf19490311669e3a6f5e0f2edaa2ddf44",
5
+ "frozen_on": "2026-10-10",
6
+ "task_count": 10,
7
+ "tasks": [
8
+ "h2.calendar_three",
9
+ "h2.contact_two",
10
+ "h2.about_two",
11
+ "h2.receipts_count",
12
+ "h2.room_plus_phone",
13
+ "h2.code_digit_sum",
14
+ "h2.maps_alcatraz",
15
+ "h2.bridge_opened",
16
+ "h2.turing_phd_university",
17
+ "h2.reminder_last_alpha"
18
+ ],
19
+ "note": "Written before any engine ran it; never tuned on."
20
+ }
@@ -0,0 +1,20 @@
1
+ {
2
+ "suite": "MobsterBench-iOS H3",
3
+ "version": "1.0",
4
+ "sha256": "559d23a39a5887523fcfc8ba63d816c2935e71f2ffda8e871d8777a93204a1c3",
5
+ "frozen_on": "2026-10-10",
6
+ "task_count": 10,
7
+ "tasks": [
8
+ "h3.hopper_retired",
9
+ "h3.lovelace_father",
10
+ "h3.eiffel_height",
11
+ "h3.apollo_cmp",
12
+ "h3.python_designer",
13
+ "h3.coit_city",
14
+ "h3.space_needle_two",
15
+ "h3.sunglasses_count",
16
+ "h3.locker_and_room",
17
+ "h3.phone_minus_room"
18
+ ],
19
+ "note": "Written before any engine ran it; never tuned on."
20
+ }
@@ -0,0 +1,32 @@
1
+ {
2
+ "suite": "MobsterBench-iOS H1",
3
+ "version": "1.0",
4
+ "sha256": "2e83875ffbf3c0a3d342320d07ff7632c3e4ecf761926ac7d29e8c2b53cefc76",
5
+ "frozen_on": "2026-10-09",
6
+ "task_count": 16,
7
+ "tranches": {
8
+ "short": 6,
9
+ "retrieval": 4,
10
+ "multi_app": 4,
11
+ "text_entry": 2
12
+ },
13
+ "tasks": [
14
+ "h.short.battery",
15
+ "h.short.storage",
16
+ "h.short.screen_time",
17
+ "h.short.camera",
18
+ "h.short.note_link",
19
+ "h.short.contact_city",
20
+ "h.ret.calendar_start",
21
+ "h.ret.reminder_books",
22
+ "h.ret.turing_birthplace",
23
+ "h.ret.titanic_year",
24
+ "h.multi.note_plus",
25
+ "h.multi.contact_county",
26
+ "h.multi.room_times",
27
+ "h.multi.phone_minus",
28
+ "h.text.maps_bridge",
29
+ "h.text.settings_wallpaper"
30
+ ],
31
+ "note": "Written before any engine idea was built. Never tuned on: anything that learns uses heldout.build_training() and the v1 suite."
32
+ }
@@ -0,0 +1,375 @@
1
+ """The held-out tranches (MobsterBench-iOS H1, H2 and H3) and H1's training siblings, each frozen by hash."""
2
+
3
+ import dataclasses
4
+
5
+ from .checks import ActedAtLeast, AnswerIs, Claimed, Fixture, InApp, Literal, OnScreen, Truth
6
+ from .suite import (
7
+ BUNDLES,
8
+ LOOK_ONLY,
9
+ NO_CHANGE,
10
+ READ_ONLY_HISTORY,
11
+ REVERSIBLE_RESET,
12
+ SAFARI,
13
+ SETTINGS,
14
+ WIKI,
15
+ Task,
16
+ _canonical,
17
+ )
18
+
19
+ HELDOUT_NAME = "MobsterBench-iOS H1"
20
+ HELDOUT_VERSION = "1.0"
21
+ # (max steps, max seconds) per tranche, the same for every arm.
22
+ H_BUDGETS = {"short": (12, 90), "retrieval": (20, 150), "multi_app": (35, 300), "text_entry": (20, 150)}
23
+
24
+
25
+ def _h(id, tranche, apps, goal, checks, *, answer=(), **kw):
26
+ steps, seconds = H_BUDGETS[tranche]
27
+ return Task(id=id, category=tranche, apps=tuple(apps), goal=goal, checks=tuple(checks), answer=tuple(answer),
28
+ steps=steps, seconds=seconds, **kw)
29
+
30
+
31
+ def _nav(id, title, goal, tranche="short", min_actions=1, **kw):
32
+ return _h(id, tranche, ["settings"], goal + NO_CHANGE,
33
+ [Claimed(), ActedAtLeast(min_actions), InApp(SETTINGS), OnScreen(SETTINGS, Literal(title))], **kw)
34
+
35
+
36
+ def _ask(id, tranche, apps, goal, field, expect, *, mode="text", alternatives=(), min_actions=1, extra=(), **kw):
37
+ checks = [Claimed(), ActedAtLeast(min_actions), AnswerIs(field, expect, mode, tuple(alternatives)), *extra]
38
+ return _h(id, tranche, apps, goal, checks, answer=((field, "string"),), **kw)
39
+
40
+
41
+ def build_heldout():
42
+ return (
43
+ _nav("h.short.battery", "Battery", "In Settings, open Battery."),
44
+ _nav("h.short.storage", "iPhone Storage", "In Settings, open General, then iPhone Storage.", min_actions=2),
45
+ _nav("h.short.screen_time", "Screen Time", "In Settings, open Screen Time."),
46
+ _nav("h.short.camera", "Camera", "In Settings, open Camera."),
47
+ _ask("h.short.note_link", "short", ["notes"],
48
+ "In Notes, open the note titled 'MobsterBench Note' and report the web address written in it."
49
+ + LOOK_ONLY, "web_address", Literal(r"(https?://)?en\.m\.wikipedia\.org/wiki/alan_turing/?"),
50
+ mode="regex", fixtures=("notes.bench",)),
51
+ _ask("h.short.contact_city", "short", ["contacts"],
52
+ "In Contacts, open the contact Bench Tester and report the city in the address. Do not call, "
53
+ "message or edit the contact.", "city", Fixture("contacts.bench", "city"),
54
+ fixtures=("contacts.bench",)),
55
+
56
+ _ask("h.ret.calendar_start", "retrieval", ["calendar"],
57
+ "In Calendar, find the event 'MobsterBench Review' on 15 October 2026 and report its start time. "
58
+ "Do not edit, delete or respond to any event.", "start_time",
59
+ Literal(r"(2(:00)? ?p\.?m\.?|14[:.]00)"), mode="regex", fixtures=("calendar.bench",),
60
+ safety=REVERSIBLE_RESET, allow=("text_entry",)),
61
+ _ask("h.ret.reminder_books", "retrieval", ["reminders"],
62
+ "In Reminders, open the list named MobsterBench and report the reminder that mentions books, "
63
+ "exactly as written. Do not complete, edit or add reminders.", "reminder",
64
+ Literal("Return library books"), fixtures=("reminders.bench",)),
65
+ _ask("h.ret.turing_birthplace", "retrieval", ["safari"],
66
+ "According to this Wikipedia article, in which city was Alan Turing born?", "city",
67
+ Literal(r"(.*\b)?london(\b.*)?|maida vale.*"), mode="regex", min_actions=0,
68
+ start_url=WIKI + "Alan_Turing", safety=READ_ONLY_HISTORY, extra=[InApp(SAFARI)]),
69
+ _ask("h.ret.titanic_year", "retrieval", ["safari"],
70
+ "According to this Wikipedia article, in what year did the Titanic sink?", "year", Literal("1912"),
71
+ mode="number", min_actions=0, start_url=WIKI + "Titanic", safety=READ_ONLY_HISTORY,
72
+ extra=[InApp(SAFARI)]),
73
+
74
+ _ask("h.multi.note_plus", "multi_app", ["notes", "calculator"],
75
+ "Read the locker code in the note 'MobsterBench Note' in Notes, then use Calculator to add 1000 to "
76
+ "it, and report the result." + LOOK_ONLY, "result", Literal("5817"), mode="number", min_actions=4,
77
+ fixtures=("notes.bench",), safety=REVERSIBLE_RESET, allow=("calculator_keys",)),
78
+ _ask("h.multi.contact_county", "multi_app", ["contacts", "safari"],
79
+ "In Contacts, find the city in Bench Tester's address, then in Safari open the English Wikipedia "
80
+ "article about that city and report the county it is in. Do not call, message or edit the contact.",
81
+ "county", Literal(r"santa clara( county)?(,? california)?"), mode="regex", min_actions=3,
82
+ fixtures=("contacts.bench",), safety=READ_ONLY_HISTORY, allow=("text_entry",)),
83
+ _ask("h.multi.room_times", "multi_app", ["calendar", "calculator"],
84
+ "In Calendar, find the location of the event 'MobsterBench Review' on 15 October 2026, then use "
85
+ "Calculator to multiply the room number in it by 3, and report the result. Do not edit any event.",
86
+ "result", Literal("126"), mode="number", min_actions=4, fixtures=("calendar.bench",),
87
+ safety=REVERSIBLE_RESET, allow=("calculator_keys",)),
88
+ _ask("h.multi.phone_minus", "multi_app", ["contacts", "calculator"],
89
+ "In Contacts, read the last four digits of Bench Tester's phone number, then use Calculator to "
90
+ "subtract 77 from that four-digit number, and report the result. Do not call, message or edit the "
91
+ "contact.", "result", Literal("4400"), mode="number", min_actions=4, fixtures=("contacts.bench",),
92
+ safety=REVERSIBLE_RESET, allow=("calculator_keys",)),
93
+
94
+ _ask("h.text.maps_bridge", "text_entry", ["maps"],
95
+ "In Maps, search for the Golden Gate Bridge and report the city it is in. Do not start directions, "
96
+ "save, share or report anything.", "city", Literal("San Francisco"), alternatives=("SF",),
97
+ safety=READ_ONLY_HISTORY, allow=("text_entry",)),
98
+ _h("h.text.settings_wallpaper", "text_entry", ["settings"],
99
+ "Use the search field in Settings to find Wallpaper and open the Wallpaper screen." + NO_CHANGE,
100
+ [Claimed(), ActedAtLeast(2), InApp(SETTINGS), OnScreen(SETTINGS, Literal("Wallpaper"))],
101
+ safety=REVERSIBLE_RESET, allow=("text_entry",)),
102
+ )
103
+
104
+
105
+ def build_training():
106
+ """Siblings of the H1 templates (other values), for anything that learns. Never graded as held-out."""
107
+ return (
108
+ _nav("t.short.sounds", "Sounds & Haptics", "In Settings, open Sounds & Haptics."),
109
+ _nav("t.short.airdrop", "AirDrop", "In Settings, open General, then AirDrop.", min_actions=2),
110
+ _nav("t.short.focus", "Focus", "In Settings, open Focus."),
111
+ _ask("t.short.note_code", "short", ["notes"],
112
+ "In Notes, open the note titled 'MobsterBench Note' and report the locker code written in it."
113
+ + LOOK_ONLY, "code", Fixture("notes.bench", "code"), fixtures=("notes.bench",)),
114
+ _ask("t.short.contact_company", "short", ["contacts"],
115
+ "In Contacts, open the contact Bench Tester and report the company name. Do not call, message or "
116
+ "edit the contact.", "company", Fixture("contacts.bench", "company"), fixtures=("contacts.bench",)),
117
+ _ask("t.ret.calendar_location", "retrieval", ["calendar"],
118
+ "In Calendar, find the event 'MobsterBench Review' on 15 October 2026 and report its location. "
119
+ "Do not edit, delete or respond to any event.", "location", Fixture("calendar.bench", "location"),
120
+ fixtures=("calendar.bench",), safety=REVERSIBLE_RESET, allow=("text_entry",)),
121
+ _ask("t.ret.reminder_plants", "retrieval", ["reminders"],
122
+ "In Reminders, open the list named MobsterBench and report the reminder that mentions plants, "
123
+ "exactly as written. Do not complete, edit or add reminders.", "reminder",
124
+ Literal("Water the plants"), fixtures=("reminders.bench",)),
125
+ _ask("t.ret.hopper_birthplace", "retrieval", ["safari"],
126
+ "According to this Wikipedia article, in which city was Grace Hopper born?", "city",
127
+ Literal(r"(.*\b)?new york( city)?(\b.*)?"), mode="regex", min_actions=0,
128
+ start_url=WIKI + "Grace_Hopper", safety=READ_ONLY_HISTORY),
129
+ _ask("t.multi.note_minus", "multi_app", ["notes", "calculator"],
130
+ "Read the locker code in the note 'MobsterBench Note' in Notes, then use Calculator to subtract 800 "
131
+ "from it, and report the result." + LOOK_ONLY, "result", Literal("4017"), mode="number",
132
+ min_actions=4, fixtures=("notes.bench",), safety=REVERSIBLE_RESET, allow=("calculator_keys",)),
133
+ _ask("t.multi.room_plus", "multi_app", ["calendar", "calculator"],
134
+ "In Calendar, find the location of the event 'MobsterBench Review' on 15 October 2026, then use "
135
+ "Calculator to add 100 to the room number in it, and report the result. Do not edit any event.",
136
+ "result", Literal("142"), mode="number", min_actions=4, fixtures=("calendar.bench",),
137
+ safety=REVERSIBLE_RESET, allow=("calculator_keys",)),
138
+ _ask("t.multi.phone_plus", "multi_app", ["contacts", "calculator"],
139
+ "In Contacts, read the last four digits of Bench Tester's phone number, then use Calculator to add "
140
+ "23 to that four-digit number, and report the result. Do not call, message or edit the contact.",
141
+ "result", Literal("4500"), mode="number", min_actions=4, fixtures=("contacts.bench",),
142
+ safety=REVERSIBLE_RESET, allow=("calculator_keys",)),
143
+ _ask("t.text.maps_liberty", "text_entry", ["maps"],
144
+ "In Maps, search for the Statue of Liberty and report the city it is in. Do not start directions, "
145
+ "save, share or report anything.", "city", Literal("New York"),
146
+ alternatives=("New York City", "NYC", "New York, NY"), safety=READ_ONLY_HISTORY,
147
+ allow=("text_entry",)),
148
+ )
149
+
150
+
151
+ # The hard set (H-hard, 9 Oct, owner: "harder benchmark problems ... long horizon, complex"): long multi-app chains,
152
+ # web testing and library counts. Not held out: it is for telling architectures apart.
153
+ HARD_BUDGET = (40, 360)
154
+
155
+
156
+ def _hard(id, apps, goal, field, expect, *, mode="text", **kw):
157
+ return Task(id=id, category="hard", apps=tuple(apps), goal=goal,
158
+ checks=(Claimed(), AnswerIs(field, expect, mode)), answer=((field, "string"),),
159
+ steps=HARD_BUDGET[0], seconds=HARD_BUDGET[1], **kw)
160
+
161
+
162
+ def build_hard():
163
+ return (
164
+ _hard("hard.four_apps", ["notes", "reminders", "calendar", "calculator"],
165
+ "Take the locker code from the note 'MobsterBench Note' in Notes, add the number of incomplete "
166
+ "reminders in the Reminders list MobsterBench, multiply that sum by the room number in the location "
167
+ "of the Calendar event 'MobsterBench Review' on 15 October 2026, using Calculator for the arithmetic, "
168
+ "and report the result. Do not change any note, reminder or event.", "result", Literal("202440"),
169
+ mode="number", fixtures=("notes.bench", "reminders.bench", "calendar.bench"), safety=REVERSIBLE_RESET,
170
+ allow=("calculator_keys", "text_entry")),
171
+ _hard("hard.city_incorporated", ["contacts", "safari"],
172
+ "In Contacts, find the city in Bench Tester's address. Then in Safari open the English Wikipedia "
173
+ "article about that city and report the year the city was incorporated. Do not call, message or edit "
174
+ "the contact.", "year", Literal("1955"), mode="number", fixtures=("contacts.bench",),
175
+ safety=READ_ONLY_HISTORY, allow=("text_entry",)),
176
+ _hard("hard.turing_years", ["notes", "safari", "calculator"],
177
+ "Open the note 'MobsterBench Note' in Notes and open the web link written in it in Safari. Find the "
178
+ "year Alan Turing's paper 'On Computable Numbers' was published, then use Calculator to subtract that "
179
+ "year from 2026, and report the result." + LOOK_ONLY, "result", Literal("90"), mode="number",
180
+ fixtures=("notes.bench",), safety=REVERSIBLE_RESET, allow=("calculator_keys", "text_entry")),
181
+ _hard("hard.files_folders", ["files"],
182
+ "In Files, open On My iPhone, then the MobsterBench folder, and report how many folders it contains."
183
+ + LOOK_ONLY, "count", Literal("4"), mode="number", fixtures=("files.dogs12",)),
184
+ _hard("hard.album_count", ["photos"],
185
+ "In Photos, find the album 'MobsterBench Receipts' and report how many photos it contains." + LOOK_ONLY,
186
+ "count", Literal("12"), mode="number", fixtures=("photos.receipts12",)),
187
+ _hard("hard.reminders_sorted", ["reminders"],
188
+ "In Reminders, open the list MobsterBench and report its incomplete reminders in alphabetical order, "
189
+ "separated by commas. Do not complete, edit or add reminders.", "reminders",
190
+ Literal(r"pick up dry cleaning[,;]? (and )?return library books[,;]? (and )?water the plants"),
191
+ mode="regex", fixtures=("reminders.bench",)),
192
+ _hard("hard.example_iana", ["safari"],
193
+ "In Safari, on this page follow the 'More information' link and report the main heading of the page it "
194
+ "opens.", "heading", Literal("Example Domains"), start_url="https://example.com",
195
+ safety=READ_ONLY_HISTORY),
196
+ )
197
+
198
+
199
+ COMBO_IDS = ("t.short.sounds", "t.short.contact_company", "t.ret.calendar_location", "t.ret.hopper_birthplace",
200
+ "t.multi.note_minus", "t.text.maps_liberty")
201
+
202
+
203
+ def build_combo():
204
+ """The small, varied set for reading engine combinations by hand (never H1): six training siblings (short,
205
+ retrieval, web, multi-app, text entry) and one long v1 task (Settings, then Safari by URL, then a date)."""
206
+ from .suite import build_suite
207
+ training = {t.id: t for t in build_training()}
208
+ return tuple(training[i] for i in COMBO_IDS) + tuple(t for t in build_suite() if t.id == "multi.ios_release")
209
+
210
+
211
+ def heldout_document(tasks=None):
212
+ tasks = build_heldout() if tasks is None else tasks
213
+ return {"suite": HELDOUT_NAME, "version": HELDOUT_VERSION, "budgets": H_BUDGETS, "bundles": BUNDLES,
214
+ "tasks": [_canonical(task) for task in tasks]}
215
+
216
+
217
+ def heldout_hash(tasks=None):
218
+ import hashlib
219
+ import json
220
+ blob = json.dumps(heldout_document(tasks), sort_keys=True, separators=(",", ":"), ensure_ascii=True)
221
+ return hashlib.sha256(blob.encode()).hexdigest()
222
+
223
+
224
+ def tranche_counts(tasks=None):
225
+ counts = {}
226
+ for task in tasks or build_heldout():
227
+ counts[task.category] = counts.get(task.category, 0) + 1
228
+ return counts
229
+
230
+
231
+ # The harder held-out tranche for the engine-on A/B (owner, 10 Oct): multi-field answers, three-app chains, two-hop
232
+ # web lookups and counts, on the same fixtures and system apps. Written and hashed (h2-manifest.json) before any
233
+ # engine ran it; never tuned on.
234
+ H2_NAME, H2_VERSION = "MobsterBench-iOS H2", "1.0"
235
+ H2_BUDGET = (30, 240)
236
+
237
+
238
+ def _h2(id, apps, goal, answers, *, min_actions=1, **kw):
239
+ """``answers``: ((field, expect, mode), ...), each checked on its own."""
240
+ checks = [Claimed(), ActedAtLeast(min_actions)] + [AnswerIs(field, expect, mode) for field, expect, mode in answers]
241
+ return Task(id=id, category="h2", apps=tuple(apps), goal=goal, checks=tuple(checks),
242
+ answer=tuple((field, "string") for field, _, _ in answers), steps=H2_BUDGET[0], seconds=H2_BUDGET[1],
243
+ **kw)
244
+
245
+
246
+ def build_h2():
247
+ return (
248
+ _h2("h2.calendar_three", ["calendar"],
249
+ "In Calendar, find the event 'MobsterBench Review' on 15 October 2026 and report its start time, its end "
250
+ "time and the day of the week it falls on. Do not edit, delete or respond to any event.",
251
+ (("start_time", Literal(r"(2(:00)? ?p\.?m\.?|14[:.]00)"), "regex"),
252
+ ("end_time", Literal(r"(3(:00)? ?p\.?m\.?|15[:.]00)"), "regex"),
253
+ ("weekday", Literal("Thursday"), "text")),
254
+ fixtures=("calendar.bench",), safety=REVERSIBLE_RESET, allow=("text_entry",)),
255
+ _h2("h2.contact_two", ["contacts"],
256
+ "In Contacts, open the contact Bench Tester and report the company name and the city in the address. "
257
+ "Do not call, message or edit the contact.",
258
+ (("company", Fixture("contacts.bench", "company"), "text"),
259
+ ("city", Fixture("contacts.bench", "city"), "text")),
260
+ fixtures=("contacts.bench",)),
261
+ _h2("h2.about_two", ["settings"],
262
+ "In Settings > General > About, report the iOS version and the Model Name." + NO_CHANGE,
263
+ (("ios_version", Truth("device.ios_version"), "text"), ("model_name", Truth("device.model_name"), "text")),
264
+ min_actions=2),
265
+ _h2("h2.receipts_count", ["files"],
266
+ "In Files, open On My iPhone > MobsterBench > receipts12 and report how many files the folder contains."
267
+ + LOOK_ONLY, (("count", Literal("12"), "number"),), min_actions=2, fixtures=("files.receipts12",)),
268
+ _h2("h2.room_plus_phone", ["calendar", "contacts", "calculator"],
269
+ "Find the room number in the location of the Calendar event 'MobsterBench Review' on 15 October 2026, "
270
+ "then read the last four digits of Bench Tester's phone number in Contacts, then use Calculator to add the "
271
+ "two numbers, and report the sum. Do not edit, call or message anything.",
272
+ (("sum", Literal("4519"), "number"),), min_actions=5,
273
+ fixtures=("calendar.bench", "contacts.bench"), safety=REVERSIBLE_RESET,
274
+ allow=("calculator_keys", "text_entry")),
275
+ _h2("h2.code_digit_sum", ["notes", "calculator"],
276
+ "Read the locker code in the note 'MobsterBench Note' in Notes, then use Calculator to add up its four "
277
+ "digits, and report the total." + LOOK_ONLY, (("total", Literal("20"), "number"),), min_actions=5,
278
+ fixtures=("notes.bench",), safety=REVERSIBLE_RESET, allow=("calculator_keys",)),
279
+ _h2("h2.maps_alcatraz", ["maps"],
280
+ "In Maps, search for Alcatraz Island and report the city it belongs to. Do not start directions, save, "
281
+ "share or report anything.", (("city", Literal(r"(.*\b)?san francisco(\b.*)?"), "regex"),),
282
+ safety=READ_ONLY_HISTORY, allow=("text_entry",)),
283
+ _h2("h2.bridge_opened", ["safari"],
284
+ "According to this Wikipedia article, in what year did the bridge open to the public?",
285
+ (("year", Literal("1937"), "number"),), min_actions=0, start_url=WIKI + "Golden_Gate_Bridge",
286
+ safety=READ_ONLY_HISTORY),
287
+ _h2("h2.turing_phd_university", ["safari"],
288
+ "From this Wikipedia article, open the article about the university where Alan Turing earned his PhD, and "
289
+ "report the year that university was founded.", (("founded", Literal("1746"), "number"),),
290
+ start_url=WIKI + "Alan_Turing", safety=READ_ONLY_HISTORY),
291
+ _h2("h2.reminder_last_alpha", ["reminders"],
292
+ "In Reminders, open the list MobsterBench and report which of its incomplete reminders comes last in "
293
+ "alphabetical order, exactly as written. Do not complete, edit or add reminders.",
294
+ (("reminder", Literal("Water the plants"), "text"),), fixtures=("reminders.bench",)),
295
+ )
296
+
297
+
298
+ # H3 (10 Oct 2026): a fresh held-out tranche for the default decision (owner: the routed engine becomes the default
299
+ # only when, on a tranche it was never tuned on, its median and p90 per task are at least as fast as Sonnet's with
300
+ # success no worse). Written and hashed (h3-manifest.json) before any engine ran it; H2 is tuned on since 10 Oct.
301
+ H3_NAME, H3_VERSION = "MobsterBench-iOS H3", "1.0"
302
+ H3_BUDGET = H2_BUDGET
303
+
304
+
305
+ def _h3(id, apps, goal, answers, **kw):
306
+ task = _h2(id, apps, goal, answers, **kw)
307
+ return dataclasses.replace(task, category="h3", steps=H3_BUDGET[0], seconds=H3_BUDGET[1])
308
+
309
+
310
+ def build_h3():
311
+ return (
312
+ _h3("h3.hopper_retired", ["safari"],
313
+ "According to this Wikipedia article, in what year did Grace Hopper finally retire from the US Navy?",
314
+ (("year", Literal("1986"), "number"),), min_actions=0, start_url=WIKI + "Grace_Hopper",
315
+ safety=READ_ONLY_HISTORY),
316
+ _h3("h3.lovelace_father", ["safari"],
317
+ "In Safari, open the English Wikipedia article about Ada Lovelace and report her father's name.",
318
+ (("father", Literal(r"(.*\b)?byron(\b.*)?"), "regex"),), safety=READ_ONLY_HISTORY, allow=("text_entry",)),
319
+ _h3("h3.eiffel_height", ["safari"],
320
+ "In Safari, open the English Wikipedia article about the Eiffel Tower and report its height in metres "
321
+ "(the total height, with antennas).", (("height_m", Literal("330"), "number"),), safety=READ_ONLY_HISTORY,
322
+ allow=("text_entry",)),
323
+ _h3("h3.apollo_cmp", ["safari"],
324
+ "According to this Wikipedia article, who was the command module pilot of the mission?",
325
+ (("pilot", Literal(r"(.*\b)?collins(\b.*)?"), "regex"),), min_actions=0, start_url=WIKI + "Apollo_11",
326
+ safety=READ_ONLY_HISTORY),
327
+ _h3("h3.python_designer", ["safari"],
328
+ "In Safari, open the English Wikipedia article about the Python programming language and report who "
329
+ "designed it.", (("designer", Literal(r"(.*\b)?guido van rossum(\b.*)?"), "regex"),),
330
+ safety=READ_ONLY_HISTORY, allow=("text_entry",)),
331
+ _h3("h3.coit_city", ["maps"],
332
+ "In Maps, search for Coit Tower and report the city it is in. Do not start directions, save, share or "
333
+ "report anything.", (("city", Literal(r"(.*\b)?san francisco(\b.*)?"), "regex"),),
334
+ safety=READ_ONLY_HISTORY, allow=("text_entry",)),
335
+ _h3("h3.space_needle_two", ["maps", "safari"],
336
+ "In Maps, search for the Space Needle and report the city it is in, then in Safari open the English "
337
+ "Wikipedia article about the Space Needle and report the year it opened. Do not start directions, save "
338
+ "or share anything.", (("city", Literal(r"(.*\b)?seattle(\b.*)?"), "regex"),
339
+ ("opened", Literal("1962"), "number")),
340
+ min_actions=3, safety=READ_ONLY_HISTORY, allow=("text_entry",)),
341
+ _h3("h3.sunglasses_count", ["files"],
342
+ "In Files, open On My iPhone > MobsterBench > sunglasses12 and report how many files the folder contains."
343
+ + LOOK_ONLY, (("count", Literal("12"), "number"),), min_actions=2, fixtures=("files.sunglasses12",)),
344
+ _h3("h3.locker_and_room", ["notes", "calendar"],
345
+ "Report the locker code written in the note 'MobsterBench Note' in Notes, and the room number in the "
346
+ "location of the Calendar event 'MobsterBench Review' on 15 October 2026. Do not edit anything.",
347
+ (("locker_code", Literal("4817"), "number"), ("room", Literal("42"), "number")), min_actions=3,
348
+ fixtures=("notes.bench", "calendar.bench"), safety=REVERSIBLE_RESET, allow=("text_entry",)),
349
+ _h3("h3.phone_minus_room", ["contacts", "calendar", "calculator"],
350
+ "Read the last four digits of Bench Tester's phone number in Contacts and the room number in the location "
351
+ "of the Calendar event 'MobsterBench Review' on 15 October 2026, then use Calculator to subtract the room "
352
+ "number from the four digits, and report the result. Do not edit, call or message anything.",
353
+ (("result", Literal("4435"), "number"),), min_actions=5, fixtures=("contacts.bench", "calendar.bench"),
354
+ safety=REVERSIBLE_RESET, allow=("calculator_keys", "text_entry")),
355
+ )
356
+
357
+
358
+ def h3_hash(tasks=None):
359
+ import hashlib
360
+ import json
361
+ tasks = build_h3() if tasks is None else tasks
362
+ blob = json.dumps({"suite": H3_NAME, "version": H3_VERSION, "budget": H3_BUDGET, "bundles": BUNDLES,
363
+ "tasks": [_canonical(t) for t in tasks]}, sort_keys=True, separators=(",", ":"),
364
+ ensure_ascii=True)
365
+ return hashlib.sha256(blob.encode()).hexdigest()
366
+
367
+
368
+ def h2_hash(tasks=None):
369
+ import hashlib
370
+ import json
371
+ tasks = build_h2() if tasks is None else tasks
372
+ blob = json.dumps({"suite": H2_NAME, "version": H2_VERSION, "budget": H2_BUDGET, "bundles": BUNDLES,
373
+ "tasks": [_canonical(t) for t in tasks]}, sort_keys=True, separators=(",", ":"),
374
+ ensure_ascii=True)
375
+ return hashlib.sha256(blob.encode()).hexdigest()