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,217 @@
1
+ # Copyright 2025-2026 Minitap, Inc.
2
+ # Modifications 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
+ # Originally from mobile-use (https://github.com/minitap-ai/mobile-use).
17
+ # See third_party/mobile_use/METADATA for the upstream source and local modifications.
18
+
19
+ """Utilities for handling app locking and initial app launch logic."""
20
+
21
+ import asyncio
22
+
23
+ from artemis.context import AppLaunchResult, ArtemisContext
24
+ from artemis.controllers.unified_controller import UnifiedMobileController
25
+ from artemis.data_engine.trace import TraceSpan
26
+ from artemis.utils.app_launch_utils import current_foreground, observe_foreground
27
+ from third_party.mobile_use.utils.logger import get_logger
28
+
29
+ logger = get_logger(__name__)
30
+
31
+
32
+ async def _poll_for_app_ready(
33
+ ctx: ArtemisContext,
34
+ app_package: str,
35
+ max_poll_seconds: int = 15,
36
+ poll_interval: float = 1.0,
37
+ ) -> tuple[bool, str | None]:
38
+ """Poll for app to be ready after launch.
39
+
40
+ Treats mCurrentFocus=null as a loading state and keeps polling.
41
+ Only fails if we get a different (non-null) package or timeout.
42
+
43
+ Args:
44
+ ctx: Mobile use context
45
+ app_package: Expected package name
46
+ max_poll_seconds: Maximum time to poll (default: 15s)
47
+ poll_interval: Time between polls (default: 1s)
48
+
49
+ Returns:
50
+ Tuple of (success: bool, error_message: str | None)
51
+ """
52
+ polls = int(max_poll_seconds / poll_interval)
53
+
54
+ for i in range(polls):
55
+ ready, current_package, task = await observe_foreground(ctx, app_package)
56
+
57
+ if ready:
58
+ if current_package == app_package:
59
+ logger.success(f"App {app_package} is ready (took ~{i * poll_interval:.1f}s)")
60
+ else:
61
+ logger.success(
62
+ f"App {app_package} is ready (focused window belongs to"
63
+ f" '{current_package}', but the foreground {task.describe()} is owned"
64
+ f" by {app_package}, took ~{i * poll_interval:.1f}s)"
65
+ )
66
+ return True, None
67
+
68
+ if current_package is None:
69
+ logger.debug(f"Poll {i + 1}/{polls}: App loading (mCurrentFocus=null)...")
70
+ else:
71
+ logger.debug(
72
+ f"Poll {i + 1}/{polls}: Wrong app in foreground (expected"
73
+ f" '{app_package}', got '{current_package}', foreground"
74
+ f" {task.describe() if task else 'task unknown'}). Still waiting..."
75
+ )
76
+
77
+ if i < polls - 1:
78
+ await asyncio.sleep(poll_interval)
79
+
80
+ current_package, task = await current_foreground(ctx)
81
+ error_msg = (
82
+ f"Timeout waiting for {app_package} to load after {max_poll_seconds}s. "
83
+ f"Current foreground: {current_package}; foreground"
84
+ f" {task.describe() if task else 'task unknown'}"
85
+ )
86
+ logger.error(error_msg)
87
+ return False, error_msg
88
+
89
+
90
+ async def launch_app_with_retries(
91
+ ctx: ArtemisContext,
92
+ app_package: str,
93
+ max_retries: int = 3,
94
+ max_poll_seconds: int = 15,
95
+ ) -> tuple[bool, str | None]:
96
+ """Launch an app with retry logic and smart polling.
97
+
98
+ Args:
99
+ ctx: Mobile use context
100
+ app_package: Package name (Android) to launch
101
+ max_retries: Maximum number of launch attempts (default: 3)
102
+ max_poll_seconds: Maximum time to wait for app to load per attempt
103
+ (default: 15s)
104
+
105
+ Returns:
106
+ Tuple of (success: bool, error_message: str | None)
107
+ """
108
+
109
+ for attempt in range(1, max_retries + 1):
110
+ logger.info(f"Launch attempt {attempt}/{max_retries} for app {app_package}")
111
+
112
+ with TraceSpan(
113
+ name=f"Launch Attempt {attempt}",
114
+ trace_type="span",
115
+ ctx=ctx,
116
+ ) as span:
117
+ span.payload = {"attempt": attempt, "app_package": app_package}
118
+
119
+ controller = UnifiedMobileController(ctx)
120
+ if attempt > 1:
121
+ logger.warning(
122
+ f"Attempt {attempt - 1} failed. Force stopping"
123
+ f" '{app_package}' to clear frozen state before retrying..."
124
+ )
125
+ await controller.terminate_app(app_package)
126
+ await asyncio.sleep(1.0)
127
+
128
+ launch_success = await controller.launch_app(app_package)
129
+ if not launch_success:
130
+ error_msg = f"Failed to execute launch command for {app_package}"
131
+ logger.error(error_msg)
132
+ span.status = "failed"
133
+ span.error = error_msg
134
+ if attempt == max_retries:
135
+ return False, error_msg
136
+ await asyncio.sleep(2)
137
+ continue
138
+
139
+ await asyncio.sleep(1)
140
+
141
+ success, error_msg = await _poll_for_app_ready(ctx, app_package, max_poll_seconds)
142
+
143
+ if success:
144
+ span.status = "success"
145
+ span.result = "App is ready"
146
+ return True, None
147
+
148
+ span.status = "failed"
149
+ span.error = error_msg
150
+
151
+ if attempt < max_retries:
152
+ logger.warning(f"Attempt {attempt} failed: {error_msg}. Retrying...")
153
+ await asyncio.sleep(1)
154
+
155
+ error_msg = f"Failed to launch {app_package} after {max_retries} attempts"
156
+ logger.error(error_msg)
157
+ return False, error_msg
158
+
159
+
160
+ async def _handle_initial_app_launch(
161
+ ctx: ArtemisContext,
162
+ locked_app_package: str,
163
+ ) -> AppLaunchResult:
164
+ """Handle initial app launch verification and launching if needed.
165
+
166
+ If locked_app_package is set:
167
+ 1. Check if the app is already in the foreground
168
+ 2. If not, attempt to launch it (with retries)
169
+ 3. Return status with success/error information
170
+
171
+ Args:
172
+ ctx: Mobile use context
173
+ locked_app_package: Package name (Android) to lock to
174
+
175
+ Returns:
176
+ AppLaunchResult with launch status and error information
177
+ """
178
+ if not locked_app_package:
179
+ error_msg = f"Invalid locked_app_package: '{locked_app_package}'"
180
+ logger.error(error_msg)
181
+ return AppLaunchResult(
182
+ locked_app_package=locked_app_package,
183
+ locked_app_initial_launch_success=False,
184
+ locked_app_initial_launch_error=error_msg,
185
+ )
186
+
187
+ logger.info(f"Starting initial app launch for package: {locked_app_package}")
188
+
189
+ try:
190
+ already_foreground, current_package, _ = await observe_foreground(ctx, locked_app_package)
191
+ logger.info(f"Current foreground app: {current_package}")
192
+
193
+ if already_foreground:
194
+ logger.info(f"App {locked_app_package} is already in foreground")
195
+ return AppLaunchResult(
196
+ locked_app_package=locked_app_package,
197
+ locked_app_initial_launch_success=True,
198
+ locked_app_initial_launch_error=None,
199
+ )
200
+
201
+ logger.info(f"App {locked_app_package} not in foreground, attempting to launch")
202
+ success, error_msg = await launch_app_with_retries(ctx, locked_app_package)
203
+
204
+ return AppLaunchResult(
205
+ locked_app_package=locked_app_package,
206
+ locked_app_initial_launch_success=success,
207
+ locked_app_initial_launch_error=error_msg,
208
+ )
209
+
210
+ except Exception as e:
211
+ error_msg = f"Exception during initial app launch: {str(e)}"
212
+ logger.error(error_msg)
213
+ return AppLaunchResult(
214
+ locked_app_package=locked_app_package,
215
+ locked_app_initial_launch_success=False,
216
+ locked_app_initial_launch_error=error_msg,
217
+ )
@@ -0,0 +1,60 @@
1
+ # Copyright 2025-2026 Minitap, Inc.
2
+ # Modifications 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
+ # Originally from mobile-use (https://github.com/minitap-ai/mobile-use).
17
+ # See third_party/mobile_use/METADATA for the upstream source and local modifications.
18
+
19
+ from shutil import which
20
+ import sys
21
+
22
+ from adbutils import AdbClient
23
+ from rich.console import Console
24
+
25
+ from third_party.mobile_use.utils.logger import get_logger
26
+
27
+ logger = get_logger(__name__)
28
+
29
+
30
+ def display_device_status(console: Console, adb_client: AdbClient | None = None):
31
+ """Checks for connected devices and displays the status."""
32
+ console.print("\n[bold]📱 Device Status[/bold]")
33
+ devices = None
34
+ if adb_client is not None:
35
+ devices = adb_client.device_list()
36
+ if devices:
37
+ console.print("✅ [bold green]Android device(s) connected:[/bold green]")
38
+ for device in devices:
39
+ console.print(f" - {device.serial}")
40
+ else:
41
+ console.print("❌ [bold red]No Android device found.[/bold red]")
42
+ command = "emulator -avd <avd_name>"
43
+ if sys.platform not in ["win32", "darwin"]:
44
+ command = f"./{command}"
45
+ console.print(
46
+ f"You can start an emulator using a command like: [bold]'{command}'[/bold]"
47
+ )
48
+
49
+
50
+ def display_local_device_status(console: Console, host: str | None, port: int | None) -> None:
51
+ """Display connected Android devices, using a local ADB client when ``adb`` is available."""
52
+ adb_client = None
53
+ try:
54
+ if which("adb"):
55
+ adb_client = AdbClient(host=host or "localhost", port=port or 5037)
56
+ except Exception as exc:
57
+ # Optional cosmetic device-status display; run continues without it.
58
+ logger.debug(f"Could not create ADB client for device status display: {exc}")
59
+
60
+ display_device_status(console, adb_client=adb_client)
@@ -0,0 +1,158 @@
1
+ # Copyright 2025-2026 Minitap, Inc.
2
+ # Modifications 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
+ # Originally from mobile-use (https://github.com/minitap-ai/mobile-use).
17
+ # See third_party/mobile_use/METADATA for the upstream source and local modifications.
18
+
19
+ import asyncio
20
+ from collections.abc import Awaitable, Callable
21
+ from functools import wraps
22
+ import inspect
23
+ from typing import Any, TypeVar, cast, overload
24
+
25
+ R = TypeVar("R")
26
+
27
+
28
+ def wrap_with_callbacks_sync(
29
+ fn: Callable[..., R],
30
+ *,
31
+ before: Callable[..., None] | None = None,
32
+ on_success: Callable[[R], None] | None = None,
33
+ on_failure: Callable[[Exception], None] | None = None,
34
+ suppress_exceptions: bool = False,
35
+ ) -> Callable[..., R]:
36
+ @wraps(fn)
37
+ def wrapper(*args: Any, **kwargs: Any) -> R:
38
+ if before:
39
+ before()
40
+ try:
41
+ result = fn(*args, **kwargs)
42
+ if on_success:
43
+ on_success(result)
44
+ return result
45
+ except Exception as e:
46
+ if on_failure:
47
+ on_failure(e)
48
+ if suppress_exceptions:
49
+ return None # type: ignore
50
+ raise
51
+
52
+ return wrapper
53
+
54
+
55
+ def wrap_with_callbacks_async(
56
+ fn: Callable[..., Awaitable[R]],
57
+ *,
58
+ before: Callable[..., None] | None = None,
59
+ on_success: Callable[[R], None] | None = None,
60
+ on_failure: Callable[[Exception], None] | None = None,
61
+ suppress_exceptions: bool = False,
62
+ ) -> Callable[..., Awaitable[R]]:
63
+ @wraps(fn)
64
+ async def wrapper(*args: Any, **kwargs: Any) -> R:
65
+ if before:
66
+ before()
67
+ try:
68
+ result = await fn(*args, **kwargs)
69
+ if on_success:
70
+ on_success(result)
71
+ return result
72
+ except Exception as e:
73
+ if on_failure:
74
+ on_failure(e)
75
+ if suppress_exceptions:
76
+ return None # type: ignore
77
+ raise
78
+
79
+ return wrapper
80
+
81
+
82
+ @overload
83
+ def wrap_with_callbacks(
84
+ fn: Callable[..., Awaitable[R]],
85
+ *,
86
+ before: Callable[[], None] | None = ...,
87
+ on_success: Callable[[R], None] | None = ...,
88
+ on_failure: Callable[[Exception], None] | None = ...,
89
+ suppress_exceptions: bool = ...,
90
+ ) -> Callable[..., Awaitable[R]]: ...
91
+
92
+
93
+ @overload
94
+ def wrap_with_callbacks(
95
+ *,
96
+ before: Callable[..., None] | None = ...,
97
+ on_success: Callable[[Any], None] | None = ...,
98
+ on_failure: Callable[[Exception], None] | None = ...,
99
+ suppress_exceptions: bool = ...,
100
+ ) -> Callable[[Callable[..., R]], Callable[..., R]]: ...
101
+
102
+
103
+ @overload
104
+ def wrap_with_callbacks(
105
+ fn: Callable[..., R],
106
+ *,
107
+ before: Callable[[], None] | None = ...,
108
+ on_success: Callable[[R], None] | None = ...,
109
+ on_failure: Callable[[Exception], None] | None = ...,
110
+ suppress_exceptions: bool = ...,
111
+ ) -> Callable[..., R]: ...
112
+
113
+
114
+ def wrap_with_callbacks(
115
+ fn: Callable[..., Any] | None = None,
116
+ *,
117
+ before: Callable[[], None] | None = None,
118
+ on_success: Callable[[Any], None] | None = None,
119
+ on_failure: Callable[[Exception], None] | None = None,
120
+ suppress_exceptions: bool = False,
121
+ ) -> Any:
122
+ def decorator(func: Callable[..., Any]) -> Any:
123
+ if inspect.iscoroutinefunction(func):
124
+ return wrap_with_callbacks_async(
125
+ cast(Callable[..., Awaitable[Any]], func),
126
+ before=before,
127
+ on_success=on_success,
128
+ on_failure=on_failure,
129
+ suppress_exceptions=suppress_exceptions,
130
+ )
131
+ else:
132
+ return wrap_with_callbacks_sync(
133
+ cast(Callable[..., Any], func),
134
+ before=before,
135
+ on_success=on_success,
136
+ on_failure=on_failure,
137
+ suppress_exceptions=suppress_exceptions,
138
+ )
139
+
140
+ if fn is None:
141
+ return decorator
142
+ else:
143
+ return decorator(fn)
144
+
145
+
146
+ def agent_lifecycle_logging(
147
+ agent_name: str, logger: Any
148
+ ) -> Callable[[Callable[..., R]], Callable[..., R]]:
149
+ """Log the start, success and failure of an agent node call.
150
+
151
+ ``agent_name`` is the display name, e.g. ``"Planner"``.
152
+ """
153
+ label = f"{agent_name} Agent"
154
+ return wrap_with_callbacks(
155
+ before=lambda: logger.info(f"Starting {label}..."),
156
+ on_success=lambda _: logger.success(label),
157
+ on_failure=lambda _: logger.error(label),
158
+ )
@@ -0,0 +1,31 @@
1
+ # Copyright 2025-2026 Minitap, Inc.
2
+ # Modifications 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
+ # Originally from mobile-use (https://github.com/minitap-ai/mobile-use).
17
+ # See third_party/mobile_use/METADATA for the upstream source and local modifications.
18
+
19
+ import json
20
+ import re
21
+ from typing import IO
22
+
23
+
24
+ def strip_json_comments(text: str) -> str:
25
+ text = re.sub(r"//.*?$", "", text, flags=re.MULTILINE)
26
+ text = re.sub(r"/\*.*?\*/", "", text, flags=re.DOTALL)
27
+ return text
28
+
29
+
30
+ def load_jsonc(file: IO) -> dict:
31
+ return json.loads(strip_json_comments(file.read()))
@@ -0,0 +1,216 @@
1
+ # Copyright 2025-2026 Minitap, Inc.
2
+ # Modifications 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
+ # Originally from mobile-use (https://github.com/minitap-ai/mobile-use).
17
+ # See third_party/mobile_use/METADATA for the upstream source and local modifications.
18
+
19
+ from enum import Enum
20
+ import logging
21
+ import os
22
+ from pathlib import Path
23
+ import queue
24
+ import sys
25
+ import threading
26
+
27
+ from colorama import Fore, Style, init
28
+
29
+ from artemis.data_engine.context_vars import CURRENT_TRACE_ID
30
+
31
+ init(autoreset=True)
32
+
33
+
34
+ class LogLevel(Enum):
35
+ DEBUG = ("DEBUG", Fore.MAGENTA, "🔍")
36
+ INFO = ("INFO", Fore.WHITE, "ℹ")
37
+ SUCCESS = ("SUCCESS", Fore.GREEN, "✓")
38
+ WARNING = ("WARNING", Fore.YELLOW, "⚠")
39
+ ERROR = ("ERROR", Fore.RED, "❌")
40
+ CRITICAL = ("CRITICAL", Fore.RED + Style.BRIGHT, "💥")
41
+
42
+
43
+ class DataEngineHandler(logging.Handler):
44
+ def __init__(self, *args, **kwargs):
45
+ super().__init__(*args, **kwargs)
46
+ self._log_queue = queue.Queue(maxsize=5000)
47
+ self._worker_thread = threading.Thread(target=self._drain_queue, daemon=True)
48
+ self._worker_thread.start()
49
+
50
+ def _drain_queue(self):
51
+ while True:
52
+ item = self._log_queue.get()
53
+ if item is None:
54
+ break
55
+ engine_mod = sys.modules.get("artemis.data_engine.engine")
56
+ if engine_mod and getattr(engine_mod, "_CURRENT_DATA_ENGINE", None):
57
+ _CURRENT_DATA_ENGINE = engine_mod._CURRENT_DATA_ENGINE
58
+ if _CURRENT_DATA_ENGINE and _CURRENT_DATA_ENGINE.current_session_id:
59
+ record_name, message, level_name, formatted, parent_id = item
60
+ _CURRENT_DATA_ENGINE.record_trace(
61
+ type="log",
62
+ name=record_name,
63
+ payload={
64
+ "message": message,
65
+ "level": level_name,
66
+ "formatted": formatted,
67
+ },
68
+ status="success",
69
+ parent_trace_id=parent_id,
70
+ )
71
+ self._log_queue.task_done()
72
+
73
+ def emit(self, record):
74
+ engine_mod = sys.modules.get("artemis.data_engine.engine")
75
+ if not engine_mod or not getattr(engine_mod, "_CURRENT_DATA_ENGINE", None):
76
+ return
77
+ _CURRENT_DATA_ENGINE = engine_mod._CURRENT_DATA_ENGINE
78
+
79
+ if _CURRENT_DATA_ENGINE and _CURRENT_DATA_ENGINE.current_session_id:
80
+ log_entry = self.format(record)
81
+ log_level = getattr(record, "log_level", None)
82
+ if log_level and hasattr(log_level, "value"):
83
+ level_name, _color, _symbol = log_level.value
84
+ else:
85
+ level_name = record.levelname
86
+
87
+ parent_id = CURRENT_TRACE_ID.get()
88
+
89
+ try:
90
+ self._log_queue.put_nowait(
91
+ (
92
+ record.name,
93
+ record.getMessage(),
94
+ level_name,
95
+ log_entry,
96
+ parent_id,
97
+ )
98
+ )
99
+ except queue.Full as e:
100
+ raise RuntimeError(
101
+ "DataEngine log queue is full - cannot accept more log"
102
+ " entries without dropping records"
103
+ ) from e
104
+
105
+
106
+ class ArtemisLogger:
107
+ def __init__(
108
+ self,
109
+ name: str,
110
+ console_level: str = "INFO",
111
+ ):
112
+ """Initialize the ARTEMIS logger.
113
+
114
+ Args:
115
+ name: Logger name (usually __name__)
116
+ console_level: Minimum level for console output
117
+ """
118
+ self.name = name
119
+ self.logger = logging.getLogger(name)
120
+ self.logger.setLevel(logging.DEBUG)
121
+
122
+ self.logger.handlers.clear()
123
+ self.logger.propagate = False
124
+
125
+ self._setup_console_handler(console_level)
126
+
127
+ # Add DataEngineHandler
128
+ data_engine_handler = DataEngineHandler()
129
+ data_engine_handler.setLevel(logging.DEBUG)
130
+ formatter = logging.Formatter(fmt="%(asctime)s | %(name)s | %(levelname)s | %(message)s")
131
+ data_engine_handler.setFormatter(formatter)
132
+ self.logger.addHandler(data_engine_handler)
133
+
134
+ if os.environ.get("ARTEMIS_MCP_SERVER") == "true":
135
+ traces_dir = os.environ.get("ARTEMIS_TRACES_PATH", "traces")
136
+ log_path = Path(traces_dir) / "mcp_server.log"
137
+ log_path.parent.mkdir(parents=True, exist_ok=True)
138
+ file_handler = logging.FileHandler(log_path, encoding="utf-8")
139
+ file_handler.setLevel(logging.DEBUG)
140
+ file_handler.setFormatter(formatter)
141
+ self.logger.addHandler(file_handler)
142
+
143
+ def _setup_console_handler(self, level: str):
144
+ console_handler = logging.StreamHandler(sys.stderr)
145
+ console_handler.setLevel(getattr(logging, level.upper()))
146
+
147
+ console_formatter = ColoredFormatter()
148
+ console_handler.setFormatter(console_formatter)
149
+
150
+ self.logger.addHandler(console_handler)
151
+
152
+ def debug(self, message: str, **kwargs):
153
+ self.logger.debug(message, extra={"log_level": LogLevel.DEBUG}, **kwargs)
154
+
155
+ def info(self, message: str, **kwargs):
156
+ self.logger.info(message, extra={"log_level": LogLevel.INFO}, **kwargs)
157
+
158
+ def success(self, message: str, **kwargs):
159
+ self.logger.info(message, extra={"log_level": LogLevel.SUCCESS}, **kwargs)
160
+
161
+ def warning(self, message: str, **kwargs):
162
+ self.logger.warning(message, extra={"log_level": LogLevel.WARNING}, **kwargs)
163
+
164
+ def error(self, message: str, **kwargs):
165
+ self.logger.error(message, extra={"log_level": LogLevel.ERROR}, **kwargs)
166
+
167
+ def critical(self, message: str, **kwargs):
168
+ self.logger.critical(message, extra={"log_level": LogLevel.CRITICAL}, **kwargs)
169
+
170
+ def header(self, message: str, **_kwargs):
171
+ separator = "=" * 60
172
+ colored_separator = f"{Fore.CYAN}{separator}{Style.RESET_ALL}"
173
+ colored_message = f"{Fore.CYAN}{message}{Style.RESET_ALL}"
174
+
175
+ print(colored_separator, file=sys.stderr)
176
+ print(colored_message, file=sys.stderr)
177
+ print(colored_separator, file=sys.stderr)
178
+ self.logger.info(f"\n{separator}\n{message}\n{separator}")
179
+
180
+
181
+ class ColoredFormatter(logging.Formatter):
182
+ def format(self, record):
183
+ log_level = getattr(record, "log_level", LogLevel.INFO)
184
+ _level_name, color, symbol = log_level.value
185
+
186
+ colored_message = f"{color}{symbol} {record.getMessage()}{Style.RESET_ALL}"
187
+
188
+ return colored_message
189
+
190
+
191
+ _loggers = {}
192
+
193
+
194
+ def get_logger(
195
+ name: str,
196
+ console_level: str = "INFO",
197
+ ) -> ArtemisLogger:
198
+ """Get or create a logger instance.
199
+
200
+ Args:
201
+ name: Logger name (usually __name__)
202
+ log_file: Path to log file (defaults to logs/{name}.log)
203
+ console_level: Minimum level for console output
204
+ file_level: Minimum level for file output
205
+ enable_file_logging: Whether to enable file logging
206
+
207
+ Returns:
208
+ ArtemisLogger instance
209
+ """
210
+ if name not in _loggers:
211
+ _loggers[name] = ArtemisLogger(
212
+ name=name,
213
+ console_level=console_level,
214
+ )
215
+
216
+ return _loggers[name]