android-midscene-automation 0.2.4 → 1.1.0

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 (525) hide show
  1. package/CHANGELOG.md +7 -0
  2. package/README.md +35 -0
  3. package/artemis-main/.dockerignore +15 -0
  4. package/artemis-main/.env.example +34 -0
  5. package/artemis-main/CONTRIBUTING.md +35 -0
  6. package/artemis-main/Dockerfile +68 -0
  7. package/artemis-main/LICENSE +202 -0
  8. package/artemis-main/Makefile +115 -0
  9. package/artemis-main/README.md +322 -0
  10. package/artemis-main/README_CN.md +320 -0
  11. package/artemis-main/apps/__init__.py +15 -0
  12. package/artemis-main/apps/admin_console/__init__.py +19 -0
  13. package/artemis-main/apps/admin_console/core/__init__.py +13 -0
  14. package/artemis-main/apps/admin_console/core/config.py +43 -0
  15. package/artemis-main/apps/admin_console/core/security.py +178 -0
  16. package/artemis-main/apps/admin_console/core/state.py +243 -0
  17. package/artemis-main/apps/admin_console/database/__init__.py +13 -0
  18. package/artemis-main/apps/admin_console/database/connection.py +60 -0
  19. package/artemis-main/apps/admin_console/database/repositories/__init__.py +13 -0
  20. package/artemis-main/apps/admin_console/database/repositories/session_repository.py +564 -0
  21. package/artemis-main/apps/admin_console/database/repositories/step_repository.py +512 -0
  22. package/artemis-main/apps/admin_console/database/repositories/trace_repository.py +133 -0
  23. package/artemis-main/apps/admin_console/index.html +3787 -0
  24. package/artemis-main/apps/admin_console/replay_manager.py +2585 -0
  25. package/artemis-main/apps/admin_console/routers/__init__.py +13 -0
  26. package/artemis-main/apps/admin_console/routers/media.py +198 -0
  27. package/artemis-main/apps/admin_console/routers/replay.py +91 -0
  28. package/artemis-main/apps/admin_console/routers/sessions.py +239 -0
  29. package/artemis-main/apps/admin_console/routers/steps.py +109 -0
  30. package/artemis-main/apps/admin_console/routers/stream.py +56 -0
  31. package/artemis-main/apps/admin_console/routers/system.py +761 -0
  32. package/artemis-main/apps/admin_console/routers/tasks.py +517 -0
  33. package/artemis-main/apps/admin_console/schemas/__init__.py +13 -0
  34. package/artemis-main/apps/admin_console/schemas/task_schema.py +47 -0
  35. package/artemis-main/apps/admin_console/server.py +508 -0
  36. package/artemis-main/apps/admin_console/services/__init__.py +13 -0
  37. package/artemis-main/apps/admin_console/services/device_stream_service.py +137 -0
  38. package/artemis-main/apps/admin_console/services/ipc_service.py +262 -0
  39. package/artemis-main/apps/admin_console/services/media_service.py +623 -0
  40. package/artemis-main/apps/admin_console/services/model_service.py +148 -0
  41. package/artemis-main/apps/admin_console/services/task_preset_catalog.py +514 -0
  42. package/artemis-main/apps/admin_console/services/task_queue_service.py +1719 -0
  43. package/artemis-main/apps/admin_console/services/worker_process_io.py +144 -0
  44. package/artemis-main/apps/showcase_ui/README.md +31 -0
  45. package/artemis-main/apps/showcase_ui/angular.json +132 -0
  46. package/artemis-main/apps/showcase_ui/package-lock.json +9472 -0
  47. package/artemis-main/apps/showcase_ui/package.json +39 -0
  48. package/artemis-main/apps/showcase_ui/proxy.conf.json +23 -0
  49. package/artemis-main/apps/showcase_ui/public/favicon.ico +0 -0
  50. package/artemis-main/apps/showcase_ui/public/logo.png +0 -0
  51. package/artemis-main/apps/showcase_ui/public/logo.svg +56 -0
  52. package/artemis-main/apps/showcase_ui/src/app/app.component.html +24 -0
  53. package/artemis-main/apps/showcase_ui/src/app/app.component.scss +49 -0
  54. package/artemis-main/apps/showcase_ui/src/app/app.component.spec.ts +42 -0
  55. package/artemis-main/apps/showcase_ui/src/app/app.component.ts +32 -0
  56. package/artemis-main/apps/showcase_ui/src/app/app.config.ts +29 -0
  57. package/artemis-main/apps/showcase_ui/src/app/app.routes.ts +27 -0
  58. package/artemis-main/apps/showcase_ui/src/app/components/agent-stream/agent-stream-startup.spec.ts +108 -0
  59. package/artemis-main/apps/showcase_ui/src/app/components/agent-stream/agent-stream.component.html +1436 -0
  60. package/artemis-main/apps/showcase_ui/src/app/components/agent-stream/agent-stream.component.scss +3973 -0
  61. package/artemis-main/apps/showcase_ui/src/app/components/agent-stream/agent-stream.component.ts +2235 -0
  62. package/artemis-main/apps/showcase_ui/src/app/components/chat-interface/chat-interface.component.html +380 -0
  63. package/artemis-main/apps/showcase_ui/src/app/components/chat-interface/chat-interface.component.scss +1171 -0
  64. package/artemis-main/apps/showcase_ui/src/app/components/chat-interface/chat-interface.component.ts +270 -0
  65. package/artemis-main/apps/showcase_ui/src/app/components/floating-video-player/floating-video-player.component.html +433 -0
  66. package/artemis-main/apps/showcase_ui/src/app/components/floating-video-player/floating-video-player.component.scss +987 -0
  67. package/artemis-main/apps/showcase_ui/src/app/components/floating-video-player/floating-video-player.component.ts +632 -0
  68. package/artemis-main/apps/showcase_ui/src/app/components/legacy-agent-stream/legacy-agent-stream.component.html +130 -0
  69. package/artemis-main/apps/showcase_ui/src/app/components/legacy-agent-stream/legacy-agent-stream.component.scss +406 -0
  70. package/artemis-main/apps/showcase_ui/src/app/components/legacy-agent-stream/legacy-agent-stream.component.ts +74 -0
  71. package/artemis-main/apps/showcase_ui/src/app/components/nav-switcher/nav-switcher.component.scss +115 -0
  72. package/artemis-main/apps/showcase_ui/src/app/components/nav-switcher/nav-switcher.component.ts +52 -0
  73. package/artemis-main/apps/showcase_ui/src/app/core/data/smart-tasks.data.ts +334 -0
  74. package/artemis-main/apps/showcase_ui/src/app/core/i18n/language.service.spec.ts +55 -0
  75. package/artemis-main/apps/showcase_ui/src/app/core/i18n/language.service.ts +61 -0
  76. package/artemis-main/apps/showcase_ui/src/app/core/i18n/translate.pipe.ts +11 -0
  77. package/artemis-main/apps/showcase_ui/src/app/core/i18n/zh-cn.ts +651 -0
  78. package/artemis-main/apps/showcase_ui/src/app/core/models/markdown.model.ts +43 -0
  79. package/artemis-main/apps/showcase_ui/src/app/core/models/pro-tuning.model.spec.ts +47 -0
  80. package/artemis-main/apps/showcase_ui/src/app/core/models/pro-tuning.model.ts +165 -0
  81. package/artemis-main/apps/showcase_ui/src/app/core/models/session.model.ts +85 -0
  82. package/artemis-main/apps/showcase_ui/src/app/core/models/stream.model.ts +246 -0
  83. package/artemis-main/apps/showcase_ui/src/app/core/models/system.model.ts +118 -0
  84. package/artemis-main/apps/showcase_ui/src/app/core/services/task-recommendation.service.ts +111 -0
  85. package/artemis-main/apps/showcase_ui/src/app/pages/home/home.component.html +2130 -0
  86. package/artemis-main/apps/showcase_ui/src/app/pages/home/home.component.scss +5204 -0
  87. package/artemis-main/apps/showcase_ui/src/app/pages/home/home.component.ts +1189 -0
  88. package/artemis-main/apps/showcase_ui/src/app/pages/legacy-workspace/legacy-workspace.component.html +39 -0
  89. package/artemis-main/apps/showcase_ui/src/app/pages/legacy-workspace/legacy-workspace.component.scss +151 -0
  90. package/artemis-main/apps/showcase_ui/src/app/pages/legacy-workspace/legacy-workspace.component.ts +58 -0
  91. package/artemis-main/apps/showcase_ui/src/app/pages/workspace/workspace.component.html +201 -0
  92. package/artemis-main/apps/showcase_ui/src/app/pages/workspace/workspace.component.scss +886 -0
  93. package/artemis-main/apps/showcase_ui/src/app/pages/workspace/workspace.component.ts +324 -0
  94. package/artemis-main/apps/showcase_ui/src/app/services/agent.service.spec.ts +418 -0
  95. package/artemis-main/apps/showcase_ui/src/app/services/agent.service.ts +2012 -0
  96. package/artemis-main/apps/showcase_ui/src/app/services/system.service.spec.ts +173 -0
  97. package/artemis-main/apps/showcase_ui/src/app/services/system.service.ts +620 -0
  98. package/artemis-main/apps/showcase_ui/src/app/utils/action-formatter.util.spec.ts +513 -0
  99. package/artemis-main/apps/showcase_ui/src/app/utils/action-formatter.util.ts +1015 -0
  100. package/artemis-main/apps/showcase_ui/src/app/utils/image-overlay.util.spec.ts +85 -0
  101. package/artemis-main/apps/showcase_ui/src/app/utils/image-overlay.util.ts +662 -0
  102. package/artemis-main/apps/showcase_ui/src/app/utils/markdown-parser.util.spec.ts +70 -0
  103. package/artemis-main/apps/showcase_ui/src/app/utils/markdown-parser.util.ts +383 -0
  104. package/artemis-main/apps/showcase_ui/src/app/utils/recording-timeline.util.spec.ts +107 -0
  105. package/artemis-main/apps/showcase_ui/src/app/utils/recording-timeline.util.ts +123 -0
  106. package/artemis-main/apps/showcase_ui/src/app/utils/run-info.util.spec.ts +92 -0
  107. package/artemis-main/apps/showcase_ui/src/app/utils/run-info.util.ts +116 -0
  108. package/artemis-main/apps/showcase_ui/src/app/utils/stream-aggregator.util.spec.ts +640 -0
  109. package/artemis-main/apps/showcase_ui/src/app/utils/stream-aggregator.util.ts +1021 -0
  110. package/artemis-main/apps/showcase_ui/src/app/utils/tool-formatter.util.spec.ts +375 -0
  111. package/artemis-main/apps/showcase_ui/src/app/utils/tool-formatter.util.ts +1419 -0
  112. package/artemis-main/apps/showcase_ui/src/index.html +37 -0
  113. package/artemis-main/apps/showcase_ui/src/main.ts +22 -0
  114. package/artemis-main/apps/showcase_ui/src/styles.scss +60 -0
  115. package/artemis-main/apps/showcase_ui/tsconfig.app.json +23 -0
  116. package/artemis-main/apps/showcase_ui/tsconfig.json +27 -0
  117. package/artemis-main/apps/showcase_ui/tsconfig.spec.json +23 -0
  118. package/artemis-main/artemis/__init__.py +44 -0
  119. package/artemis-main/artemis/__main__.py +18 -0
  120. package/artemis-main/artemis/_legacy_imports.py +94 -0
  121. package/artemis-main/artemis/_version.py +30 -0
  122. package/artemis-main/artemis/agents/__init__.py +17 -0
  123. package/artemis-main/artemis/agents/checker/checker.json +9 -0
  124. package/artemis-main/artemis/agents/checker/checker.py +859 -0
  125. package/artemis-main/artemis/agents/diagnoser/diagnoser.json +23 -0
  126. package/artemis-main/artemis/agents/diagnoser/diagnoser.py +608 -0
  127. package/artemis-main/artemis/agents/explorer/constants.py +52 -0
  128. package/artemis-main/artemis/agents/explorer/explorer.json +57 -0
  129. package/artemis-main/artemis/agents/explorer/explorer.py +473 -0
  130. package/artemis-main/artemis/agents/explorer/geometry.py +73 -0
  131. package/artemis-main/artemis/agents/explorer/native_runner.py +878 -0
  132. package/artemis-main/artemis/agents/explorer/perception_tools.py +632 -0
  133. package/artemis-main/artemis/agents/explorer/run_setup.py +403 -0
  134. package/artemis-main/artemis/agents/explorer/screen_index.py +266 -0
  135. package/artemis-main/artemis/agents/explorer/tiers.py +110 -0
  136. package/artemis-main/artemis/agents/explorer/tool_declarations.py +394 -0
  137. package/artemis-main/artemis/agents/explorer/universal_runner.py +317 -0
  138. package/artemis-main/artemis/agents/flash/context_compressor.py +452 -0
  139. package/artemis-main/artemis/agents/flash/flash_runner.md +51 -0
  140. package/artemis-main/artemis/agents/flash/flash_summarizer.md +53 -0
  141. package/artemis-main/artemis/agents/flash/flash_summarizer_single.md +54 -0
  142. package/artemis-main/artemis/agents/flash/runner.py +1167 -0
  143. package/artemis-main/artemis/agents/flash/summarizer.py +511 -0
  144. package/artemis-main/artemis/agents/history_analyzer/history_analyzer.md +8 -0
  145. package/artemis-main/artemis/agents/history_analyzer/history_analyzer.py +182 -0
  146. package/artemis-main/artemis/agents/image_processor/image_processor.md +74 -0
  147. package/artemis-main/artemis/agents/image_processor/image_processor.py +322 -0
  148. package/artemis-main/artemis/agents/log_analyzer/log_analyzer.md +15 -0
  149. package/artemis-main/artemis/agents/log_analyzer/log_analyzer.py +403 -0
  150. package/artemis-main/artemis/agents/log_analyzer/log_reader_sub_agent.md +8 -0
  151. package/artemis-main/artemis/agents/log_analyzer/output_analyzer.md +11 -0
  152. package/artemis-main/artemis/agents/log_analyzer/output_analyzer.py +163 -0
  153. package/artemis-main/artemis/agents/object_detector/object_detector.json +9 -0
  154. package/artemis-main/artemis/agents/object_detector/object_detector.py +211 -0
  155. package/artemis-main/artemis/agents/operator/operator.json +3 -0
  156. package/artemis-main/artemis/agents/operator/operator.py +1394 -0
  157. package/artemis-main/artemis/agents/operator/prompts.py +1064 -0
  158. package/artemis-main/artemis/agents/outputter/outputter.md +36 -0
  159. package/artemis-main/artemis/agents/outputter/outputter.py +552 -0
  160. package/artemis-main/artemis/agents/planner/planner.json +32 -0
  161. package/artemis-main/artemis/agents/planner/planner.py +436 -0
  162. package/artemis-main/artemis/agents/prompt_assembly.py +79 -0
  163. package/artemis-main/artemis/agents/summarizer/summarizer.py +190 -0
  164. package/artemis-main/artemis/agents/validator/action_execution.py +98 -0
  165. package/artemis-main/artemis/agents/validator/categories.py +33 -0
  166. package/artemis-main/artemis/agents/validator/execution_loop.py +488 -0
  167. package/artemis-main/artemis/agents/validator/incidents.py +120 -0
  168. package/artemis-main/artemis/agents/validator/pixel_safety_net.md +15 -0
  169. package/artemis-main/artemis/agents/validator/precondition_pixel.py +320 -0
  170. package/artemis-main/artemis/agents/validator/precondition_xml.py +672 -0
  171. package/artemis-main/artemis/agents/validator/tool_declarations.py +332 -0
  172. package/artemis-main/artemis/agents/validator/validator.py +188 -0
  173. package/artemis-main/artemis/agents/video_analyzer/__init__.py +15 -0
  174. package/artemis-main/artemis/agents/video_analyzer/audio_native.py +543 -0
  175. package/artemis-main/artemis/agents/video_analyzer/blackboard.py +821 -0
  176. package/artemis-main/artemis/agents/video_analyzer/chunk_agentic.py +455 -0
  177. package/artemis-main/artemis/agents/video_analyzer/chunk_conversation.py +491 -0
  178. package/artemis-main/artemis/agents/video_analyzer/chunk_native.py +437 -0
  179. package/artemis-main/artemis/agents/video_analyzer/conflict_resolution.py +137 -0
  180. package/artemis-main/artemis/agents/video_analyzer/gemini_files.py +162 -0
  181. package/artemis-main/artemis/agents/video_analyzer/native_coordinator.py +541 -0
  182. package/artemis-main/artemis/agents/video_analyzer/reliability.py +128 -0
  183. package/artemis-main/artemis/agents/video_analyzer/universal_engine.py +487 -0
  184. package/artemis-main/artemis/agents/video_analyzer/universal_tools.py +323 -0
  185. package/artemis-main/artemis/agents/video_analyzer/video_analyzer.md +51 -0
  186. package/artemis-main/artemis/agents/video_analyzer/video_analyzer.py +875 -0
  187. package/artemis-main/artemis/agents/video_analyzer/video_sub_agent.md +15 -0
  188. package/artemis-main/artemis/agents/video_analyzer/video_sub_agent_audio.md +12 -0
  189. package/artemis-main/artemis/clients/accessibility_client.py +438 -0
  190. package/artemis-main/artemis/clients/screen_client_factory.py +403 -0
  191. package/artemis-main/artemis/config/__init__.py +309 -0
  192. package/artemis-main/artemis/config/agent.py +1098 -0
  193. package/artemis-main/artemis/config/constants.py +151 -0
  194. package/artemis-main/artemis/config/core/__init__.py +24 -0
  195. package/artemis-main/artemis/config/core/engine.py +118 -0
  196. package/artemis-main/artemis/config/core/interpolator.py +78 -0
  197. package/artemis-main/artemis/config/llm.py +267 -0
  198. package/artemis-main/artemis/config/paths.py +244 -0
  199. package/artemis-main/artemis/config/runtime.py +197 -0
  200. package/artemis-main/artemis/config/settings.py +315 -0
  201. package/artemis-main/artemis/constants.py +47 -0
  202. package/artemis-main/artemis/context.py +289 -0
  203. package/artemis-main/artemis/controllers/__init__.py +41 -0
  204. package/artemis-main/artemis/controllers/controller_factory.py +41 -0
  205. package/artemis-main/artemis/controllers/device_controller.py +29 -0
  206. package/artemis-main/artemis/controllers/types.py +37 -0
  207. package/artemis-main/artemis/controllers/unified_controller.py +649 -0
  208. package/artemis-main/artemis/core/__init__.py +21 -0
  209. package/artemis-main/artemis/core/diagnostics/__init__.py +90 -0
  210. package/artemis-main/artemis/core/diagnostics/adb_keys.py +270 -0
  211. package/artemis-main/artemis/core/diagnostics/adb_server_connection.py +330 -0
  212. package/artemis-main/artemis/core/diagnostics/device_smoke.py +364 -0
  213. package/artemis-main/artemis/core/diagnostics/emulator_manager.py +501 -0
  214. package/artemis-main/artemis/core/diagnostics/engine.py +438 -0
  215. package/artemis-main/artemis/core/diagnostics/hierarchy_parity.py +216 -0
  216. package/artemis-main/artemis/core/diagnostics/probes/adb_probe.py +887 -0
  217. package/artemis-main/artemis/core/diagnostics/probes/base.py +45 -0
  218. package/artemis-main/artemis/core/diagnostics/probes/credentials_probe.py +296 -0
  219. package/artemis-main/artemis/core/diagnostics/probes/host_probe.py +427 -0
  220. package/artemis-main/artemis/core/diagnostics/probes/runtime_probe.py +169 -0
  221. package/artemis-main/artemis/core/diagnostics/probes/toolchain_probe.py +139 -0
  222. package/artemis-main/artemis/core/diagnostics/readiness.py +83 -0
  223. package/artemis-main/artemis/core/diagnostics/schema.py +118 -0
  224. package/artemis-main/artemis/core/tool_declaration.py +43 -0
  225. package/artemis-main/artemis/core/tool_failure.py +56 -0
  226. package/artemis-main/artemis/data_engine/context_vars.py +24 -0
  227. package/artemis-main/artemis/data_engine/engine.py +1615 -0
  228. package/artemis-main/artemis/data_engine/history_reader.py +142 -0
  229. package/artemis-main/artemis/data_engine/models.py +131 -0
  230. package/artemis-main/artemis/data_engine/storage.py +1245 -0
  231. package/artemis-main/artemis/data_engine/trace.py +727 -0
  232. package/artemis-main/artemis/drivers/__init__.py +48 -0
  233. package/artemis-main/artemis/drivers/android/adb_driver.py +507 -0
  234. package/artemis-main/artemis/drivers/android/hierarchy.py +64 -0
  235. package/artemis-main/artemis/drivers/android/input_ime.py +120 -0
  236. package/artemis-main/artemis/drivers/android/recorder.py +57 -0
  237. package/artemis-main/artemis/drivers/base.py +290 -0
  238. package/artemis-main/artemis/drivers/cloud/cloud_driver.py +117 -0
  239. package/artemis-main/artemis/drivers/factory.py +80 -0
  240. package/artemis-main/artemis/drivers/mock/mock_driver.py +198 -0
  241. package/artemis-main/artemis/drivers/types.py +67 -0
  242. package/artemis-main/artemis/graph/checkpoints.py +1243 -0
  243. package/artemis-main/artemis/graph/graph.py +1055 -0
  244. package/artemis-main/artemis/graph/perception.py +234 -0
  245. package/artemis-main/artemis/graph/state.py +151 -0
  246. package/artemis-main/artemis/graph/visibility.py +238 -0
  247. package/artemis-main/artemis/interfaces/cli/__init__.py +19 -0
  248. package/artemis-main/artemis/interfaces/cli/commands/__init__.py +15 -0
  249. package/artemis-main/artemis/interfaces/cli/commands/batch.py +278 -0
  250. package/artemis-main/artemis/interfaces/cli/commands/doctor.py +468 -0
  251. package/artemis-main/artemis/interfaces/cli/commands/helper.py +226 -0
  252. package/artemis-main/artemis/interfaces/cli/commands/init.py +193 -0
  253. package/artemis-main/artemis/interfaces/cli/commands/mcp.py +862 -0
  254. package/artemis-main/artemis/interfaces/cli/commands/run.py +463 -0
  255. package/artemis-main/artemis/interfaces/cli/commands/server.py +151 -0
  256. package/artemis-main/artemis/interfaces/cli/commands/server_lifecycle.py +328 -0
  257. package/artemis-main/artemis/interfaces/cli/commands/trace.py +111 -0
  258. package/artemis-main/artemis/interfaces/cli/commands/ui.py +404 -0
  259. package/artemis-main/artemis/interfaces/cli/main.py +109 -0
  260. package/artemis-main/artemis/interfaces/sdk/__init__.py +31 -0
  261. package/artemis-main/artemis/interfaces/sdk/client.py +19 -0
  262. package/artemis-main/artemis/interfaces/sdk/task.py +72 -0
  263. package/artemis-main/artemis/llm/__init__.py +25 -0
  264. package/artemis-main/artemis/llm/fake_model.py +179 -0
  265. package/artemis-main/artemis/llm/google/__init__.py +57 -0
  266. package/artemis-main/artemis/llm/google/provider.py +185 -0
  267. package/artemis-main/artemis/llm/google/usage.py +148 -0
  268. package/artemis-main/artemis/llm/parser.py +32 -0
  269. package/artemis-main/artemis/llm/reliability.py +267 -0
  270. package/artemis-main/artemis/llm/router.py +389 -0
  271. package/artemis-main/artemis/llm/schemas.py +36 -0
  272. package/artemis-main/artemis/llm/structured.py +196 -0
  273. package/artemis-main/artemis/main.py +58 -0
  274. package/artemis-main/artemis/mcp/__init__.py +20 -0
  275. package/artemis-main/artemis/mcp/action_executor.py +760 -0
  276. package/artemis-main/artemis/mcp/action_manifest.py +320 -0
  277. package/artemis-main/artemis/mcp/action_names.py +172 -0
  278. package/artemis-main/artemis/mcp/action_server.py +202 -0
  279. package/artemis-main/artemis/mcp/action_session.py +245 -0
  280. package/artemis-main/artemis/mcp/action_specs.py +868 -0
  281. package/artemis-main/artemis/mcp/action_types.py +86 -0
  282. package/artemis-main/artemis/mcp/actuators/__init__.py +21 -0
  283. package/artemis-main/artemis/mcp/actuators/adb.py +414 -0
  284. package/artemis-main/artemis/mcp/actuators/base.py +113 -0
  285. package/artemis-main/artemis/mcp/actuators/mock.py +70 -0
  286. package/artemis-main/artemis/mcp/adb_server.py +490 -0
  287. package/artemis-main/artemis/mcp/observation.py +133 -0
  288. package/artemis-main/artemis/mcp/xml_search_server.py +297 -0
  289. package/artemis-main/artemis/memory/__init__.py +90 -0
  290. package/artemis-main/artemis/memory/chunking.py +1843 -0
  291. package/artemis-main/artemis/memory/context_policy.py +226 -0
  292. package/artemis-main/artemis/memory/step_capsule.md +73 -0
  293. package/artemis-main/artemis/memory/step_memory.py +281 -0
  294. package/artemis-main/artemis/memory/transcript.py +798 -0
  295. package/artemis-main/artemis/platform/__init__.py +61 -0
  296. package/artemis-main/artemis/platform/base.py +104 -0
  297. package/artemis-main/artemis/platform/darwin.py +184 -0
  298. package/artemis-main/artemis/platform/linux.py +207 -0
  299. package/artemis-main/artemis/platform/windows.py +193 -0
  300. package/artemis-main/artemis/resources/__init__.py +46 -0
  301. package/artemis-main/artemis/runtime/__init__.py +108 -0
  302. package/artemis-main/artemis/runtime/adb_endpoint.py +173 -0
  303. package/artemis-main/artemis/runtime/awake_lease.py +283 -0
  304. package/artemis-main/artemis/runtime/awake_service.py +343 -0
  305. package/artemis-main/artemis/runtime/cancel_requests.py +254 -0
  306. package/artemis-main/artemis/runtime/daemon_client.py +422 -0
  307. package/artemis-main/artemis/runtime/device_lock.py +1013 -0
  308. package/artemis-main/artemis/runtime/device_pool.py +478 -0
  309. package/artemis-main/artemis/runtime/helper_manager.py +1030 -0
  310. package/artemis-main/artemis/runtime/process_probe.py +98 -0
  311. package/artemis-main/artemis/runtime/server_lifecycle.py +435 -0
  312. package/artemis-main/artemis/runtime/supervisor.py +121 -0
  313. package/artemis-main/artemis/runtime/trace_store.py +375 -0
  314. package/artemis-main/artemis/sdk/__init__.py +26 -0
  315. package/artemis-main/artemis/sdk/agent.py +485 -0
  316. package/artemis-main/artemis/sdk/builders/__init__.py +29 -0
  317. package/artemis-main/artemis/sdk/builders/agent_config_builder.py +474 -0
  318. package/artemis-main/artemis/sdk/constants.py +15 -0
  319. package/artemis-main/artemis/sdk/run_outcome.py +58 -0
  320. package/artemis-main/artemis/sdk/types/__init__.py +61 -0
  321. package/artemis-main/artemis/sdk/types/agent.py +89 -0
  322. package/artemis-main/artemis/sdk/utils.py +28 -0
  323. package/artemis-main/artemis/services/llm.py +1040 -0
  324. package/artemis-main/artemis/services/token_meter.py +267 -0
  325. package/artemis-main/artemis/telemetry/__init__.py +35 -0
  326. package/artemis-main/artemis/telemetry/models.py +64 -0
  327. package/artemis-main/artemis/telemetry/replay.py +34 -0
  328. package/artemis-main/artemis/telemetry/storage/json_storage.py +47 -0
  329. package/artemis-main/artemis/telemetry/tracer.py +85 -0
  330. package/artemis-main/artemis/toolchain/__init__.py +52 -0
  331. package/artemis-main/artemis/toolchain/descriptors.py +92 -0
  332. package/artemis-main/artemis/toolchain/resolver.py +185 -0
  333. package/artemis-main/artemis/tools/base.py +232 -0
  334. package/artemis-main/artemis/tools/command_tool.py +1240 -0
  335. package/artemis-main/artemis/tools/committee_prompts.json +5 -0
  336. package/artemis-main/artemis/tools/committee_tool.py +422 -0
  337. package/artemis-main/artemis/tools/diagnoser_submit_answer_tool.py +87 -0
  338. package/artemis-main/artemis/tools/diagnostic_tool.py +120 -0
  339. package/artemis-main/artemis/tools/explorer_tool.py +581 -0
  340. package/artemis-main/artemis/tools/history/__init__.py +398 -0
  341. package/artemis-main/artemis/tools/history/replay.py +120 -0
  342. package/artemis-main/artemis/tools/history/screenshot.py +139 -0
  343. package/artemis-main/artemis/tools/history/search.py +563 -0
  344. package/artemis-main/artemis/tools/image_processor_tool.py +124 -0
  345. package/artemis-main/artemis/tools/index.py +47 -0
  346. package/artemis-main/artemis/tools/log_tool.py +108 -0
  347. package/artemis-main/artemis/tools/mobile/launch_app.py +132 -0
  348. package/artemis-main/artemis/tools/mobile/log_utils.py +138 -0
  349. package/artemis-main/artemis/tools/mobile/ocr.py +202 -0
  350. package/artemis-main/artemis/tools/mobile/read_hierarchy.py +108 -0
  351. package/artemis-main/artemis/tools/mobile/read_logs.py +116 -0
  352. package/artemis-main/artemis/tools/mobile/search_logs.py +260 -0
  353. package/artemis-main/artemis/tools/object_detection_tool.py +244 -0
  354. package/artemis-main/artemis/tools/scratchpad.py +819 -0
  355. package/artemis-main/artemis/tools/system/adb_shell.py +31 -0
  356. package/artemis-main/artemis/tools/tool_wrapper.py +270 -0
  357. package/artemis-main/artemis/tools/video_tool.py +261 -0
  358. package/artemis-main/artemis/tools/wait_tool.py +97 -0
  359. package/artemis-main/artemis/utils/app_launch_utils.py +207 -0
  360. package/artemis-main/artemis/utils/coordinates.py +606 -0
  361. package/artemis-main/artemis/utils/credentials_validator.py +201 -0
  362. package/artemis-main/artemis/utils/cv_canvas.py +189 -0
  363. package/artemis-main/artemis/utils/cython_compat.py +45 -0
  364. package/artemis-main/artemis/utils/element_hit_test.py +80 -0
  365. package/artemis-main/artemis/utils/image_diff.py +335 -0
  366. package/artemis-main/artemis/utils/image_hash.py +80 -0
  367. package/artemis-main/artemis/utils/notes.py +470 -0
  368. package/artemis-main/artemis/utils/ocr_api.py +105 -0
  369. package/artemis-main/artemis/utils/ocr_xml_fusion.py +318 -0
  370. package/artemis-main/artemis/utils/plan_grammar.py +513 -0
  371. package/artemis-main/artemis/utils/python_executor.py +213 -0
  372. package/artemis-main/artemis/utils/startup_progress.py +60 -0
  373. package/artemis-main/artemis/utils/task_tree.py +1174 -0
  374. package/artemis-main/artemis/utils/text.py +37 -0
  375. package/artemis-main/artemis/utils/ui_filter.py +651 -0
  376. package/artemis-main/artemis/utils/video.py +685 -0
  377. package/artemis-main/artemis/utils/visualization.py +802 -0
  378. package/artemis-main/config/artemis.example.jsonc +12 -0
  379. package/artemis-main/mcp_server/README.md +111 -0
  380. package/artemis-main/mcp_server/__init__.py +21 -0
  381. package/artemis-main/mcp_server/__main__.py +20 -0
  382. package/artemis-main/mcp_server/background/__init__.py +15 -0
  383. package/artemis-main/mcp_server/background/task_runner.py +453 -0
  384. package/artemis-main/mcp_server/base.py +49 -0
  385. package/artemis-main/mcp_server/notifiers/__init__.py +74 -0
  386. package/artemis-main/mcp_server/notifiers/agentapi.py +289 -0
  387. package/artemis-main/mcp_server/notifiers/base.py +45 -0
  388. package/artemis-main/mcp_server/notifiers/composite.py +87 -0
  389. package/artemis-main/mcp_server/notifiers/desktop.py +108 -0
  390. package/artemis-main/mcp_server/notifiers/file.py +69 -0
  391. package/artemis-main/mcp_server/notifiers/script.py +90 -0
  392. package/artemis-main/mcp_server/notifiers/webhook.py +87 -0
  393. package/artemis-main/mcp_server/rules.md +68 -0
  394. package/artemis-main/mcp_server/server.py +106 -0
  395. package/artemis-main/mcp_server/tools/__init__.py +29 -0
  396. package/artemis-main/mcp_server/tools/device_state.py +107 -0
  397. package/artemis-main/mcp_server/tools/diagnose.py +1249 -0
  398. package/artemis-main/mcp_server/tools/inspect_trace.py +394 -0
  399. package/artemis-main/mcp_server/tools/task_manager.py +627 -0
  400. package/artemis-main/mcp_server/tools/task_runner.py +548 -0
  401. package/artemis-main/mcp_server/utils/__init__.py +15 -0
  402. package/artemis-main/mcp_server/utils/device_utils.py +180 -0
  403. package/artemis-main/mcp_server/utils/env_utils.py +65 -0
  404. package/artemis-main/packages/artemis-accessibility-helper/ArtemisAccessibilityHelper.apk +0 -0
  405. package/artemis-main/packages/artemis-accessibility-helper/app/build.gradle.kts +35 -0
  406. package/artemis-main/packages/artemis-accessibility-helper/app/src/main/AndroidManifest.xml +49 -0
  407. package/artemis-main/packages/artemis-accessibility-helper/app/src/main/java/com/artemis/helper/A11yNode.java +274 -0
  408. package/artemis-main/packages/artemis-accessibility-helper/app/src/main/java/com/artemis/helper/ArtemisAccessibilityService.java +227 -0
  409. package/artemis-main/packages/artemis-accessibility-helper/app/src/main/java/com/artemis/helper/CommandServer.java +463 -0
  410. package/artemis-main/packages/artemis-accessibility-helper/app/src/main/java/com/artemis/helper/DisplayUtils.java +111 -0
  411. package/artemis-main/packages/artemis-accessibility-helper/app/src/main/java/com/artemis/helper/GestureController.java +232 -0
  412. package/artemis-main/packages/artemis-accessibility-helper/app/src/main/java/com/artemis/helper/HierarchyDumper.java +832 -0
  413. package/artemis-main/packages/artemis-accessibility-helper/app/src/main/java/com/artemis/helper/TokenReceiver.java +36 -0
  414. package/artemis-main/packages/artemis-accessibility-helper/app/src/main/java/com/artemis/helper/TokenStore.java +38 -0
  415. package/artemis-main/packages/artemis-accessibility-helper/app/src/main/java/com/artemis/helper/XmlUtils.java +102 -0
  416. package/artemis-main/packages/artemis-accessibility-helper/app/src/main/res/values/strings.xml +4 -0
  417. package/artemis-main/packages/artemis-accessibility-helper/app/src/main/res/xml/accessibility_service_config.xml +10 -0
  418. package/artemis-main/packages/artemis-accessibility-helper/build.gradle.kts +3 -0
  419. package/artemis-main/packages/artemis-accessibility-helper/build_apk.sh +215 -0
  420. package/artemis-main/packages/artemis-accessibility-helper/debug.keystore +0 -0
  421. package/artemis-main/packages/artemis-accessibility-helper/gradle.properties +2 -0
  422. package/artemis-main/packages/artemis-accessibility-helper/helper_manifest.json +7 -0
  423. package/artemis-main/packages/artemis-accessibility-helper/settings.gradle.kts +17 -0
  424. package/artemis-main/packages/artemis-client/DESIGN.md +89 -0
  425. package/artemis-main/packages/artemis-client/README.md +112 -0
  426. package/artemis-main/packages/artemis-client/examples/basic.py +30 -0
  427. package/artemis-main/packages/artemis-client/pyproject.toml +36 -0
  428. package/artemis-main/packages/artemis-client/src/artemis_client/__init__.py +45 -0
  429. package/artemis-main/packages/artemis-client/src/artemis_client/client.py +401 -0
  430. package/artemis-main/packages/artemis-client/src/artemis_client/errors.py +66 -0
  431. package/artemis-main/packages/artemis-client/src/artemis_client/models.py +212 -0
  432. package/artemis-main/packages/artemis-client/src/artemis_client/py.typed +1 -0
  433. package/artemis-main/packages/artemis-client/src/artemis_client/transport.py +134 -0
  434. package/artemis-main/packages/artemis-client/tests/test_client.py +289 -0
  435. package/artemis-main/packages/artemis-client/tests/test_transport.py +85 -0
  436. package/artemis-main/pyproject.toml +259 -0
  437. package/artemis-main/pyright-core.json +17 -0
  438. package/artemis-main/scripts/check_dependency_sources.py +68 -0
  439. package/artemis-main/scripts/install_deps.ps1 +520 -0
  440. package/artemis-main/scripts/install_deps.sh +533 -0
  441. package/artemis-main/scripts/quality_ratchet.py +97 -0
  442. package/artemis-main/scripts/real_device_video_acceptance.py +483 -0
  443. package/artemis-main/scripts/run_edge_case_part.py +327 -0
  444. package/artemis-main/scripts/run_ui.py +242 -0
  445. package/artemis-main/scripts/start.ps1 +500 -0
  446. package/artemis-main/scripts/test_all_edge_cases.py +269 -0
  447. package/artemis-main/scripts/verify_concurrency_and_devices.py +385 -0
  448. package/artemis-main/setup.py +164 -0
  449. package/artemis-main/start.bat +35 -0
  450. package/artemis-main/start.sh +423 -0
  451. package/artemis-main/third_party/mobile_use/LICENSE +190 -0
  452. package/artemis-main/third_party/mobile_use/METADATA +27 -0
  453. package/artemis-main/third_party/mobile_use/NOTICE +12 -0
  454. package/artemis-main/third_party/mobile_use/__init__.py +19 -0
  455. package/artemis-main/third_party/mobile_use/agents/__init__.py +13 -0
  456. package/artemis-main/third_party/mobile_use/agents/hopper/__init__.py +13 -0
  457. package/artemis-main/third_party/mobile_use/agents/hopper/hopper.md +22 -0
  458. package/artemis-main/third_party/mobile_use/agents/hopper/hopper.py +80 -0
  459. package/artemis-main/third_party/mobile_use/clients/__init__.py +13 -0
  460. package/artemis-main/third_party/mobile_use/clients/ui_automator_client.py +482 -0
  461. package/artemis-main/third_party/mobile_use/config/__init__.py +13 -0
  462. package/artemis-main/third_party/mobile_use/config/llm.py +183 -0
  463. package/artemis-main/third_party/mobile_use/config/output.py +184 -0
  464. package/artemis-main/third_party/mobile_use/context.py +119 -0
  465. package/artemis-main/third_party/mobile_use/controllers/__init__.py +13 -0
  466. package/artemis-main/third_party/mobile_use/controllers/platform_specific_commands_controller.py +151 -0
  467. package/artemis-main/third_party/mobile_use/controllers/types.py +92 -0
  468. package/artemis-main/third_party/mobile_use/controllers/unified_controller.py +183 -0
  469. package/artemis-main/third_party/mobile_use/graph.py +54 -0
  470. package/artemis-main/third_party/mobile_use/llm_service.py +193 -0
  471. package/artemis-main/third_party/mobile_use/main.py +130 -0
  472. package/artemis-main/third_party/mobile_use/sdk/__init__.py +13 -0
  473. package/artemis-main/third_party/mobile_use/sdk/agent.py +1060 -0
  474. package/artemis-main/third_party/mobile_use/sdk/builders/__init__.py +13 -0
  475. package/artemis-main/third_party/mobile_use/sdk/builders/agent_config_builder.py +225 -0
  476. package/artemis-main/third_party/mobile_use/sdk/builders/index.py +33 -0
  477. package/artemis-main/third_party/mobile_use/sdk/builders/task_request_builder.py +243 -0
  478. package/artemis-main/third_party/mobile_use/sdk/types/__init__.py +13 -0
  479. package/artemis-main/third_party/mobile_use/sdk/types/agent.py +98 -0
  480. package/artemis-main/third_party/mobile_use/sdk/types/exceptions.py +109 -0
  481. package/artemis-main/third_party/mobile_use/sdk/types/task.py +234 -0
  482. package/artemis-main/third_party/mobile_use/testing.py +59 -0
  483. package/artemis-main/third_party/mobile_use/tools/__init__.py +13 -0
  484. package/artemis-main/third_party/mobile_use/tools/index.py +44 -0
  485. package/artemis-main/third_party/mobile_use/tools/mobile/__init__.py +13 -0
  486. package/artemis-main/third_party/mobile_use/tools/mobile/launch_app.py +89 -0
  487. package/artemis-main/third_party/mobile_use/tools/tool_wrapper.py +52 -0
  488. package/artemis-main/third_party/mobile_use/utils/__init__.py +13 -0
  489. package/artemis-main/third_party/mobile_use/utils/app_launch_utils.py +217 -0
  490. package/artemis-main/third_party/mobile_use/utils/cli_helpers.py +60 -0
  491. package/artemis-main/third_party/mobile_use/utils/decorators.py +158 -0
  492. package/artemis-main/third_party/mobile_use/utils/file.py +31 -0
  493. package/artemis-main/third_party/mobile_use/utils/logger.py +216 -0
  494. package/artemis-main/third_party/mobile_use/utils/media.py +213 -0
  495. package/artemis-main/third_party/mobile_use/utils/shell_utils.py +39 -0
  496. package/artemis-main/third_party/mobile_use/utils/video.py +398 -0
  497. package/artemis-main/uv.lock +4908 -0
  498. package/bin/android-midscene-automation.js +1 -5
  499. package/bin/artemis-config.mjs +15 -0
  500. package/bin/artemis-embedded.py +56 -0
  501. package/bin/start-artemis.mjs +30 -0
  502. package/bin/start-dev.mjs +112 -0
  503. package/bin/test_artemis_embedded.py +44 -0
  504. package/package.json +4 -2
  505. package/server/artemis.ts +34 -0
  506. package/server/http-api.ts +12 -0
  507. package/src/App.vue +10 -5
  508. package/src/api.ts +5 -0
  509. package/src/appium-recorder/AppiumPage.vue +1 -1
  510. package/src/appium-recorder/components/ComponentInspector.vue +1 -1
  511. package/src/appium-recorder/components/FlowNodeSearch.vue +3 -3
  512. package/src/appium-recorder/components/ImageCheckDialog.vue +3 -3
  513. package/src/appium-recorder/components/RecorderWorkspace.vue +4 -4
  514. package/src/appium-recorder/flow-appearance.ts +4 -4
  515. package/src/components/AppSidebar.vue +8 -7
  516. package/src/components/MidsceneMaintenanceNotice.vue +17 -0
  517. package/src/components/analytics/AnalysisCharts.vue +4 -4
  518. package/src/components/analytics/ReportAnalysis.vue +1 -1
  519. package/src/components/generator/TestCaseFileUpload.vue +1 -1
  520. package/src/components/help/HelpCenter.vue +56 -37
  521. package/src/pages/AiGeneratorPage.vue +1 -1
  522. package/src/pages/ArtemisPage.vue +72 -0
  523. package/src/pages/ConfigPage.vue +18 -19
  524. package/src/style.css +340 -121
  525. package/src/types.ts +1 -1
