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,588 @@
1
+ """The dashboard's event stream from a Smart run: SmartEvents turns the loop's trace into the contract events the
2
+ app renders (docs/engine-contract.md), with the readable step text. Nothing here changes a decision the loop makes."""
3
+
4
+ import re
5
+ import threading
6
+
7
+ from . import harness_api
8
+ from .task_policy import ROW_STATE
9
+
10
+ # FrontierAgent events kept out of the run's history: the full prompt and screenshot of every turn is
11
+ # for offline replay (bench/replay.py), 20-60 KB each; the loop's own result is replaced by the server's.
12
+ DROPPED = frozenset({"frontier_prompt", "result"})
13
+ TOOL_BLOCKS = 6
14
+ TYPING = frozenset({"TYPE", "TYPE_SUBMIT", "SET_TEXT"})
15
+ # A press on a named control: the steps whose `commit` the engine can vouch for (frontier.safety.consequential).
16
+ PRESSES = frozenset({"TAP", "LONG_PRESS"})
17
+
18
+
19
+ SYMBOLS = {"circle": "the selection circle", "ellipsis": "More", "ellipsis.circle": "More", "checkmark": "Done",
20
+ "trash": "Delete", "trash.fill": "Delete", "square.and.pencil": "Compose"}
21
+ def readable(label):
22
+ label = ROW_STATE.sub("", " ".join(str(label or "").split()))
23
+ if label in SYMBOLS:
24
+ return SYMBOLS[label]
25
+ if label and " " not in label and re.fullmatch(r"[A-Za-z0-9_.-]+", label) and re.search(r"[_.]|[a-z][A-Z]", label):
26
+ label = re.sub(r"(?<=[a-z])(?=[A-Z])", " ", label).replace("_", " ").replace(".", " ").replace("-", " ")
27
+ label = " ".join(label.split()).lower()
28
+ return label
29
+
30
+
31
+ def shorten(text, limit):
32
+ text = " ".join(str(text or "").split())
33
+ return text if len(text) <= limit else text[:limit - 1].rstrip() + "…"
34
+
35
+
36
+ def in_quotes(text, limit=40):
37
+ return f"“{shorten(text, limit)}”"
38
+
39
+
40
+ def written_kind(label):
41
+ """What a composer holds, by its label: the comment, the reply, the post or (else) the message."""
42
+ folded = readable(label).casefold()
43
+ for word, kind in (("comment", "comment"), ("reply", "reply"), ("caption", "post"), ("tweet", "post"),
44
+ ("post", "post"), ("on your mind", "post")):
45
+ if word in folded:
46
+ return kind
47
+ return "message"
48
+
49
+
50
+ def step_text(op, label, apps=None, *, text=None, field=None, where=None, again=False):
51
+ label = readable(label) if op not in ("LAUNCH_APP",) else " ".join(str(label or "").split())
52
+ short = shorten(label, 60)
53
+ if op == "LAUNCH_APP":
54
+ return f"Opened {(apps or {}).get(label, label) or 'an app'}"
55
+ if op in ("DRAG_XY", "HOLD_XY", "FLICK_XY"):
56
+ verb = {"DRAG_XY": "Dragged", "HOLD_XY": "Held", "FLICK_XY": "Flicked"}[op]
57
+ return f"{verb} {short or 'the screen'}"
58
+ if op == "TAP_XY":
59
+ return f"Tapped {re.sub(r' at [0-9]+,[0-9]+$', '', short) or 'the screen'}"
60
+ if op in TYPING and text is not None:
61
+ submit = " and pressed Return" if op == "TYPE_SUBMIT" else ""
62
+ if op == "SET_TEXT" and not str(text).strip():
63
+ return f"Cleared the {written_kind(label)}" if field == "composer" else f"Cleared {short}" if short \
64
+ else "Cleared the field"
65
+ if field == "search":
66
+ return f"Searched for {in_quotes(text)}"
67
+ if field == "composer":
68
+ return f"{'Rewrote' if again else 'Wrote'} the {written_kind(label)}{submit}"
69
+ place = (f" in {short}"
70
+ if short and len(short) <= 30 and short.casefold() != " ".join(str(text).split()).casefold() else "")
71
+ return f"Typed {in_quotes(text)}{place}{submit}"
72
+ if op == "SCROLL_TO" and text:
73
+ return f"Scrolled to {shorten(text, 60)}"
74
+ if op == "READ_LIST" and where:
75
+ return f"Read the {shorten(where, 40)} list"
76
+ return {
77
+ "TAP": f"Tapped {short}" if short else "Tapped the screen",
78
+ "LONG_PRESS": f"Pressed and held {short}",
79
+ "TYPE": f"Typed in {short}" if short else "Typed text",
80
+ "TYPE_SUBMIT": f"Typed in {short} and pressed Return" if short else "Typed text and pressed Return",
81
+ "SET_TEXT": f"Entered text in {short}" if short else "Entered text",
82
+ "SUBMIT": "Pressed Return",
83
+ "SWIPE_UP": "Scrolled down", "SWIPE_DOWN": "Scrolled up",
84
+ "SWIPE_LEFT": f"Swiped left on {short}" if short else "Swiped left",
85
+ "SWIPE_RIGHT": f"Swiped right on {short}" if short else "Swiped right",
86
+ "DISMISS": "Closed a pop-up", "HOME": "Went to the Home Screen",
87
+ "READ_LIST": "Read the whole list", "SCROLL_TO": "Scrolled to the item",
88
+ "ASK_USER": "Asked you a question", "OPEN_URL": "Opened a link", "WATCH_FEED": "Watched the feed",
89
+ "WATCH": "Watched the screen",
90
+ "READ_EACH": "Read each item", "RECALL": "Looked back through earlier results",
91
+ }.get(op, op.replace("_", " ").capitalize())
92
+
93
+
94
+ def connector_step(call):
95
+ """A CALL_TOOL step in plain words, from its last connector_call event: "Used Linear: create_issue"."""
96
+ if not isinstance(call, dict) or not call.get("connector"):
97
+ return "Didn't use a connected service"
98
+ if call.get("status") == "searched":
99
+ return f"Looked for a connected tool: {shorten(str(call.get('arguments') or ''), 60)}"
100
+ name = " ".join(str(call["connector"]).split())
101
+ name = shorten(name, 40) + (f": {shorten(str(call['tool']), 60)}" if call.get("tool") else "")
102
+ return {"unknown": f"{name} didn't answer", "not_sent": f"Couldn't use {name}", "refused": f"Didn't use {name}",
103
+ "error": f"{name} returned an error"}.get(call.get("status"), f"Used {name}")
104
+
105
+
106
+ def item_state(contract, item):
107
+ """open | done | unproven, as the dashboard shows a contract item."""
108
+ if item.kind == "FORBID":
109
+ return "done"
110
+ if item.unproven and not contract.proof(item)[0]:
111
+ return "unproven"
112
+ if item.kind == "REPORT":
113
+ return "done" if item.status in ("found", "missing") else "open"
114
+ if item.kind == "COMMIT":
115
+ return "done" if item.status == "closed" or contract.proof(item)[0] else "open"
116
+ return "done" if contract.proof(item)[0] else "open"
117
+
118
+
119
+ ITEM_ACTS = {
120
+ "send_message": "Send the message", "reply": "Send the reply", "post": "Post it", "comment": "Post the comment",
121
+ "react": "React", "pay": "Pay", "request_money": "Request money", "transfer": "Make the transfer",
122
+ "order": "Place the order", "book": "Make the booking", "follow": "Follow", "unfollow": "Unfollow",
123
+ "share": "Share", "call": "Call", "delete": "Delete", "cancel": "Cancel", "save": "Save", "archive": "Archive",
124
+ "install": "Install"}
125
+
126
+
127
+ def item_text(item):
128
+ from . import contract_screen as KS
129
+ what = " ".join(str(item.what or "").split())
130
+ where = f" in {item.app}" if item.app and not what.casefold().endswith(f" in {item.app}".casefold()) else ""
131
+ if item.kind == "COMMIT":
132
+ who = KS.recipient(item)
133
+ verb = ITEM_ACTS.get(item.act)
134
+ if verb is None:
135
+ text = what[:1].upper() + what[1:]
136
+ elif item.act in ("send_message", "reply", "transfer"):
137
+ text = verb + (f" to {who}" if who else "")
138
+ elif item.act in ("pay", "follow", "unfollow", "call"):
139
+ text = f"{verb} {who or what}"
140
+ elif item.act == "request_money":
141
+ text = verb + (f" from {who}" if who else "")
142
+ elif item.act in ("order", "book", "post", "comment", "react"):
143
+ text = verb if item.act != "react" else f"React to {what}"
144
+ else:
145
+ text = f"{verb} {what}" if what else verb
146
+ if item.condition:
147
+ text += f" if {item.condition}"
148
+ return text + where
149
+ if item.kind == "WRITE":
150
+ return (f"Enter {in_quotes(item.payload, 60)}" if item.payload else f"Write {what}") + where
151
+ if item.kind == "READ":
152
+ return f"Look at {what}" + where
153
+ if item.kind == "REPORT":
154
+ return f"Find {what}" + where
155
+ verb = ITEM_ACTS.get(item.act)
156
+ return f"Don't {verb.split()[0].lower()} {what}" if verb else f"Don't {what}"
157
+
158
+
159
+ class SmartEvents:
160
+
161
+ def __init__(self, emit, driver=None, apps=None, frame=None, ledger=None):
162
+ self.out, self.driver, self.apps = emit, driver, dict(apps or {})
163
+ # A long task's SpendLedger: its cost events report the run's whole spend (every metered call), not only
164
+ # the navigation client's.
165
+ self.ledger = ledger
166
+ self.agent = None
167
+ self.frame = frame or self._video_frame
168
+ self.steps = 0
169
+ self.planned = False
170
+ self.states = {}
171
+ self.receipts_seen = 0
172
+ self.reports = {}
173
+ self.proof = {}
174
+ self.write_keys = {}
175
+ self.stale = set() # WRITE items whose quoted text was typed over and not read back again
176
+ self.typed_at = {} # index in contract.typed -> the first contract.screens index after it
177
+ self.held = None
178
+ self.ops = []
179
+ self.written = set()
180
+ self.declined = set() # texts the user declined at an approval (folded): never proof
181
+ self.cost_usd = None
182
+ self.watched = None
183
+ # A tool's own words for its next step (``tool_step`` {op, text}: "Opened your Instagram DMs"), by op.
184
+ self.tool_steps = {}
185
+ # Result cards tools built from what they read (``result_blocks`` {op, blocks}), newest last.
186
+ self.tool_blocks = []
187
+ self.lock = threading.Lock()
188
+
189
+ def _video_frame(self):
190
+ from .frontier.screen import video_frame
191
+ return video_frame(self.driver) if self.driver is not None else None
192
+
193
+ def _grab(self):
194
+ try:
195
+ data = self.frame()
196
+ except Exception:
197
+ data = None
198
+ return bytes(data) if isinstance(data, (bytes, bytearray)) and data else None
199
+
200
+ def _with_frame(self, event, data=None):
201
+ data = data if data is not None else self._grab()
202
+ if data:
203
+ event["_frame"] = data
204
+ return event
205
+
206
+ def _private(self):
207
+ """Where the last action's screen shows fields and messages, as frames (never their text); None when unknown."""
208
+ screen = getattr(self.agent, "last_screen", None)
209
+ if screen is None:
210
+ return None
211
+ private_rects = harness_api.hooks().private_rects
212
+ try:
213
+ return private_rects(screen.elements, getattr(screen, "bundle_id", None))
214
+ except Exception:
215
+ return None
216
+
217
+ @property
218
+ def contract(self):
219
+ return getattr(self.agent, "_contract", None)
220
+
221
+ def __call__(self, event):
222
+ kind = event.get("event")
223
+ with self.lock:
224
+ # WATCH's timeline is kept once, in its step (below), not in the event as well.
225
+ media = event.pop("media", None) if kind == "watched" else None
226
+ if kind not in DROPPED:
227
+ self.out(event)
228
+ if kind in ("frontier_contract", "frontier_checklist") and not self.planned:
229
+ self._plan()
230
+ elif kind == "frontier_decision":
231
+ self.ops = list(event.get("ops") or ())
232
+ held = self.held
233
+ if held is not None and not (event.get("operation") in TYPING
234
+ and event.get("target_label") == held["label"]):
235
+ self._flush()
236
+ self._cost()
237
+ self._thought(event)
238
+ elif kind == "tool_step" and isinstance(event.get("op"), str) and isinstance(event.get("text"), str):
239
+ self.tool_steps[event["op"]] = shorten(" ".join(event["text"].split()), 120)
240
+ elif kind == "result_blocks" and isinstance(event.get("blocks"), list):
241
+ self.tool_blocks = [*self.tool_blocks, *event["blocks"]][-TOOL_BLOCKS:]
242
+ elif kind == "watched" and event.get("ok"):
243
+ # After out(): the server put the contact sheet beside the run and left its id here.
244
+ self.watched = {"text": event.get("text"), "frameId": event.get("frameId"), "media": media}
245
+ elif kind == "connector_call":
246
+ self.connector_call = event
247
+ if event.get("status") == "done" and event.get("excerpt"):
248
+ # What a connected service answered is proof as a screen is: quoted, with where it came from.
249
+ self.tool_proof = [*getattr(self, "tool_proof", [])[-2:], {
250
+ "quote": shorten(str(event["excerpt"]), 300), "app": event.get("connector"),
251
+ "screen": event.get("tool"), "frameId": None}]
252
+ elif kind == "frontier_action":
253
+ self._action(event)
254
+ elif kind == "system_dialog_shown":
255
+ self.dialog_frame = self._grab() # the prompt itself, before anything is pressed
256
+ elif kind == "system_dialog" and event.get("ok") and event.get("button"):
257
+ # iOS's own prompt, answered: a step in the thread, with the frame that shows the prompt.
258
+ from .wda.system_dialogs import thread_text
259
+ self._flush()
260
+ frame, self.dialog_frame = getattr(self, "dialog_frame", None), None
261
+ self._step(thread_text(event.get("kind"), event.get("app"), event["button"], event.get("by")),
262
+ event.get("step"), frame)
263
+ if kind in ("frontier_decision", "frontier_action", "frontier_contract_update", "frontier_failed",
264
+ "frontier_chunk_stop"):
265
+ self._receipts(event.get("step"))
266
+
267
+ def _thought(self, event):
268
+ """The turn's ``thought``: the model's own sentence, secrets masked, shown and never sent to a model again."""
269
+ if event.get("source") == "program":
270
+ return
271
+ from .secret_filter import redact
272
+ text = event.get("thought")
273
+ text = " ".join(redact(text).split()) if isinstance(text, str) else ""
274
+ # The model names elements by the short ids it was shown ("e32"); a person reads "on screen".
275
+ text = re.sub(r"\s*\((?:e\d{1,4}(?:,\s*)?)+\)", "", text)
276
+ text = re.sub(r"\b(?:(?:in|at|on) )?(?:the )?(?:element|button|field|row) e\d{1,4}\b", "on screen", text)
277
+ text = re.sub(r"\be\d{1,4}\b", "it", text)[:300]
278
+ ms = event.get("latency_ms")
279
+ self.out({"event": "thought", "step": event.get("step"), "text": text or None,
280
+ "ms": round(ms) if isinstance(ms, (int, float)) and ms >= 0 else None})
281
+
282
+ def inference(self, event):
283
+ """Where ``meter`` sends its events (the same stream)."""
284
+ with self.lock:
285
+ self.out(event)
286
+
287
+ def approving(self, approve):
288
+ """``approve`` for FrontierAgent: a held typing step is shown before the user is asked."""
289
+ def ask(request):
290
+ from . import contract_screen as KS
291
+ with self.lock:
292
+ self._flush()
293
+ answer = approve(request)
294
+ if answer != "approved" and request.get("text"):
295
+ with self.lock:
296
+ self.declined.add(KS.plain(request["text"]))
297
+ return answer
298
+ return ask
299
+
300
+ def _step(self, text, step, data=None, private=None, focus=None, commit=None, changed=None):
301
+ self.steps += 1
302
+ event = self._with_frame({"event": "step", "n": self.steps, "step": step, "text": text}, data)
303
+ # Left out when unknown, which the app reads as "may commit" and "may not have changed".
304
+ if isinstance(commit, bool):
305
+ event["commit"] = commit
306
+ if isinstance(changed, bool):
307
+ event["changed"] = changed
308
+ if focus and "_frame" in event:
309
+ event["focus"] = focus # where the element it acted on sits in that frame, as fractions: data only
310
+ if "_frame" in event:
311
+ private = private if private is not None else self._private()
312
+ if private is not None:
313
+ event["redact"] = private
314
+ self.out(event)
315
+
316
+ def _action(self, event):
317
+ op, label = event.get("operation"), event.get("target_label")
318
+ contract = self.contract
319
+ if op in TYPING and contract is not None and contract.typed:
320
+ self.typed_at.setdefault(len(contract.typed) - 1, max(0, len(contract.screens) - 1))
321
+ if op in TYPING:
322
+ if self.held is not None and self.held["label"] == label:
323
+ self.held.update(op=op, text=event.get("text"), step=event.get("step"), frame=self._grab(),
324
+ private=self._private(), field=event.get("field") or self.held["field"],
325
+ focus=event.get("element_rect"), changed=self.held["changed"] or event.get("changed"))
326
+ else:
327
+ self._flush()
328
+ self.held = {"op": op, "label": label, "text": event.get("text"), "field": event.get("field"),
329
+ "step": event.get("step"), "frame": self._grab(), "private": self._private(),
330
+ "focus": event.get("element_rect"), "changed": event.get("changed")}
331
+ index = event.get("index")
332
+ following = self.ops[index + 1] if isinstance(index, int) and index + 1 < len(self.ops) else None
333
+ if following is not None and following not in TYPING:
334
+ self._flush() # the chain goes on to something else: nothing to merge
335
+ return
336
+ self._flush()
337
+ if op == "WATCH" and self.watched is not None:
338
+ # The step shows the frames it kept (one picture) and keeps the timeline it read as evidence.
339
+ watched, self.watched = self.watched, None
340
+ self.steps += 1
341
+ step = {"event": "step", "n": self.steps, "step": event.get("step"),
342
+ "text": watched.get("text") or step_text(op, label, self.apps)}
343
+ if watched.get("frameId"):
344
+ step["frameId"], step["sheet"] = watched["frameId"], True
345
+ if isinstance(watched.get("media"), dict):
346
+ step["media"] = watched["media"]
347
+ self.out(step)
348
+ return
349
+ if op == "CALL_TOOL":
350
+ # No phone picture: the step happened at a connected service, not on the screen.
351
+ self._step(connector_step(getattr(self, "connector_call", None)), event.get("step"), data=b"")
352
+ self.connector_call = None
353
+ return
354
+ said = self.tool_steps.pop(op, None)
355
+ if said:
356
+ # A tool said what its step did ("Read 10 DMs in Instagram"): that is the step's line.
357
+ self._step(said, event.get("step"))
358
+ return
359
+ where = None
360
+ if op == "READ_LIST":
361
+ shown = contract.shown[-1] if contract is not None and contract.shown else None
362
+ where = (shown[2] if shown and shown[2] else None) or event.get("app_name")
363
+ self._step(step_text(op, label, self.apps, text=event.get("text"), where=where), event.get("step"),
364
+ focus=event.get("element_rect"), commit=event.get("consequential") if op in PRESSES else None,
365
+ changed=event.get("changed"))
366
+
367
+ def _flush(self):
368
+ held, self.held = self.held, None
369
+ if held is None:
370
+ return
371
+ again = held["field"] == "composer" and held["label"] in self.written
372
+ if held["field"] == "composer":
373
+ self.written.add(held["label"])
374
+ self._step(step_text(held["op"], held["label"], self.apps, text=held["text"], field=held["field"],
375
+ again=again), held["step"], held["frame"], held.get("private"), held.get("focus"),
376
+ changed=held.get("changed"))
377
+
378
+ def _plan(self):
379
+ from . import contract_asks as proof_asks
380
+ self.planned = True
381
+ contract = self.contract
382
+ apps = []
383
+ for item in (contract.items if contract else ()):
384
+ if item.app and item.app not in apps:
385
+ apps.append(item.app)
386
+ self.out({"event": "plan", "line": proof_asks.plan_line(contract), "apps": apps,
387
+ "commits": proof_asks.plan_commits(contract)})
388
+ for item in (contract.items if contract else ()):
389
+ state = item_state(contract, item)
390
+ self.states[item.id] = state
391
+ self.out({"event": "contract_item", "id": item.id, "kind": item.kind.lower(), "label": item.label(),
392
+ "text": item_text(item), "state": state})
393
+
394
+ def _cost(self):
395
+ from .model_clients import cost_usd
396
+ client = getattr(self.agent, "client", None)
397
+ usage = getattr(client, "usage", None)
398
+ if not usage:
399
+ return
400
+ usd = cost_usd(getattr(client, "model", ""), usage)
401
+ if self.ledger is not None:
402
+ from .costs import NANODOLLARS
403
+ usd = round(self.ledger.cost_nanodollars / NANODOLLARS, 6)
404
+ self.cost_usd = usd
405
+ self.out({"event": "cost", "usd": usd, "calls": usage.get("calls", 0)})
406
+
407
+ def _receipts(self, step):
408
+ from . import contract_screen as KS
409
+ contract = self.contract
410
+ if contract is None:
411
+ return
412
+ by_id = {item.id: item for item in contract.items}
413
+ fresh = contract.events[self.receipts_seen:]
414
+ self.receipts_seen = len(contract.events)
415
+ for entry in fresh:
416
+ item = by_id.get(entry.get("item"))
417
+ if entry.get("event") != "receipt" or item is None:
418
+ continue
419
+ self._receipt(item, self._commit_quote(contract, item, entry), entry.get("step", step),
420
+ screen=entry.get("screen"))
421
+ for item in contract.items:
422
+ if (item.kind == "REPORT" and item.status == "found" and item.value
423
+ and self.reports.get(item.id) != item.value):
424
+ self.reports[item.id] = item.value
425
+ row, screen = self._report_row(contract, item)
426
+ label = None
427
+ if row and KS.plain(row).endswith(KS.plain(item.value)) and len(row) > len(item.value):
428
+ label = row[:len(row) - len(item.value)].strip(" :,-–—")
429
+ # "iOS Version, 26.4" is how VoiceOver reads the row; the screen shows "iOS Version 26.4"
430
+ row = f"{label} {row[len(row) - len(item.value):]}" if label else row
431
+ self._flush()
432
+ taken = item.value_step if item.value_step is not None else step
433
+ data = self._grab()
434
+ self._step(f"Read {shorten(label or item.what, 40)}: {shorten(item.value, 60)}", taken, data)
435
+ self._receipt(item, row or item.value, taken, screen=screen, data=data)
436
+ self._writes(contract)
437
+ self._states()
438
+
439
+ def _commit_quote(self, contract, item, entry):
440
+ from . import contract_screen as KS
441
+ payload = " ".join(str(entry.get("payload") or "").split())
442
+ if item.act in KS.MESSAGE_ACTS and payload:
443
+ return payload
444
+ if entry.get("shown"):
445
+ return entry["shown"]
446
+ receipt = str(entry.get("receipt") or "")
447
+ if receipt == "screen changed":
448
+ last = contract.shown[-1][3] if contract.shown else {}
449
+ for typed in reversed(contract.typed):
450
+ if typed["search"] or typed.get("picker") or not contract._app_ok(item, typed["app"]):
451
+ continue
452
+ key = KS.plain(next((line for line in typed["text"].split("\n") if line.strip()), ""))[:40]
453
+ if key and any(KS.shows(key, text) for text in last):
454
+ return " ".join(typed["text"].split())
455
+ return item.payload or item.what or (item.receipts[-1]["label"] if item.receipts else "")
456
+ return re.sub(r"^(sent|shown): ", "", receipt)
457
+
458
+ @staticmethod
459
+ def _report_row(contract, item):
460
+ from . import contract_screen as KS
461
+ value = KS.plain(item.value)
462
+ if not value:
463
+ return None, None
464
+ # The value as a whole word: "on" is not in "chevron.forward" (B6, bench/state.bluetooth), and an SF
465
+ # Symbol's name is never a quote.
466
+ whole = re.compile(r"(?<![\w])" + re.escape(value) + r"(?![\w])")
467
+ for (step, _, _, _), (_, _, title, originals) in reversed(list(zip(contract.screens, contract.shown))):
468
+ if item.value_step is not None and step > item.value_step:
469
+ continue
470
+ for text, original in originals.items():
471
+ if whole.search(text) and len(original) <= 80 and not KS.SF_SYMBOL.fullmatch(original.strip()):
472
+ return original, title
473
+ return None, None
474
+
475
+ def _writes(self, contract):
476
+ """WRITE receipts: the latest text typed, once shown outside its field; a compose-box draft proves nothing."""
477
+ from . import contract_screen as KS
478
+ for item in contract.items:
479
+ if item.kind != "WRITE":
480
+ continue
481
+ typed = [(index, t) for index, t in enumerate(contract.typed) if not t["search"] and not t.get("picker")
482
+ and contract._app_ok(item, t["app"])
483
+ and (not item.payload or KS.overlaps(item.payload, t["text"]))]
484
+ if not typed:
485
+ continue
486
+ index, latest = typed[-1]
487
+ key = KS.plain(next((line for line in latest["text"].split("\n") if line.strip()), ""))[:40]
488
+ if not key:
489
+ continue
490
+ if self.write_keys.get(item.id) == key:
491
+ self.stale.discard(item.id)
492
+ continue
493
+ found = self._read_back(contract, item, latest, key, self.typed_at.get(index))
494
+ if found is None:
495
+ if item.id in self.write_keys:
496
+ self.stale.add(item.id)
497
+ continue
498
+ quote, screen = found
499
+ self.write_keys[item.id] = key
500
+ self.stale.discard(item.id)
501
+ self._receipt(item, quote, latest["step"], screen=screen)
502
+
503
+ @staticmethod
504
+ def _read_back(contract, item, typed, key, since):
505
+ from . import contract_screen as KS
506
+ field = KS.plain(readable(typed.get("field") or ""))
507
+ composer = bool(KS.COMPOSER.search(typed.get("field") or "") or KS.COMPOSER.search(field))
508
+
509
+ def showing(rows, keyboard):
510
+ return [text for text, editable, _ in rows if key in text
511
+ and not (editable and (keyboard or composer or field and text.startswith(field + " ")))]
512
+
513
+ pairs = list(zip(contract.screens, contract.shown))
514
+ first = since if since is not None else 0
515
+ before = pairs[first - 1] if first > 0 else None
516
+ baseline = len(showing(before[0][2], before[0][3])) if before and before[0][1] == (
517
+ pairs[first][0][1] if first < len(pairs) else None) else 0
518
+ for position in range(len(pairs) - 1, first - 1, -1):
519
+ (step, app, rows, keyboard), (_, _, title, originals) = pairs[position]
520
+ if since is None and step < typed["step"] or not contract._app_ok(item, app):
521
+ continue
522
+ shown = showing(rows, keyboard)
523
+ if len(shown) <= baseline:
524
+ continue
525
+ original = originals.get(shown[-1]) or typed["text"]
526
+ same = KS.plain(original) == KS.plain(typed["text"])
527
+ return (original if same else " ".join(typed["text"].split())), title
528
+ return None
529
+
530
+ def _receipt(self, item, quote, step, *, screen=None, data=None):
531
+ quote = shorten(quote, 300)
532
+ if not quote:
533
+ return
534
+ event = self._with_frame({"event": "receipt", "itemId": item.id, "kind": item.kind.lower(), "quote": quote,
535
+ "app": item.app or None, "screen": screen or None, "step": step}, data)
536
+ self.out(event)
537
+ self.proof.pop(item.id, None) # the latest receipt for an item replaces the one before
538
+ self.proof[item.id] = {"itemId": item.id, "kind": item.kind, "quote": quote, "app": event["app"],
539
+ "screen": event["screen"], "frameId": event.get("frameId"),
540
+ **self._commit_fields(item)}
541
+
542
+ @staticmethod
543
+ def _commit_fields(item):
544
+ from . import contract_screen as KS
545
+ if item.kind != "COMMIT" or not item.act or item.act == "none":
546
+ return {}
547
+ who = KS.recipient(item)
548
+ return {"act": item.act, **({"to": who} if who else {})}
549
+
550
+ def _states(self):
551
+ contract = self.contract
552
+ for item in (contract.items if contract else ()):
553
+ state = item_state(contract, item)
554
+ if self.states.get(item.id) != state:
555
+ self.states[item.id] = state
556
+ self.out({"event": "contract_item", "id": item.id, "kind": item.kind.lower(), "label": item.label(),
557
+ "text": item_text(item), "state": state})
558
+
559
+ def final_proof(self):
560
+ from . import contract_screen as KS
561
+ entries = [e for e in self.proof.values() if e["quote"]]
562
+ commits = [e for e in entries if e["kind"] == "COMMIT"]
563
+ kept, quotes = [], set()
564
+ for entry in entries:
565
+ if KS.plain(entry["quote"]) in self.declined and entry["kind"] != "COMMIT":
566
+ continue # a draft the user turned down
567
+ if entry["kind"] == "WRITE" and (entry["itemId"] in self.stale or any(
568
+ (not c["app"] or not entry["app"] or KS.plain(c["app"]) == KS.plain(entry["app"]))
569
+ and KS.overlaps(c["quote"], entry["quote"]) for c in commits)):
570
+ continue
571
+ folded = KS.plain(entry["quote"])
572
+ if folded in quotes:
573
+ continue
574
+ quotes.add(folded)
575
+ kept.append({"quote": entry["quote"], "app": entry["app"], "screen": entry["screen"],
576
+ "frameId": entry.get("frameId"),
577
+ **{key: entry[key] for key in ("act", "to") if entry.get(key)}})
578
+ for entry in getattr(self, "tool_proof", ()):
579
+ if KS.plain(entry["quote"]) not in quotes:
580
+ quotes.add(KS.plain(entry["quote"]))
581
+ kept.append(dict(entry))
582
+ return kept[-12:]
583
+
584
+ def finish(self):
585
+ """Final item states (the DONE gate may have marked some unproven) and the last held step."""
586
+ with self.lock:
587
+ self._receipts(None)
588
+ self._flush()