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,599 @@
1
+ """Runtime's task work: the Smart engine's run, the worker thread and a run's end."""
2
+
3
+ import threading
4
+ import time
5
+ from pathlib import Path
6
+
7
+ from . import (
8
+ ai_plans,
9
+ costs,
10
+ engines,
11
+ harness_api,
12
+ orchestrate,
13
+ permissions,
14
+ project_env,
15
+ result_blocks,
16
+ smart_events,
17
+ smart_result,
18
+ traces,
19
+ )
20
+ from .api_errors import APIError
21
+ from .compose import close_all
22
+ from .costs import SpendLedger
23
+ from .latency_trace import Trace
24
+ from .lockscreen import after_error
25
+ from .replay_core import path as replay_path
26
+ from .runtime_driver import PhoneNotReady, RunSetup
27
+ from .wda import watchdog as wda_watchdog
28
+ from .wda.guards import DriverRejection
29
+
30
+ # Finished runs whose full-size final screenshot stays in memory; older ones show their step frames.
31
+ KEPT_PREVIEWS = 3
32
+ MAX_SMART_APPS = 120
33
+
34
+
35
+ def smart_request(run):
36
+ pins = [pin["name"] for pin in run.pinned_apps or () if pin.get("name")]
37
+ if len(pins) > 1:
38
+ return f"In {orchestrate.joined(pins)}: {run.goal}"
39
+ return f"In {run.app['name']}: {run.goal}" if run.app.get("bundleId") else run.goal
40
+
41
+
42
+ # Each provider runs on its own thread while the phone gets ready; join waits until BUDGET after the gather began.
43
+ class ContextGather:
44
+ BUDGET = 2.0
45
+
46
+ def __init__(self, ctx, emit, budget=None):
47
+ self.ctx, self.emit = ctx, emit
48
+ self.budget = self.BUDGET if budget is None else budget
49
+ self.started = time.monotonic()
50
+ self.providers = harness_api.context_providers(ctx)
51
+ self.jobs = []
52
+ for provider in self.providers:
53
+ box = {}
54
+ thread = threading.Thread(target=self._collect, args=(provider, box), daemon=True,
55
+ name=f"mobster-context-{getattr(provider, 'key', '?')}")
56
+ thread.start()
57
+ self.jobs.append((provider, box, thread))
58
+
59
+ def _collect(self, provider, box):
60
+ try:
61
+ box["blocks"] = list(provider.blocks(self.ctx) or ())
62
+ except BaseException as error: # noqa: BLE001
63
+ box["error"] = type(error).__name__
64
+
65
+ def join(self):
66
+ deadline = self.started + self.budget
67
+ blocks, ready, used, chars = [], [], [], 0
68
+ for provider, box, thread in self.jobs:
69
+ thread.join(max(0.0, deadline - time.monotonic()))
70
+ key = str(getattr(provider, "key", "") or "")[:40]
71
+ if thread.is_alive():
72
+ self.emit({"event": "context_error", "key": key, "error": "timeout"})
73
+ continue
74
+ if "error" in box:
75
+ self.emit({"event": "context_error", "key": key, "error": box["error"]})
76
+ continue
77
+ ready.append(provider)
78
+ cap = getattr(provider, "max_chars", None)
79
+ for block in box.get("blocks", ()):
80
+ if not isinstance(block, harness_api.ContextBlock) or not block.text:
81
+ continue
82
+ if isinstance(cap, int) and cap > 0 and len(block.text) > cap:
83
+ block = harness_api.ContextBlock(block.key, block.title, block.text[:cap - 1] + "…",
84
+ block.stable, block.untrusted, block.images)
85
+ blocks.append(block)
86
+ chars += len(block.text)
87
+ if key not in used:
88
+ used.append(key)
89
+ if blocks:
90
+ self.emit({"event": "context_used", "keys": used, "chars": chars})
91
+ return blocks, ready
92
+
93
+ def turn_context(self, providers):
94
+ # A provider that raises is skipped for that turn.
95
+ ctx = self.ctx
96
+
97
+ def turn(state):
98
+ out = []
99
+ for provider in providers:
100
+ try:
101
+ out.extend(provider.turn_blocks(ctx, state) or ())
102
+ except Exception: # noqa: BLE001
103
+ harness_api.log.exception("a provider's turn_blocks failed")
104
+ return [block for block in out if isinstance(block, harness_api.ContextBlock) and block.text]
105
+ return turn
106
+
107
+
108
+ class TaskWorkMixin:
109
+ def smart_apps(self, run):
110
+ apps = {}
111
+ if run.app.get("bundleId"):
112
+ apps[run.app["bundleId"]] = run.app["name"]
113
+ for pin in run.pinned_apps or ():
114
+ if pin.get("bundleId"):
115
+ apps.setdefault(pin["bundleId"], pin["name"])
116
+ for app in self.apps_for(self.phone_for(run)):
117
+ bundle = app.get("bundleId")
118
+ if bundle and app.get("installed") is not False and bundle not in apps:
119
+ apps[bundle] = app["name"]
120
+ allowed = getattr(run, "phone_apps", None)
121
+ if isinstance(allowed, frozenset):
122
+ apps = {bundle: name for bundle, name in apps.items() if bundle in allowed}
123
+ return dict(list(apps.items())[:MAX_SMART_APPS])
124
+
125
+ def estimate_for(self, engine, goal, output_schema=None, output_format=None):
126
+ try:
127
+ estimates = self.engine_estimates(goal, output_schema, output_format=output_format)
128
+ if engine not in estimates:
129
+ return None
130
+ # The figure kept with the run is what the model would cost: a path that costs nothing can still hand over.
131
+ return costs.midpoint({**estimates[engine], **estimates[engine].get("fallbackUsd", {})})
132
+ except Exception:
133
+ return None
134
+
135
+ def engine_estimates(self, goal="", output_schema=None, output_format=None):
136
+ state = self.engines_status()
137
+ names = [app["name"] for app in self.apps() if app.get("installed") is not False]
138
+ structured = output_schema is not None or output_format in smart_result.AUTOMATIC_FORMATS
139
+ estimate = costs.smart_estimate(goal, names, available=state[engines.SMART]["available"],
140
+ reason=state[engines.SMART]["reason"], structured=structured,
141
+ model=state[engines.SMART]["model"])
142
+ free = None if structured or not goal else self.free_kind(goal)
143
+ if free is not None:
144
+ # A request that costs no model call: $0, and what the model would cost if the path doesn't fit is kept.
145
+ estimate = {**estimate, "lowUsd": 0.0, "highUsd": 0.0, "basis": free,
146
+ "fallbackUsd": {"lowUsd": estimate["lowUsd"], "highUsd": estimate["highUsd"]}}
147
+ return {engines.SMART: estimate}
148
+
149
+ def free_kind(self, goal):
150
+ from .learned import zero
151
+ state_db = getattr(self.config, "state_db", None)
152
+ return zero.free_kind(Path(state_db).parent if state_db else None, goal)
153
+
154
+ def run_smart(self, run, trace, setup):
155
+ key = engines.smart_key()
156
+ ledger = SpendLedger()
157
+ keyless = harness_api.calls_no_model(run.extras)
158
+ with trace.span("setup.models"):
159
+ client = None if keyless else self.smart_client() # a run that calls no model opens no connection
160
+ long_task = getattr(run, "longform", None)
161
+ events = smart_events.SmartEvents(run.emit, ledger=ledger if long_task else None)
162
+
163
+ def inference(event):
164
+ ledger.add_event(event)
165
+ events.inference(event)
166
+
167
+ if client is not None:
168
+ costs.meter(client, inference)
169
+ session = None
170
+ recording = traces.Recording(getattr(self, "frames", None), run.id, smart_request(run))
171
+ try:
172
+ ctx = self.run_context(run)
173
+ hooks_ = harness_api.pre_runs(ctx)
174
+ handoff = None
175
+ for hook in [h for h in hooks_ if getattr(h, "before_phone", False)]:
176
+ if run.stop.is_set():
177
+ return None
178
+ try:
179
+ done = hook(ctx, None, None)
180
+ except Exception as error: # noqa: BLE001 -- a track's hook never breaks the run
181
+ harness_api.log.exception("a pre-run hook failed")
182
+ run.emit({"event": "context_error", "key": "pre_run", "error": type(error).__name__})
183
+ continue
184
+ if done is not None and done.result is not None:
185
+ result = {**done.result, "costUsd": run.cost_usd}
186
+ run.emit(result)
187
+ return result
188
+ if done is not None and done.handoff is not None and handoff is None:
189
+ handoff = done.handoff
190
+ if (run.extras or {}).get("resumeFrom") and handoff is None:
191
+ # A disabled or missing checkpoint hook must not turn a pickup into a fresh task.
192
+ from .harness import checkpoints
193
+ result = checkpoints._pickup_unavailable(ctx, "missing_hook").result
194
+ run.emit(result)
195
+ return result
196
+ # The plan, before the phone is touched: Start, Skip or a change.
197
+ ended = self.confirm_plan(run)
198
+ if ended is not None:
199
+ run.emit(ended)
200
+ return ended
201
+ long_task = getattr(run, "longform", None)
202
+ # A run that calls no model reads no context: its hook ends it, and nothing reads a provider's blocks.
203
+ gather = None if keyless else ContextGather(ctx, run.emit)
204
+ driver = self.open_driver(run, trace, setup)
205
+ if driver is None:
206
+ return None
207
+ run.capture = getattr(driver, "capture_preview", None)
208
+ events.driver, events.apps = driver, self.smart_apps(run)
209
+ gate = self.permission_gate(run)
210
+ # Full access has no approver; a one-time code the agent types still asks, through floor_approve.
211
+ approve = events.approving(run.request_approval) if gate.mode != permissions.DONT_ASK else None
212
+ floor_approve = None if approve is not None else events.approving(run.request_approval)
213
+ from .accounts.screen import commit_check_for
214
+ commit_check = commit_check_for(ctx)
215
+ # iOS's privacy prompts ask the person in every permission mode, even with Ask before acting off.
216
+ dialog_approve = approve or (events.approving(run.request_approval) if ctx.interactive else None)
217
+ program = parts = None
218
+ for hook in hooks_:
219
+ if getattr(hook, "before_phone", False):
220
+ continue
221
+ if (handoff is not None or program is not None) and not getattr(hook, "after_handoff", False):
222
+ continue
223
+ if run.stop.is_set():
224
+ return None
225
+ try:
226
+ # No approver: a routine hands off at its first commit, which the loop checks the account for.
227
+ done = hook(ctx, driver, approve if commit_check is None else None)
228
+ except Exception as error: # noqa: BLE001 -- a hook with the phone may have acted before it failed
229
+ harness_api.log.exception("a pre-run hook failed")
230
+ run.emit({"event": "context_error", "key": "pre_run", "error": type(error).__name__})
231
+ result = {"event": "result", "engine": engines.SMART, "status": "completion_not_confirmed",
232
+ "outcome": "check", "code": "outcome_unknown", "answer": None, "proof": [],
233
+ "independently_verified": False, "actions_may_have_run": True, "costUsd": run.cost_usd,
234
+ "reason": "Mobster stopped while preparing the task. An action may have run. "
235
+ "Inspect your iPhone before starting another task."}
236
+ run.emit(result)
237
+ return result
238
+ if done is None:
239
+ continue
240
+ if handoff is None and done.result is not None:
241
+ result = {**done.result, "costUsd": run.cost_usd}
242
+ run.emit(result)
243
+ return result
244
+ if handoff is None and done.handoff is not None:
245
+ handoff = done.handoff
246
+ if handoff is None and program is None and getattr(done, "program", None) is not None:
247
+ program = done.program # a saved program: the live loop takes its steps first
248
+ if parts is None and getattr(done, "parts", None) is not None:
249
+ parts = done.parts # parts of saved programs: the live loop may run one at a planning point
250
+ if keyless:
251
+ # A hook that doesn't end a run that calls no model leaves nothing to run: the loop below is a model's.
252
+ raise RuntimeError("Nothing ran this task, which calls no model. No action was taken; try again.")
253
+ if long_task:
254
+ from .longform import plan as long_plan
255
+ from .longform import session as long_session
256
+ card = long_task["plan"]
257
+ session = long_session.start(
258
+ run_id=run.id, goal=run.goal, plan=card, budget=long_plan.budget_of(card), origin=run.origin,
259
+ nav_client=client, key=key, inference=inference, ledger=ledger, cap_usd=self.run_cap(run),
260
+ emit=events, frames=getattr(self, "frames", None), journal=self.journal, awake=self.awake,
261
+ spent_offset=float(getattr(handoff, "cost_usd", 0.0) or 0.0), rules=self.thread_rules(ctx),
262
+ earlier=self.earlier_seen(ctx, card), run=run)
263
+ blocks, providers = gather.join()
264
+ if session is not None:
265
+ session.guidance = tuple(b.text for b in blocks or () if getattr(b, "key", None) == "plugins")
266
+ hooks = {}
267
+ if blocks:
268
+ hooks["context_blocks"] = blocks
269
+ if providers:
270
+ hooks["turn_context"] = gather.turn_context(providers)
271
+ extra_tools = harness_api.tools(ctx)
272
+ withheld = harness_api.WITHHELD_SKILLS.get(run.origin, frozenset())
273
+ if extra_tools or withheld:
274
+ from .frontier import default_skills
275
+ hooks["skills"] = tuple(skill for skill in (*default_skills(), *extra_tools)
276
+ if getattr(skill, "op", None) not in withheld)
277
+ if commit_check is not None:
278
+ # A task as an account: every commit in its app waits for the account confirmed on screen.
279
+ hooks["commit_check"] = commit_check
280
+ try:
281
+ hooks.update(harness_api.frontier_options(ctx))
282
+ except Exception as error: # noqa: BLE001
283
+ harness_api.log.exception("frontier options failed")
284
+ run.emit({"event": "context_error", "key": "frontier_options", "error": type(error).__name__})
285
+ listeners = run.listeners if harness_api.listeners() else None
286
+ if listeners is not None:
287
+ # Only the hooks a listener takes: the checkpoint hook runs the secret filter on the agent's thread.
288
+ wanted = {name for listener in harness_api.listeners() for name in ("on_step", "on_checkpoint")
289
+ if callable(getattr(listener, name, None))}
290
+ if "on_step" in wanted:
291
+ hooks["step_observer"] = lambda record: listeners.post("on_step", run, record)
292
+ if "on_checkpoint" in wanted:
293
+ hooks["checkpoint"] = lambda state: listeners.post("on_checkpoint", run, state)
294
+ # A long run's DONE turn carries no data: its schema is applied to the final report instead.
295
+ nav_schema = None
296
+ if session is None:
297
+ nav_schema = run.output_schema
298
+ if nav_schema is None and run.output_format in smart_result.AUTOMATIC_FORMATS:
299
+ nav_schema = smart_result.TABLE
300
+ if session is not None:
301
+ # A long run asks before every act that asks, with Ask before acting off too: forced_approve asks.
302
+ hooks.update(longform=session, forced_approve=events.approving(run.request_approval))
303
+ agent = engines.build_frontier(driver, client, apps=events.apps, emit=events,
304
+ max_cost_usd=self.run_cap(run),
305
+ approve=approve, cancelled=run.stop.is_set, steering=run.steering,
306
+ clarify=ctx.clarify, run_context=ctx, dialog_approve=dialog_approve,
307
+ permissions=gate, floor_approve=floor_approve,
308
+ permission_mode=gate.mode, saved_program=program,
309
+ program_parts=parts,
310
+ output_schema=nav_schema,
311
+ guard=run.guard, recorder=recording.attach, **hooks)
312
+ events.agent = agent
313
+ if run.stop.is_set():
314
+ return None # The loop's first step is a model call (the task contract), billed even if stopped.
315
+ setup.phase("running")
316
+ request = smart_request(run)
317
+ if (run.extras or {}).get("projectEnv"):
318
+ # The goal keeps ${NAME}; the agent gets the values and masks them from its first turn on.
319
+ values = project_env.for_task(run.extras["projectEnv"], request)
320
+ request = project_env.expand(request, values)
321
+ agent.preset_secrets = set(values.values())
322
+ with trace.span("smart.run"):
323
+ outcome = agent.run(request, initial=handoff) if handoff is not None else agent.run(request)
324
+ recording.finish(outcome)
325
+ report = markdown = None
326
+ if session is not None:
327
+ try:
328
+ session.capture_contract(getattr(agent, "_contract", None), final=True)
329
+ except Exception: # receipt persistence cannot prevent phone release or report synthesis
330
+ try:
331
+ run.emit({"event": "longform_receipt_unavailable"})
332
+ except Exception:
333
+ pass
334
+ # Synthesis needs no phone, so the phone is free before the report is written.
335
+ setup.finish_phone()
336
+ session.release_phone()
337
+ events.finish()
338
+ run.private = {"notes": list(outcome.get("notes") or ()), "plan": outcome.get("plan") or "",
339
+ "contract": [{name: getattr(item, name, "") for name in (
340
+ "kind", "app", "what", "payload", "act", "count", "condition", "quote")}
341
+ for item in (getattr(getattr(agent, "_contract", None), "items", None) or ())],
342
+ # Hashes with no text: a replay that ends on this screen with its typed words is done.
343
+ "screen": replay_path.screen_hashes(getattr(agent, "last_screen", None))}
344
+ if run.stop.is_set() and outcome.get("status") not in ("approval_denied", "approval_timeout"):
345
+ outcome = {**outcome, "status": "stopped"}
346
+ if session is not None:
347
+ from .longform.synthesis import synthesize
348
+ with trace.span("longform.report"):
349
+ report, markdown = synthesize(session, outcome.get("status") or "error",
350
+ answer=outcome.get("answer"))
351
+ result = smart_result.summarize(outcome, agent, events, request=request, output_format=run.output_format,
352
+ output_schema=run.output_schema, cost_usd=run.cost_usd, report=report,
353
+ markdown=markdown)
354
+ blocks = result_blocks.from_report(report) if report is not None else None
355
+ if blocks is not None:
356
+ result["blocks"] = blocks
357
+ if report is not None and report.get("partial"):
358
+ from .longform.report import partial_line
359
+ result["reason"] = partial_line(report, session.budget) or result.get("reason")
360
+ self.learn_reach(key, client, outcome.get("reason"), result.get("structure_error"))
361
+ result["costUsd"] = run.cost_usd
362
+ self.note_limit(run, result)
363
+ run.emit(result)
364
+ return result
365
+ finally:
366
+ recording.finish()
367
+ if session is not None:
368
+ setup.finish_phone()
369
+ session.release_phone()
370
+ session.close()
371
+ engines.release_client(client)
372
+
373
+ def thread_rules(self, ctx):
374
+ thread_id = getattr(ctx, "thread_id", None)
375
+ if not thread_id:
376
+ return ()
377
+ try:
378
+ from .threads.service import service_for
379
+ service = service_for(self)
380
+ thread = service.store.get(thread_id) if service is not None else None
381
+ return tuple(str(rule) for rule in (thread or {}).get("rules") or () if str(rule).strip())[:12]
382
+ except Exception: # noqa: BLE001
383
+ return ()
384
+
385
+ def earlier_seen(self, ctx, card):
386
+ if (card or {}).get("kind") not in ("feed", "browse", "survey"):
387
+ return set()
388
+ try:
389
+ from .longform.recall import earlier_identities
390
+ return earlier_identities(self, getattr(ctx, "thread_id", None), ctx.run_id, card.get("kind"))
391
+ except Exception: # noqa: BLE001
392
+ return set()
393
+
394
+ def agents_status(self, max_age=60.0, wait=False):
395
+ from .ai_plans import agents
396
+ return agents.snapshot(max_age=max_age, wait=wait)
397
+
398
+ def plan_agent(self):
399
+ if self.scripted_models() or engines.smart_key():
400
+ return None
401
+ from .ai_plans import agents
402
+ return agents.ready()
403
+
404
+ def check_agent(self, engine):
405
+ from .ai_plans import agents
406
+ agent = agents.get(engine)
407
+ state = agent.status(max_age=30)
408
+ if not state.get("ready"):
409
+ agent.forget_status()
410
+ state = agent.status(max_age=0)
411
+ if not state.get("ready"):
412
+ reason = (f"{agent.name} isn't installed on this Mac." if not state.get("installed")
413
+ else agent.signed_out_sentence())
414
+ raise APIError(reason, 409, "engine_unavailable", engine=engine,
415
+ agentState="not_installed" if not state.get("installed") else "signed_out")
416
+
417
+ def run_agent(self, run, trace, setup):
418
+ from .ai_plans import runner
419
+ return runner.run(self, run, trace, setup)
420
+
421
+ def learn_reach(self, key, client, *errors):
422
+ reach = getattr(self, "model_reach", None)
423
+ if reach is None:
424
+ return
425
+ for error in errors:
426
+ ok, why = engines.model_failure(error)
427
+ if ok is False:
428
+ reach.record(key, False, why)
429
+ return
430
+ if (getattr(client, "usage", None) or {}).get("calls") and not getattr(client, "model_failed", True):
431
+ reach.record(key, True)
432
+
433
+ def work(self, run):
434
+ driver = None
435
+ trace = Trace()
436
+ phase = "preparing"
437
+ result = {"status": "stopped", "independently_verified": False,
438
+ "reason": "The task stopped before executing agent actions."}
439
+
440
+ def set_phase(value):
441
+ nonlocal phase
442
+ phase = value
443
+
444
+ def hold(value):
445
+ nonlocal driver
446
+ driver = value
447
+ return value
448
+
449
+ def finish_phone():
450
+ nonlocal driver
451
+ if run.phone_finished:
452
+ return
453
+ # Drain a concurrent preview before closing its driver. Never capture after the lease moves.
454
+ if driver and not run.stop.is_set() and not self.closing:
455
+ with trace.span("finish.preview"):
456
+ self.preview(run, final=True)
457
+ with run.image_lock:
458
+ run.phone_finished = True
459
+ run.capture = None
460
+ if run.guard is not None and driver is not None:
461
+ try:
462
+ run.guard.finish(driver)
463
+ except Exception:
464
+ pass
465
+ with trace.span("finish.close"):
466
+ close_all(driver)
467
+ driver = None
468
+ run.guard = None
469
+ if run.lease:
470
+ run.lease.close()
471
+ run.lease = None
472
+ with self.lock:
473
+ self.active_runs.pop(run.id, None)
474
+ # Admission also holds this lock; end the old tracker state before a new run can start.
475
+ wda_watchdog.tracker.run_ended(self.run_phone_url(run), run.id)
476
+ try:
477
+ self.run_ended(run)
478
+ except Exception: # queue notification cannot change a task's completed phone work or report
479
+ harness_api.log.exception("phone released but its queue could not advance")
480
+
481
+ try:
482
+ if run.stop.is_set():
483
+ return
484
+ run.status = "running"
485
+ run.started_at = time.time()
486
+ self.power.run_started(run)
487
+ run.emit({"event": "run_started", "mode": run.mode, "app": run.app["name"], "engine": run.engine})
488
+ if ai_plans.is_agent(run.engine):
489
+ done = self.run_agent(run, trace, RunSetup(phase=set_phase, hold=hold,
490
+ finish_phone=finish_phone))
491
+ if done is not None:
492
+ result = done
493
+ return
494
+ done = self.run_smart(run, trace, RunSetup(phase=set_phase, hold=hold,
495
+ finish_phone=finish_phone))
496
+ if done is not None:
497
+ result = done
498
+ except Exception as exc:
499
+ inactive_app = phase == "reading_app" and isinstance(exc, DriverRejection) and exc.code == "app_not_active"
500
+ outdated_bridge = (phase == "reading_app" and isinstance(exc, DriverRejection)
501
+ and exc.code == "bridge_update_required")
502
+ result = {"event": "result", "status": "stopped" if run.stop.is_set() else "error",
503
+ "independently_verified": False, "error_type": type(exc).__name__,
504
+ "native_code": exc.code if isinstance(exc, DriverRejection) else None,
505
+ "actions_may_have_run": phase in {"running", "launching"},
506
+ "reason": ("The task was stopped while preparing the app."
507
+ if run.stop.is_set() and phase == "preparing" else
508
+ str(exc) if isinstance(exc, PhoneNotReady) else
509
+ "The app launch could not be confirmed. It may have opened; the launch was not retried."
510
+ if phase == "launching" else
511
+ (f"iOS has not activated {run.app['name']}. Its accessibility bridge is responding, "
512
+ "but the app is inactive. No agent action was dispatched.") if inactive_app else
513
+ (f"{run.app['name']} is still running an outdated interaction bridge. Close and reopen "
514
+ "the app to load the installed update. No agent action was dispatched.")
515
+ if outdated_bridge else
516
+ (f"Could not read {run.app['name']} on your iPhone. Check for a locked screen or a "
517
+ "system prompt, then try again. No agent action was dispatched.")
518
+ if phase == "reading_app" else
519
+ "The task could not prepare your app. No agent action was dispatched."
520
+ if phase == "preparing" else
521
+ "The task stopped after a service error. An in-flight action may have run; "
522
+ "it was not retried.")}
523
+ if isinstance(exc, PhoneNotReady) and not run.stop.is_set():
524
+ result["stop_code"] = exc.code # phone_locked, unplugged ... (agent_hooks.STOP_CODES)
525
+ elif not run.phone_finished and not run.stop.is_set() and phase in {"running", "launching"}:
526
+ # A step failed outside the loop's guard: ask the phone once whether it was unplugged or locked.
527
+ try:
528
+ verdict = after_error(run.guard, driver)
529
+ except Exception: # noqa: BLE001 -- the service-error sentence stands
530
+ verdict = None
531
+ if verdict is not None:
532
+ result.update(reason=verdict.message, stop_code=verdict.code)
533
+ result.update(engine=run.engine, costUsd=run.cost_usd,
534
+ outcome="stopped" if run.stop.is_set() else "couldnt_finish", answer=None, proof=[])
535
+ finally:
536
+ self.awake.release(run.id)
537
+ self.power.run_finished(run)
538
+ finish_phone()
539
+ try:
540
+ run.emit(trace.event())
541
+ except Exception:
542
+ pass # Telemetry never changes a run's outcome.
543
+ try:
544
+ run.finish(result)
545
+ finally:
546
+ with self.lock:
547
+ self.threads.discard(threading.current_thread())
548
+ self.release(run)
549
+ try:
550
+ self.finished(run)
551
+ except Exception: # noqa: BLE001 -- listeners and the bus never change a run's outcome
552
+ harness_api.log.exception("run_finished listeners failed")
553
+ run.settled.set()
554
+
555
+ def release(self, run):
556
+ # Otherwise every run kept its driver's screens and trees until it left the 100-run history.
557
+ run.capture = None
558
+ run.guard = None
559
+ with self.lock:
560
+ shown = sorted((r for r in self.runs.values() if r.finished_at is not None and r.image is not None),
561
+ key=lambda r: r.finished_at, reverse=True)
562
+ for old in shown[KEPT_PREVIEWS:]:
563
+ if old.image_lock.acquire(blocking=False): # held only while a capture is in flight
564
+ try:
565
+ old.image, old.image_captured_at = None, None
566
+ finally:
567
+ old.image_lock.release()
568
+
569
+ def finished(self, run):
570
+ # Listeners' on_finished, then the bus's run_finished, in that order, on the listeners' thread.
571
+ listeners = getattr(run, "listeners", None)
572
+ thread_id = (run.extras or {}).get("threadId") if isinstance(getattr(run, "extras", None), dict) else None
573
+ event = {"event": "run_finished", "runId": run.id, "status": run.status,
574
+ **({"threadId": thread_id} if thread_id else {})}
575
+ fanout = getattr(self, "fanout", None) if getattr(run, "group_id", None) else None
576
+ fleet = getattr(self, "fleet", None)
577
+ if fleet is not None and getattr(fleet, "health", None) is not None:
578
+ fleet.health.note_run(run)
579
+ if listeners is None:
580
+ self.publish("runs", event)
581
+ if fanout is not None:
582
+ fanout.member_finished(run)
583
+ run.settled.set()
584
+ return
585
+ listeners.post("on_finished", run, run.summary, dict(run.private or {}))
586
+ listeners.call(run.settled.set)
587
+ listeners.call(lambda: self.publish("runs", event))
588
+ if fanout is not None:
589
+ # After the conversation has the run's result: the group's summary reads every phone's.
590
+ listeners.call(lambda: fanout.member_finished(run))
591
+
592
+ def _finished_run(self, identifier, still_running=None):
593
+ with self.lock:
594
+ run = self.runs.get(identifier)
595
+ if not run:
596
+ raise APIError("Task not found", 404, "not_found")
597
+ if still_running and run.finished_at is None:
598
+ raise APIError(still_running, 409, "run_active")
599
+ return run