@@ -0,0 +1,180 @@
1
+ # Copyright 2026 Google LLC
2
+ #
3
+ # Licensed under the Apache License, Version 2.0 (the "License");
4
+ # you may not use this file except in compliance with the License.
5
+ # You may obtain a copy of the License at
6
+ #
7
+ # http://www.apache.org/licenses/LICENSE-2.0
8
+ #
9
+ # Unless required by applicable law or agreed to in writing, software
10
+ # distributed under the License is distributed on an "AS IS" BASIS,
11
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
+ # See the License for the specific language governing permissions and
13
+ # limitations under the License.
14
+
15
+ """Device discovery, ADB resolution, and emulator management for MCP workflows."""
16
+
17
+ import os
18
+ import shutil
19
+ import subprocess
20
+ import sys
21
+ import time
22
+
23
+
24
+ def resolve_adb_path() -> str:
25
+ """Resolves the absolute path to the adb binary across platforms."""
26
+ # 1. Check if adb is in PATH
27
+ which_adb = shutil.which("adb")
28
+ if which_adb:
29
+ return which_adb
30
+
31
+ # 2. Check ANDROID_HOME / ANDROID_SDK_ROOT
32
+ for env_var in ("ANDROID_HOME", "ANDROID_SDK_ROOT"):
33
+ sdk_root = os.getenv(env_var)
34
+ if sdk_root:
35
+ candidate = os.path.join(
36
+ sdk_root, "platform-tools", "adb.exe" if sys.platform == "win32" else "adb"
37
+ )
38
+ if os.path.exists(candidate):
39
+ return candidate
40
+
41
+ # 3. Check standard OS-specific SDK locations
42
+ candidates = []
43
+ if sys.platform == "win32":
44
+ local_appdata = os.getenv("LOCALAPPDATA", "")
45
+ if local_appdata:
46
+ candidates.append(
47
+ os.path.join(local_appdata, "Android", "Sdk", "platform-tools", "adb.exe")
48
+ )
49
+ elif sys.platform == "darwin":
50
+ candidates.extend(
51
+ [
52
+ os.path.expanduser("~/Library/Android/sdk/platform-tools/adb"),
53
+ "/opt/homebrew/bin/adb",
54
+ "/usr/local/bin/adb",
55
+ ]
56
+ )
57
+ else: # Linux
58
+ candidates.extend(
59
+ [
60
+ "/usr/bin/adb",
61
+ os.path.expanduser("~/Android/Sdk/platform-tools/adb"),
62
+ "/usr/local/bin/adb",
63
+ ]
64
+ )
65
+
66
+ for cand in candidates:
67
+ if os.path.exists(cand):
68
+ return cand
69
+
70
+ return "adb"
71
+
72
+
73
+ def resolve_emulator_path() -> str | None:
74
+ """Resolves the path to the Android emulator executable."""
75
+ which_emulator = shutil.which("emulator")
76
+ if which_emulator:
77
+ return which_emulator
78
+
79
+ candidates = [
80
+ os.path.expanduser("~/Android/Sdk/emulator/emulator"),
81
+ os.path.expanduser("~/Library/Android/sdk/emulator/emulator"),
82
+ ]
83
+ if sys.platform == "win32":
84
+ local_appdata = os.getenv("LOCALAPPDATA", "")
85
+ if local_appdata:
86
+ candidates.append(
87
+ os.path.join(local_appdata, "Android", "Sdk", "emulator", "emulator.exe")
88
+ )
89
+
90
+ for cand in candidates:
91
+ if os.path.exists(cand):
92
+ return cand
93
+ return None
94
+
95
+
96
+ def get_connected_devices(adb_path: str | None = None) -> list[str]:
97
+ """Returns a list of connected and authorized Android device serials."""
98
+ adb = adb_path or resolve_adb_path()
99
+ try:
100
+ res = subprocess.run(
101
+ [adb, "devices"],
102
+ stdin=subprocess.DEVNULL,
103
+ capture_output=True,
104
+ text=True,
105
+ timeout=5,
106
+ )
107
+ devices: list[str] = []
108
+ for line in res.stdout.splitlines():
109
+ line_str = line.strip()
110
+ if line_str and not line_str.startswith("List of devices"):
111
+ parts = line_str.split()
112
+ if len(parts) >= 2 and parts[1] == "device":
113
+ devices.append(parts[0])
114
+ return devices
115
+ except Exception:
116
+ return []
117
+
118
+
119
+ def is_emulator_running(adb_path: str | None = None) -> bool:
120
+ """Checks if an Android emulator (e.g. emulator-5554) is running and accessible."""
121
+ adb = adb_path or resolve_adb_path()
122
+ devices = get_connected_devices(adb)
123
+ return any(d.startswith("emulator-") for d in devices)
124
+
125
+
126
+ def ensure_emulator(
127
+ avd_name: str = "AndroidWorldAvd",
128
+ adb_path: str | None = None,
129
+ emulator_path: str | None = None,
130
+ timeout_seconds: int = 90,
131
+ ) -> bool:
132
+ """Ensures an emulator is running. If not, boots the specified AVD in the background."""
133
+ adb = adb_path or resolve_adb_path()
134
+ if is_emulator_running(adb):
135
+ return True
136
+
137
+ emu_exe = emulator_path or resolve_emulator_path()
138
+ if not emu_exe or not os.path.exists(emu_exe):
139
+ return False
140
+
141
+ try:
142
+ command = [emu_exe, "-avd", avd_name, "-no-snapshot", "-grpc", "8554"]
143
+ if sys.platform == "win32":
144
+ subprocess.Popen(
145
+ command,
146
+ stdin=subprocess.DEVNULL,
147
+ stdout=subprocess.DEVNULL,
148
+ stderr=subprocess.DEVNULL,
149
+ creationflags=(subprocess.CREATE_NEW_PROCESS_GROUP | subprocess.DETACHED_PROCESS),
150
+ )
151
+ else:
152
+ subprocess.Popen(
153
+ command,
154
+ stdin=subprocess.DEVNULL,
155
+ stdout=subprocess.DEVNULL,
156
+ stderr=subprocess.DEVNULL,
157
+ start_new_session=True,
158
+ )
159
+ except Exception:
160
+ return False
161
+
162
+ start_time = time.time()
163
+ while time.time() - start_time < timeout_seconds:
164
+ try:
165
+ res = subprocess.run(
166
+ [adb, "-s", "emulator-5554", "shell", "getprop", "sys.boot_completed"],
167
+ stdin=subprocess.DEVNULL,
168
+ capture_output=True,
169
+ text=True,
170
+ timeout=3,
171
+ )
172
+ if "1" in res.stdout:
173
+ time.sleep(2) # Stabilization
174
+ return True
175
+ except (subprocess.SubprocessError, OSError):
176
+ # adb not ready yet while the emulator boots: keep polling.
177
+ pass
178
+ time.sleep(2)
179
+
180
+ return False
@@ -0,0 +1,65 @@
1
+ # Copyright 2026 Google LLC
2
+ #
3
+ # Licensed under the Apache License, Version 2.0 (the "License");
4
+ # you may not use this file except in compliance with the License.
5
+ # You may obtain a copy of the License at
6
+ #
7
+ # http://www.apache.org/licenses/LICENSE-2.0
8
+ #
9
+ # Unless required by applicable law or agreed to in writing, software
10
+ # distributed under the License is distributed on an "AS IS" BASIS,
11
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
+ # See the License for the specific language governing permissions and
13
+ # limitations under the License.
14
+
15
+ """Environment and runtime discovery utilities for MCP."""
16
+
17
+ import os
18
+ import subprocess
19
+ import sys
20
+ from typing import Any
21
+
22
+
23
+ def get_project_root() -> str:
24
+ """Returns the absolute path to the project root repository."""
25
+ # Deliberate lazy import: this helper must keep working when the artemis
26
+ # package (or its config bootstrap) is unavailable or broken, falling back
27
+ # to filesystem-relative resolution below.
28
+ try:
29
+ from artemis.config.paths import ROOT_DIR
30
+
31
+ return str(ROOT_DIR)
32
+ except Exception:
33
+ # mcp_server/utils/env_utils.py -> root is 3 levels up
34
+ return os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
35
+
36
+
37
+ def resolve_python_executable(project_root: str | None = None) -> str:
38
+ """Resolves the preferred Python executable (.venv or current sys.executable)."""
39
+ root = project_root or get_project_root()
40
+
41
+ if sys.platform == "win32":
42
+ venv_python = os.path.join(root, ".venv", "Scripts", "python.exe")
43
+ else:
44
+ venv_python = os.path.join(root, ".venv", "bin", "python")
45
+
46
+ if os.path.exists(venv_python):
47
+ return venv_python
48
+
49
+ return sys.executable
50
+
51
+
52
+ def get_detached_process_kwargs() -> dict[str, Any]:
53
+ """Returns kwargs for subprocess.Popen to run a fully detached background process cross-platform."""
54
+ kwargs: dict[str, Any] = {
55
+ "stdin": subprocess.DEVNULL,
56
+ "stdout": subprocess.DEVNULL,
57
+ "stderr": subprocess.DEVNULL,
58
+ }
59
+ if sys.platform == "win32":
60
+ kwargs["creationflags"] = (
61
+ subprocess.CREATE_NEW_PROCESS_GROUP | subprocess.DETACHED_PROCESS # type: ignore[attr-defined]
62
+ )
63
+ else:
64
+ kwargs["start_new_session"] = True
65
+ return kwargs
@@ -0,0 +1,35 @@
1
+ plugins {
2
+ id("com.android.application")
3
+ }
4
+
5
+ android {
6
+ namespace = "com.artemis.helper"
7
+ compileSdk = 35
8
+
9
+ defaultConfig {
10
+ applicationId = "com.artemis.helper"
11
+ minSdk = 24
12
+ targetSdk = 35
13
+ versionCode = 6
14
+ versionName = "1.2.0"
15
+ }
16
+
17
+ buildTypes {
18
+ release {
19
+ isMinifyEnabled = false
20
+ proguardFiles(
21
+ getDefaultProguardFile("proguard-android-optimize.txt"),
22
+ "proguard-rules.pro"
23
+ )
24
+ }
25
+ }
26
+ compileOptions {
27
+ sourceCompatibility = JavaVersion.VERSION_1_8
28
+ targetCompatibility = JavaVersion.VERSION_1_8
29
+ }
30
+ }
31
+
32
+ dependencies {
33
+ // Pure standard Android SDK APIs (android.accessibilityservice, android.view.accessibility, org.json)
34
+ // Zero external dependencies to maximize stability, guarantee compatibility, and keep APK under 30KB.
35
+ }
@@ -0,0 +1,49 @@
1
+ <?xml version="1.0" encoding="utf-8"?>
2
+ <manifest xmlns:android="http://schemas.android.com/apk/res/android"
3
+ package="com.artemis.helper">
4
+
5
+ <uses-sdk
6
+ android:minSdkVersion="24"
7
+ android:targetSdkVersion="35" />
8
+
9
+ <uses-permission android:name="android.permission.INTERNET" />
10
+ <uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
11
+ <uses-permission android:name="android.permission.FOREGROUND_SERVICE_SPECIAL_USE" />
12
+ <uses-permission android:name="android.permission.POST_NOTIFICATIONS" />
13
+
14
+ <application
15
+ android:allowBackup="false"
16
+ android:label="@string/app_name"
17
+ android:supportsRtl="true">
18
+
19
+ <service
20
+ android:name=".ArtemisAccessibilityService"
21
+ android:permission="android.permission.BIND_ACCESSIBILITY_SERVICE"
22
+ android:exported="true"
23
+ android:foregroundServiceType="specialUse">
24
+ <intent-filter>
25
+ <action android:name="android.accessibilityservice.AccessibilityService" />
26
+ </intent-filter>
27
+ <meta-data
28
+ android:name="android.accessibilityservice"
29
+ android:resource="@xml/accessibility_service_config" />
30
+ <property
31
+ android:name="android.app.PROPERTY_SPECIAL_USE_FGS_SUBTYPE"
32
+ android:value="Artemis mobile automation test bridge" />
33
+ </service>
34
+
35
+ <!-- Session token drop: only a sender holding WRITE_SECURE_SETTINGS (the adb
36
+ shell user, the system) can deliver this broadcast, so no ordinary app on
37
+ the device can set a token of its own and then talk to the loopback server. -->
38
+ <receiver
39
+ android:name=".TokenReceiver"
40
+ android:exported="true"
41
+ android:permission="android.permission.WRITE_SECURE_SETTINGS">
42
+ <intent-filter>
43
+ <action android:name="com.artemis.helper.SET_TOKEN" />
44
+ </intent-filter>
45
+ </receiver>
46
+
47
+ </application>
48
+
49
+ </manifest>
@@ -0,0 +1,274 @@
1
+ package com.artemis.helper;
2
+
3
+ import org.json.JSONArray;
4
+ import org.json.JSONObject;
5
+
6
+ import java.util.ArrayList;
7
+ import java.util.List;
8
+
9
+ /**
10
+ * In-memory snapshot of an Accessibility UI element node.
11
+ * Decoupled from live Android AccessibilityNodeInfo objects to prevent
12
+ * Binder proxy memory leaks, avoid stale node crashes, and ensure
13
+ * instant thread-safe serialization to both XML and JSON.
14
+ *
15
+ * {@code bounds} are the node's <em>visible</em> bounds: the raw
16
+ * {@code getBoundsInScreen} rectangle intersected with the display, the
17
+ * node's window and every scrollable ancestor, exactly like UIAutomator's
18
+ * {@code getVisibleBoundsInScreen}. They are therefore never negative and
19
+ * never extend past the screen.
20
+ */
21
+ public final class A11yNode {
22
+
23
+ public int index = 0;
24
+ public String text = "";
25
+ public String resourceId = "";
26
+ public String className = "";
27
+ public String packageName = "";
28
+ public String contentDesc = "";
29
+
30
+ public boolean checkable = false;
31
+ public boolean checked = false;
32
+ public boolean clickable = false;
33
+ public boolean enabled = true;
34
+ public boolean focusable = false;
35
+ public boolean focused = false;
36
+ public boolean scrollable = false;
37
+ public boolean longClickable = false;
38
+ public boolean password = false;
39
+ public boolean selected = false;
40
+ public boolean visibleToUser = true;
41
+
42
+ // Window metadata for multi-window awareness
43
+ public int windowId = -1;
44
+ public String windowType = "";
45
+ public int windowLayer = 0;
46
+ public boolean windowActive = false;
47
+ public boolean windowFocused = false;
48
+
49
+ // Advanced semantics for Agent perception & verification
50
+ public boolean editable = false;
51
+ public boolean isHeading = false;
52
+ public boolean screenReaderFocusable = false;
53
+ public String stateDescription = "";
54
+ public String errorText = "";
55
+ public String paneTitle = "";
56
+ public String tooltip = "";
57
+
58
+ public int left = 0;
59
+ public int top = 0;
60
+ public int right = 0;
61
+ public int bottom = 0;
62
+
63
+ public int drawingOrder = 0;
64
+ public String hint = "";
65
+
66
+ public final List<A11yNode> children = new ArrayList<>(4);
67
+
68
+ public A11yNode() {}
69
+
70
+ public int getWidth() {
71
+ return right - left;
72
+ }
73
+
74
+ public int getHeight() {
75
+ return bottom - top;
76
+ }
77
+
78
+ public String getBoundsString() {
79
+ return "[" + left + "," + top + "][" + right + "," + bottom + "]";
80
+ }
81
+
82
+ /**
83
+ * Serializes this node and all of its descendants into standard UIAutomator XML.
84
+ */
85
+ public void writeXml(StringBuilder sb) {
86
+ sb.append("<node");
87
+ XmlUtils.appendIntAttribute(sb, "index", index);
88
+ XmlUtils.appendAttribute(sb, "text", text);
89
+ XmlUtils.appendAttribute(sb, "resource-id", resourceId);
90
+ XmlUtils.appendAttribute(sb, "class", className);
91
+ XmlUtils.appendAttribute(sb, "package", packageName);
92
+ XmlUtils.appendAttribute(sb, "content-desc", contentDesc);
93
+ XmlUtils.appendBooleanAttribute(sb, "checkable", checkable);
94
+ XmlUtils.appendBooleanAttribute(sb, "checked", checked);
95
+ XmlUtils.appendBooleanAttribute(sb, "clickable", clickable);
96
+ XmlUtils.appendBooleanAttribute(sb, "enabled", enabled);
97
+ XmlUtils.appendBooleanAttribute(sb, "focusable", focusable);
98
+ XmlUtils.appendBooleanAttribute(sb, "focused", focused);
99
+ XmlUtils.appendBooleanAttribute(sb, "scrollable", scrollable);
100
+ XmlUtils.appendBooleanAttribute(sb, "long-clickable", longClickable);
101
+ XmlUtils.appendBooleanAttribute(sb, "password", password);
102
+ XmlUtils.appendBooleanAttribute(sb, "selected", selected);
103
+ XmlUtils.appendBooleanAttribute(sb, "visible-to-user", visibleToUser);
104
+ XmlUtils.appendAttribute(sb, "bounds", getBoundsString());
105
+ XmlUtils.appendIntAttribute(sb, "drawing-order", drawingOrder);
106
+ if (!hint.isEmpty()) {
107
+ XmlUtils.appendAttribute(sb, "hint", hint);
108
+ }
109
+
110
+ // Window metadata (emitted on window root nodes)
111
+ if (windowId >= 0) {
112
+ XmlUtils.appendIntAttribute(sb, "window-id", windowId);
113
+ if (!windowType.isEmpty()) {
114
+ XmlUtils.appendAttribute(sb, "window-type", windowType);
115
+ }
116
+ XmlUtils.appendIntAttribute(sb, "window-layer", windowLayer);
117
+ if (windowActive) {
118
+ XmlUtils.appendBooleanAttribute(sb, "window-active", true);
119
+ }
120
+ if (windowFocused) {
121
+ XmlUtils.appendBooleanAttribute(sb, "window-focused", true);
122
+ }
123
+ }
124
+
125
+ // Extended semantic attributes
126
+ if (editable) {
127
+ XmlUtils.appendBooleanAttribute(sb, "editable", true);
128
+ }
129
+ if (isHeading) {
130
+ XmlUtils.appendBooleanAttribute(sb, "heading", true);
131
+ }
132
+ if (screenReaderFocusable) {
133
+ XmlUtils.appendBooleanAttribute(sb, "screen-reader-focusable", true);
134
+ }
135
+ if (!stateDescription.isEmpty()) {
136
+ XmlUtils.appendAttribute(sb, "state-description", stateDescription);
137
+ }
138
+ if (!errorText.isEmpty()) {
139
+ XmlUtils.appendAttribute(sb, "error", errorText);
140
+ }
141
+ if (!paneTitle.isEmpty()) {
142
+ XmlUtils.appendAttribute(sb, "pane-title", paneTitle);
143
+ }
144
+ if (!tooltip.isEmpty()) {
145
+ XmlUtils.appendAttribute(sb, "tooltip", tooltip);
146
+ }
147
+
148
+ if (children.isEmpty()) {
149
+ sb.append(" />");
150
+ } else {
151
+ sb.append('>');
152
+ for (int i = 0; i < children.size(); i++) {
153
+ children.get(i).writeXml(sb);
154
+ }
155
+ sb.append("</node>");
156
+ }
157
+ }
158
+
159
+ /**
160
+ * Serializes this node into a hierarchical JSON tree object.
161
+ */
162
+ public JSONObject toTreeJson() {
163
+ JSONObject obj = toBaseJson();
164
+ try {
165
+ if (!children.isEmpty()) {
166
+ JSONArray childrenArr = new JSONArray();
167
+ for (int i = 0; i < children.size(); i++) {
168
+ childrenArr.put(children.get(i).toTreeJson());
169
+ }
170
+ obj.put("children", childrenArr);
171
+ }
172
+ } catch (Throwable ignored) {}
173
+ return obj;
174
+ }
175
+
176
+ /**
177
+ * Serializes this node into an element JSON object for the flat list.
178
+ * Does NOT include child references.
179
+ */
180
+ public JSONObject toFlatElementJson() {
181
+ return toBaseJson();
182
+ }
183
+
184
+ private JSONObject toBaseJson() {
185
+ JSONObject obj = new JSONObject();
186
+ try {
187
+ obj.put("class", className);
188
+ obj.put("package", packageName);
189
+ obj.put("resource-id", resourceId);
190
+ obj.put("text", text);
191
+ obj.put("content-desc", contentDesc);
192
+ obj.put("bounds", getBoundsString());
193
+
194
+ JSONObject bounds = new JSONObject();
195
+ bounds.put("left", left);
196
+ bounds.put("top", top);
197
+ bounds.put("right", right);
198
+ bounds.put("bottom", bottom);
199
+ obj.put("parsed_bounds", bounds);
200
+
201
+ obj.put("clickable", clickable);
202
+ obj.put("scrollable", scrollable);
203
+ obj.put("checkable", checkable);
204
+ obj.put("checked", checked);
205
+ obj.put("enabled", enabled);
206
+ obj.put("focusable", focusable);
207
+ obj.put("focused", focused);
208
+ obj.put("selected", selected);
209
+ obj.put("password", password);
210
+ obj.put("long-clickable", longClickable);
211
+ obj.put("visible-to-user", visibleToUser);
212
+ obj.put("drawing-order", drawingOrder);
213
+ if (!hint.isEmpty()) {
214
+ obj.put("hint", hint);
215
+ }
216
+
217
+ // Window metadata
218
+ if (windowId >= 0) {
219
+ obj.put("window_id", windowId);
220
+ obj.put("window_type", windowType);
221
+ obj.put("window_layer", windowLayer);
222
+ if (windowActive) obj.put("window_active", true);
223
+ if (windowFocused) obj.put("window_focused", true);
224
+ }
225
+
226
+ // Extended semantics
227
+ if (editable) {
228
+ obj.put("editable", true);
229
+ }
230
+ if (isHeading) {
231
+ obj.put("is_heading", true);
232
+ }
233
+ if (screenReaderFocusable) {
234
+ obj.put("screen_reader_focusable", true);
235
+ }
236
+ if (!stateDescription.isEmpty()) {
237
+ obj.put("state_description", stateDescription);
238
+ }
239
+ if (!errorText.isEmpty()) {
240
+ obj.put("error", errorText);
241
+ }
242
+ if (!paneTitle.isEmpty()) {
243
+ obj.put("pane_title", paneTitle);
244
+ }
245
+ if (!tooltip.isEmpty()) {
246
+ obj.put("tooltip", tooltip);
247
+ }
248
+ } catch (Throwable ignored) {}
249
+ return obj;
250
+ }
251
+
252
+ /**
253
+ * Determines whether this node contains useful information or interactivity
254
+ * and should be included in the flat elements array for agent perception.
255
+ */
256
+ public boolean isInformativeOrInteractive() {
257
+ boolean hasContent = !text.isEmpty() || !contentDesc.isEmpty() || !resourceId.isEmpty()
258
+ || !stateDescription.isEmpty() || !errorText.isEmpty() || !paneTitle.isEmpty();
259
+ boolean isInteractive = clickable || scrollable || checkable || focusable || longClickable || editable;
260
+ return hasContent || isInteractive || isHeading;
261
+ }
262
+
263
+ /**
264
+ * Recursively collects all informative or interactive nodes into a flat list.
265
+ */
266
+ public void collectFlatElements(List<JSONObject> flatList) {
267
+ if (isInformativeOrInteractive()) {
268
+ flatList.add(toFlatElementJson());
269
+ }
270
+ for (int i = 0; i < children.size(); i++) {
271
+ children.get(i).collectFlatElements(flatList);
272
+ }
273
+ }
274
+ }