android-midscene-automation 0.2.4 → 1.1.1

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 +10 -0
  2. package/README.md +159 -29
  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,327 @@
1
+ """Interactive Edge-Case Test Suite with Clear Step-by-Step Guidance.
2
+
3
+ Supports running each edge-case part independently or all together.
4
+ Usage:
5
+ python scripts/run_edge_case_part.py --part 1
6
+ python scripts/run_edge_case_part.py --part 2
7
+ python scripts/run_edge_case_part.py --part 3
8
+ python scripts/run_edge_case_part.py --part 4
9
+ python scripts/run_edge_case_part.py --part all
10
+ """
11
+
12
+ import argparse
13
+ import asyncio
14
+ import os
15
+ import sys
16
+ import time
17
+ import requests
18
+
19
+ WORKSPACE_ROOT = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
20
+ if WORKSPACE_ROOT not in sys.path:
21
+ sys.path.insert(0, WORKSPACE_ROOT)
22
+
23
+ from artemis.interfaces.sdk.client import ArtemisClient
24
+ from artemis.runtime.device_lock import DeviceExecutionLock
25
+ from artemis.toolchain import ensure_toolchain_in_path
26
+
27
+ ensure_toolchain_in_path()
28
+
29
+ from artemis.runtime import device_pool
30
+
31
+
32
+ def _resolve_devices() -> tuple[str, str]:
33
+ all_devs = device_pool.list_devices()
34
+ real_dev = next((d for d in all_devs if not d.is_emulator and d.state == "device"), None)
35
+ emu_dev = next((d for d in all_devs if d.is_emulator and d.state == "device"), None)
36
+ phone = os.environ.get("ARTEMIS_PHONE_SERIAL") or (
37
+ real_dev.serial if real_dev else "63191FDKX00062"
38
+ )
39
+ emu = os.environ.get("ARTEMIS_EMULATOR_SERIAL") or (
40
+ emu_dev.serial if emu_dev else "emulator-5554"
41
+ )
42
+ return phone, emu
43
+
44
+
45
+ PHONE_SERIAL, EMULATOR_SERIAL = _resolve_devices()
46
+ API_STATUS_URL = "http://127.0.0.1:8000/api/status"
47
+
48
+
49
+ def query_api():
50
+ try:
51
+ r = requests.get(API_STATUS_URL, timeout=3).json()
52
+ return r.get("active_tasks", []), r.get("queue", [])
53
+ except Exception:
54
+ return [], []
55
+
56
+
57
+ def print_status_snapshot(label: str):
58
+ active, queue = query_api()
59
+ print(f"\n[{label}] Web Console Live Snapshot (/api/status):")
60
+ print(f" ⚡ Running Tasks ({len(active)}):")
61
+ for a in active:
62
+ dev = a.get("device_id") or a.get("device_serial")
63
+ print(f" - [Device: {dev}] {a.get('goal')} (PID: {a.get('pid')})")
64
+ print(f" ⏳ Pending Tasks ({len(queue)}):")
65
+ for q in queue:
66
+ dev = q.get("device_serial") or q.get("device_id")
67
+ print(f" - [Device: {dev}] {q.get('goal')} [status={q.get('status')}]")
68
+ print(f" 📊 Total Active Queue (Running + Pending) = {len(active) + len(queue)}\n")
69
+
70
+
71
+ async def run_part_1():
72
+ print("=" * 80)
73
+ print("[Part 1: Single Device Serial Queueing & Automatic Relay]")
74
+ print("=" * 80)
75
+ print(
76
+ "🎯 Goal: Launch 2 tasks on the same physical device; verify FIFO queueing, Web Active Queue count, and relay."
77
+ )
78
+ print("📱 Target Device: Physical Pixel 11 Pro (63191FDKX00062)")
79
+ print("👀 Please observe:")
80
+ print(
81
+ " 1. Web Console (http://127.0.0.1:8000): Active Queue becomes 2 (1 Running blue badge, 1 Pending yellow badge)"
82
+ )
83
+ print(
84
+ " 2. Device Screen: Task 1A opens Settings first; upon completion, Task 1B automatically takes over to open Clock"
85
+ )
86
+ print(
87
+ " 3. Web Console: After 1A finishes, 1B switches to Running, and queue eventually clears to zero"
88
+ )
89
+ print("-" * 80)
90
+
91
+ DeviceExecutionLock.cleanup_stale_locks()
92
+ client = ArtemisClient(device_serial=PHONE_SERIAL, concurrency_mode="per_device")
93
+
94
+ async def task_1a():
95
+ print("🚀 [Task 1A] Launching: Open Settings on physical device...")
96
+ res = await client.run(
97
+ "Use manage_app to launch Settings app and report task status completed",
98
+ profile="flash",
99
+ )
100
+ print(f"✅ [Task 1A] Completed: status={res.status}")
101
+ return res
102
+
103
+ async def task_1b():
104
+ await asyncio.sleep(0.4)
105
+ print(
106
+ "🚀 [Task 1B] Launching: Open Clock on physical device -> should immediately enter queue..."
107
+ )
108
+ res = await client.run(
109
+ "Use manage_app to launch Clock app and report task status completed", profile="flash"
110
+ )
111
+ print(f"✅ [Task 1B] Completed: status={res.status}")
112
+ return res
113
+
114
+ t1 = asyncio.create_task(task_1a())
115
+ t2 = asyncio.create_task(task_1b())
116
+
117
+ await asyncio.sleep(1.2)
118
+ print_status_snapshot("Part 1 Running")
119
+
120
+ await asyncio.gather(t1, t2)
121
+ print(
122
+ "🎉 Part 1 Success: Task 1B automatically relayed and completed after Task 1A finished!\n"
123
+ )
124
+
125
+
126
+ async def run_part_2():
127
+ print("=" * 80)
128
+ print("[Part 2: Multi-Device True Parallel Execution]")
129
+ print("=" * 80)
130
+ print(
131
+ "🎯 Goal: Cross-device true concurrency; both devices run independently at the same time without blocking."
132
+ )
133
+ print(f"📱 Target Device 1: Physical Pixel 11 Pro ({PHONE_SERIAL})")
134
+ print(f"💻 Target Device 2: Android Emulator ({EMULATOR_SERIAL})")
135
+ print("👀 Please observe:")
136
+ print(
137
+ " 1. Dual screens: Physical device and emulator operate simultaneously without sequential waiting!"
138
+ )
139
+ print(
140
+ " 2. Web Console (http://127.0.0.1:8000): Active Tasks shows 2 (both with blue Running badges)"
141
+ )
142
+ print(" 3. Queue count: Queue is 0, each device displays its own Device Serial")
143
+ print("-" * 80)
144
+
145
+ DeviceExecutionLock.cleanup_stale_locks()
146
+ client_phone = ArtemisClient(device_serial=PHONE_SERIAL, concurrency_mode="per_device")
147
+ client_emu = ArtemisClient(device_serial=EMULATOR_SERIAL, concurrency_mode="per_device")
148
+
149
+ start_t = time.time()
150
+
151
+ async def p_task():
152
+ print(f"🚀 [Phone Task] Launching: Open Settings on {PHONE_SERIAL}...")
153
+ res = await client_phone.run(
154
+ "Use manage_app to launch Settings app and report task status completed",
155
+ profile="flash",
156
+ )
157
+ print(f"✅ [Phone Task] Finished in {time.time() - start_t:.2f}s")
158
+ return res
159
+
160
+ async def e_task():
161
+ print(f"🚀 [Emulator Task] Launching: Open Settings on {EMULATOR_SERIAL}...")
162
+ res = await client_emu.run(
163
+ "Use manage_app to launch Settings app and report task status completed",
164
+ profile="flash",
165
+ )
166
+ print(f"✅ [Emulator Task] Finished in {time.time() - start_t:.2f}s")
167
+ return res
168
+
169
+ tp = asyncio.create_task(p_task())
170
+ te = asyncio.create_task(e_task())
171
+
172
+ await asyncio.sleep(1.2)
173
+ print_status_snapshot("Part 2 Running (Multi-Device Concurrency)")
174
+
175
+ await asyncio.gather(tp, te)
176
+ print(
177
+ f"🎉 Part 2 Success: Both devices completed tasks concurrently in {time.time() - start_t:.2f}s!\n"
178
+ )
179
+
180
+
181
+ async def run_part_3():
182
+ print("=" * 80)
183
+ print("[Part 3: Global Concurrency Limit Across Devices]")
184
+ print("=" * 80)
185
+ print(
186
+ "🎯 Goal: Configure global concurrency mode (global, limit 1); even targeting different devices, tasks must queue globally."
187
+ )
188
+ print(f"📱 Device 1: Physical Pixel 11 Pro ({PHONE_SERIAL})")
189
+ print(f"💻 Device 2: Android Emulator ({EMULATOR_SERIAL})")
190
+ print("👀 Please observe:")
191
+ print(
192
+ " 1. Emulator screen: Even though emulator is completely idle, it waits until the phone task completes!"
193
+ )
194
+ print(
195
+ " 2. Web Console: Phone task is Running (blue badge), emulator task is Pending (yellow badge)!"
196
+ )
197
+ print(" 3. Once phone finishes, emulator instantly activates and begins execution!")
198
+ print("-" * 80)
199
+
200
+ DeviceExecutionLock.cleanup_stale_locks()
201
+ client_phone_g = ArtemisClient(device_serial=PHONE_SERIAL, concurrency_mode="global")
202
+ client_emu_g = ArtemisClient(device_serial=EMULATOR_SERIAL, concurrency_mode="global")
203
+
204
+ async def task_3a():
205
+ print(f"🚀 [Task 3A - Phone] Global mode starting: Executing on {PHONE_SERIAL}...")
206
+ res = await client_phone_g.run(
207
+ "Use manage_app to launch Settings app and report task status completed",
208
+ profile="flash",
209
+ )
210
+ print("✅ [Task 3A - Phone] Finished")
211
+ return res
212
+
213
+ async def task_3b():
214
+ await asyncio.sleep(0.4)
215
+ print(
216
+ f"🚀 [Task 3B - Emulator] Global mode starting: Targeting {EMULATOR_SERIAL} (should queue due to global limit)..."
217
+ )
218
+ res = await client_emu_g.run(
219
+ "Use manage_app to launch Settings app and report task status completed",
220
+ profile="flash",
221
+ )
222
+ print("✅ [Task 3B - Emulator] Finished")
223
+ return res
224
+
225
+ t3a = asyncio.create_task(task_3a())
226
+ t3b = asyncio.create_task(task_3b())
227
+
228
+ await asyncio.sleep(1.2)
229
+ print_status_snapshot("Part 3 Running (Global Limit Cross-Device Queueing)")
230
+
231
+ await asyncio.gather(t3a, t3b)
232
+ print(
233
+ "🎉 Part 3 Success: Global mutex accurately queued and relayed tasks across physical phone and emulator!\n"
234
+ )
235
+
236
+
237
+ async def run_part_4():
238
+ print("=" * 80)
239
+ print("[Part 4: Multi-Device Asymmetric Mixed Queueing]")
240
+ print("=" * 80)
241
+ print("🎯 Goal: Assign 2 tasks to physical phone, 1 task to emulator.")
242
+ print(
243
+ " Expected: Phone 4A and Emulator 4C run concurrently; Phone 4B queues in dedicated phone queue; 4B relays immediately on phone after 4A."
244
+ )
245
+ print("👀 Please observe:")
246
+ print(
247
+ " 1. Web Console: Active Tasks = 2 (Phone 4A + Emulator 4C), Queue = 1 (Phone 4B), Total = 3!"
248
+ )
249
+ print(
250
+ " 2. Screens: Phone and emulator operate concurrently first; Phone 4B executes on phone immediately after 4A finishes!"
251
+ )
252
+ print("-" * 80)
253
+
254
+ DeviceExecutionLock.cleanup_stale_locks()
255
+ client_phone = ArtemisClient(device_serial=PHONE_SERIAL, concurrency_mode="per_device")
256
+ client_emu = ArtemisClient(device_serial=EMULATOR_SERIAL, concurrency_mode="per_device")
257
+
258
+ async def task_4a():
259
+ print("🚀 [Task 4A - Phone] Started...")
260
+ return await client_phone.run(
261
+ "Use manage_app to launch Settings app and report task status completed",
262
+ profile="flash",
263
+ )
264
+
265
+ async def task_4b():
266
+ await asyncio.sleep(0.4)
267
+ print("🚀 [Task 4B - Phone] Started (Queued behind 4A)...")
268
+ return await client_phone.run(
269
+ "Use manage_app to launch Clock app and report task status completed", profile="flash"
270
+ )
271
+
272
+ async def task_4c():
273
+ print("🚀 [Task 4C - Emulator] Started concurrently with 4A...")
274
+ return await client_emu.run(
275
+ "Use manage_app to launch Settings app and report task status completed",
276
+ profile="flash",
277
+ )
278
+
279
+ t4a = asyncio.create_task(task_4a())
280
+ t4b = asyncio.create_task(task_4b())
281
+ t4c = asyncio.create_task(task_4c())
282
+
283
+ await asyncio.sleep(1.2)
284
+ print_status_snapshot("Part 4 Running (Mixed Queueing)")
285
+
286
+ await asyncio.gather(t4a, t4b, t4c)
287
+ print("Part 4 passed: cross-device concurrency and single-device queueing.\n")
288
+
289
+
290
+ async def main():
291
+ global PHONE_SERIAL, EMULATOR_SERIAL
292
+ parser = argparse.ArgumentParser(description="Artemis Concurrency & Queue Edge-Cases Runner")
293
+ parser.add_argument(
294
+ "--part",
295
+ choices=["1", "2", "3", "4", "all"],
296
+ default="1",
297
+ help="Which part to run (1, 2, 3, 4, or all)",
298
+ )
299
+ parser.add_argument("--phone", default=None, help="Target physical phone serial number")
300
+ parser.add_argument("--emulator", default=None, help="Target Android emulator serial number")
301
+ args = parser.parse_args()
302
+
303
+ if args.phone:
304
+ PHONE_SERIAL = args.phone
305
+ if args.emulator:
306
+ EMULATOR_SERIAL = args.emulator
307
+
308
+ if args.part == "1":
309
+ await run_part_1()
310
+ elif args.part == "2":
311
+ await run_part_2()
312
+ elif args.part == "3":
313
+ await run_part_3()
314
+ elif args.part == "4":
315
+ await run_part_4()
316
+ elif args.part == "all":
317
+ await run_part_1()
318
+ await asyncio.sleep(1)
319
+ await run_part_2()
320
+ await asyncio.sleep(1)
321
+ await run_part_3()
322
+ await asyncio.sleep(1)
323
+ await run_part_4()
324
+
325
+
326
+ if __name__ == "__main__":
327
+ asyncio.run(main())
@@ -0,0 +1,242 @@
1
+ #!/usr/bin/env python3
2
+ # Copyright 2026 Google LLC
3
+ #
4
+ # Licensed under the Apache License, Version 2.0 (the "License");
5
+ # you may not use this file except in compliance with the License.
6
+ # You may obtain a copy of the License at
7
+ #
8
+ # http://www.apache.org/licenses/LICENSE-2.0
9
+ #
10
+ # Unless required by applicable law or agreed to in writing, software
11
+ # distributed under the License is distributed on an "AS IS" BASIS,
12
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
+ # See the License for the specific language governing permissions and
14
+ # limitations under the License.
15
+
16
+ """Artemis UI Quick Launcher (run_ui.py)
17
+
18
+ Fastest and simplest way to launch the Artemis Web UI across all platforms:
19
+ - Automatically detects if the UI is already running and opens the browser directly.
20
+ - Uses uv or an existing virtualenv.
21
+ - Zero-configuration one-click start.
22
+ """
23
+
24
+ import argparse
25
+ import os
26
+ from pathlib import Path
27
+ import socket
28
+ import subprocess
29
+ import sys
30
+ import urllib.request
31
+ import webbrowser
32
+
33
+ ROOT_DIR = Path(__file__).resolve().parent.parent
34
+
35
+
36
+ def is_port_in_use(port: int, host: str = "127.0.0.1") -> bool:
37
+ """Check whether a TCP port is in use."""
38
+ with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as s:
39
+ s.settimeout(0.5)
40
+ return s.connect_ex((host, port)) == 0
41
+
42
+
43
+ def is_artemis_ui_responsive(url: str, timeout: float = 1.0) -> bool:
44
+ """Check if Artemis UI server is responding on given URL."""
45
+ try:
46
+ req = urllib.request.Request(url, headers={"User-Agent": "Artemis-QuickLauncher/1.0"})
47
+ with urllib.request.urlopen(req, timeout=timeout) as resp:
48
+ return resp.status in (200, 304)
49
+ except Exception:
50
+ return False
51
+
52
+
53
+ def find_python_executable() -> str:
54
+ """Find the best Python executable (.venv or system)."""
55
+ # 1. Check local .venv
56
+ venv_py = (
57
+ ROOT_DIR
58
+ / ".venv"
59
+ / ("Scripts" if os.name == "nt" else "bin")
60
+ / ("python.exe" if os.name == "nt" else "python")
61
+ )
62
+ if venv_py.is_file() and os.access(venv_py, os.X_OK if os.name != "nt" else os.F_OK):
63
+ return str(venv_py)
64
+
65
+ # 2. Return current sys.executable
66
+ return sys.executable
67
+
68
+
69
+ def main() -> None:
70
+ parser = argparse.ArgumentParser(
71
+ description="✨ One-click launcher for Artemis Mobile Agent UI",
72
+ formatter_class=argparse.ArgumentDefaultsHelpFormatter,
73
+ )
74
+ parser.add_argument("-p", "--port", type=int, default=8000, help="Port to run UI on")
75
+ parser.add_argument(
76
+ "-H",
77
+ "--host",
78
+ type=str,
79
+ default="127.0.0.1",
80
+ help="Host address to bind (loopback by default; use a tunnel for remote access)",
81
+ )
82
+ parser.add_argument(
83
+ "-r",
84
+ "--restart",
85
+ action="store_true",
86
+ help="Restart the server by terminating any existing instance on the port first",
87
+ )
88
+ parser.add_argument(
89
+ "-s",
90
+ "--stop",
91
+ action="store_true",
92
+ help="Stop running Artemis server on the specified port",
93
+ )
94
+ parser.add_argument(
95
+ "--status",
96
+ action="store_true",
97
+ help="Display status of running Artemis server on the specified port",
98
+ )
99
+ args, unknown = parser.parse_known_args()
100
+
101
+ # Ensure project root is on sys.path for lifecycle utility imports
102
+ if str(ROOT_DIR) not in sys.path:
103
+ sys.path.insert(0, str(ROOT_DIR))
104
+
105
+ from artemis.runtime.server_lifecycle import get_server_status, stop_server
106
+
107
+ def _arm_session_reconciler() -> None:
108
+ # Entry-layer assembly: importing the admin console session repository
109
+ # registers its orphan-session reconciler with the runtime, so a forced
110
+ # stop below can mark sessions whose worker processes were killed.
111
+ try:
112
+ import apps.admin_console.database.repositories.session_repository # noqa: F401
113
+ except Exception:
114
+ pass
115
+
116
+ if args.status:
117
+ st = get_server_status(args.port)
118
+ if st["running"]:
119
+ pid_info = f" (PID: {', '.join(map(str, st['pids']))})" if st["pids"] else ""
120
+ print(f"\033[1;32m● Artemis Server is RUNNING on port {args.port}{pid_info}\033[0m")
121
+ print(f" URL: {st['url']}")
122
+ else:
123
+ print(f"\033[1;30m○ Artemis Server is STOPPED (Port {args.port} is free)\033[0m")
124
+ return
125
+
126
+ if args.stop:
127
+ print(f"\033[1;33m🛑 Stopping Artemis server on port {args.port}...\033[0m")
128
+ _arm_session_reconciler()
129
+ ok, msg, pids = stop_server(args.port)
130
+ print(f"\033[1;32m✓ {msg}\033[0m")
131
+ return
132
+
133
+ ui_url = f"http://localhost:{args.port}"
134
+ admin_url = f"http://localhost:{args.port}/admin"
135
+
136
+ print("\033[1;36m" + "=" * 56 + "\033[0m")
137
+ print("\033[1;36m ✨ Artemis Autonomous Mobile Agent UI \033[0m")
138
+ print("\033[1;36m" + "=" * 56 + "\033[0m\n")
139
+
140
+ # Check if restart requested
141
+ if args.restart:
142
+ print(f"\033[1;33m🔄 Restart requested. Recycling port {args.port}...\033[0m")
143
+ _arm_session_reconciler()
144
+ ok, msg, pids = stop_server(args.port)
145
+ if pids:
146
+ print(f"\033[1;32m✓ {msg}\033[0m\n")
147
+ else:
148
+ print(f"Port {args.port} is clear.\n")
149
+
150
+ # Check if server is already running
151
+ elif is_port_in_use(args.port):
152
+ if is_artemis_ui_responsive(ui_url):
153
+ print(f"\033[1;32m✓ Artemis UI is already running at:\033[0m \033[1;36m{ui_url}\033[0m")
154
+ print(f"\033[1;35m🛠️ Admin Console:\033[0m \033[1;36m{admin_url}\033[0m\n")
155
+
156
+ # In interactive terminal, allow user to restart or open browser
157
+ if sys.stdin.isatty():
158
+ print("Server is active in another session/terminal.")
159
+ print(" [1] Open browser (default)")
160
+ print(" [2] Restart server (stop existing and start here)")
161
+ print(" [3] Stop server")
162
+ try:
163
+ choice = input("Select [1-3, default 1]: ").strip()
164
+ except (KeyboardInterrupt, EOFError):
165
+ choice = "1"
166
+
167
+ if choice == "2":
168
+ print(f"\n\033[1;33m🔄 Restarting Artemis server on port {args.port}...\033[0m")
169
+ _arm_session_reconciler()
170
+ stop_server(args.port)
171
+ elif choice == "3":
172
+ print(f"\n\033[1;33m🛑 Stopping Artemis server on port {args.port}...\033[0m")
173
+ _arm_session_reconciler()
174
+ ok, msg, _ = stop_server(args.port)
175
+ print(f"\033[1;32m✓ {msg}\033[0m")
176
+ return
177
+ else:
178
+ if not args.no_open:
179
+ print("🌐 Opening browser...")
180
+ webbrowser.open(ui_url)
181
+ return
182
+ else:
183
+ if not args.no_open:
184
+ print("🌐 Opening browser...")
185
+ webbrowser.open(ui_url)
186
+ return
187
+ else:
188
+ print(f"\033[1;33m⚠️ Port {args.port} is in use by another process.\033[0m")
189
+ print("Attempting to connect or start on specified port...")
190
+
191
+ # Choose execution engine: uv run artemis ui > direct python
192
+ has_uv = (
193
+ subprocess.run(
194
+ ["which", "uv"] if os.name != "nt" else ["where", "uv"],
195
+ stdout=subprocess.DEVNULL,
196
+ stderr=subprocess.DEVNULL,
197
+ ).returncode
198
+ == 0
199
+ )
200
+
201
+ cmd = []
202
+ if has_uv and (ROOT_DIR / "pyproject.toml").exists():
203
+ # Launch via `python -m artemis` (not the `artemis` console-script shim) so the
204
+ # long-running server never locks .venv/Scripts/artemis.exe against reinstalls.
205
+ cmd = [
206
+ "uv",
207
+ "run",
208
+ "python",
209
+ "-m",
210
+ "artemis",
211
+ "ui",
212
+ "--port",
213
+ str(args.port),
214
+ "--host",
215
+ args.host,
216
+ ]
217
+ if args.no_open:
218
+ cmd.append("--no-open")
219
+ if args.reload:
220
+ cmd.append("--reload")
221
+ else:
222
+ python_bin = find_python_executable()
223
+ # Set PYTHONPATH so apps and artemis are importable
224
+ env = os.environ.copy()
225
+ env["PYTHONPATH"] = (
226
+ f"{ROOT_DIR}{os.pathsep}{ROOT_DIR / 'apps'}{os.pathsep}{env.get('PYTHONPATH', '')}"
227
+ )
228
+ cmd = [python_bin, "-m", "artemis", "ui", "--port", str(args.port), "--host", args.host]
229
+ if args.no_open:
230
+ cmd.append("--no-open")
231
+ if args.reload:
232
+ cmd.append("--reload")
233
+
234
+ try:
235
+ # Run the server
236
+ subprocess.run(cmd, cwd=str(ROOT_DIR))
237
+ except KeyboardInterrupt:
238
+ print("\n\033[1;33m🛑 Artemis UI server stopped.\033[0m")
239
+
240
+
241
+ if __name__ == "__main__":
242
+ main()