android-midscene-automation 0.2.4 → 1.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (525) hide show
  1. package/CHANGELOG.md +7 -0
  2. package/README.md +35 -0
  3. package/artemis-main/.dockerignore +15 -0
  4. package/artemis-main/.env.example +34 -0
  5. package/artemis-main/CONTRIBUTING.md +35 -0
  6. package/artemis-main/Dockerfile +68 -0
  7. package/artemis-main/LICENSE +202 -0
  8. package/artemis-main/Makefile +115 -0
  9. package/artemis-main/README.md +322 -0
  10. package/artemis-main/README_CN.md +320 -0
  11. package/artemis-main/apps/__init__.py +15 -0
  12. package/artemis-main/apps/admin_console/__init__.py +19 -0
  13. package/artemis-main/apps/admin_console/core/__init__.py +13 -0
  14. package/artemis-main/apps/admin_console/core/config.py +43 -0
  15. package/artemis-main/apps/admin_console/core/security.py +178 -0
  16. package/artemis-main/apps/admin_console/core/state.py +243 -0
  17. package/artemis-main/apps/admin_console/database/__init__.py +13 -0
  18. package/artemis-main/apps/admin_console/database/connection.py +60 -0
  19. package/artemis-main/apps/admin_console/database/repositories/__init__.py +13 -0
  20. package/artemis-main/apps/admin_console/database/repositories/session_repository.py +564 -0
  21. package/artemis-main/apps/admin_console/database/repositories/step_repository.py +512 -0
  22. package/artemis-main/apps/admin_console/database/repositories/trace_repository.py +133 -0
  23. package/artemis-main/apps/admin_console/index.html +3787 -0
  24. package/artemis-main/apps/admin_console/replay_manager.py +2585 -0
  25. package/artemis-main/apps/admin_console/routers/__init__.py +13 -0
  26. package/artemis-main/apps/admin_console/routers/media.py +198 -0
  27. package/artemis-main/apps/admin_console/routers/replay.py +91 -0
  28. package/artemis-main/apps/admin_console/routers/sessions.py +239 -0
  29. package/artemis-main/apps/admin_console/routers/steps.py +109 -0
  30. package/artemis-main/apps/admin_console/routers/stream.py +56 -0
  31. package/artemis-main/apps/admin_console/routers/system.py +761 -0
  32. package/artemis-main/apps/admin_console/routers/tasks.py +517 -0
  33. package/artemis-main/apps/admin_console/schemas/__init__.py +13 -0
  34. package/artemis-main/apps/admin_console/schemas/task_schema.py +47 -0
  35. package/artemis-main/apps/admin_console/server.py +508 -0
  36. package/artemis-main/apps/admin_console/services/__init__.py +13 -0
  37. package/artemis-main/apps/admin_console/services/device_stream_service.py +137 -0
  38. package/artemis-main/apps/admin_console/services/ipc_service.py +262 -0
  39. package/artemis-main/apps/admin_console/services/media_service.py +623 -0
  40. package/artemis-main/apps/admin_console/services/model_service.py +148 -0
  41. package/artemis-main/apps/admin_console/services/task_preset_catalog.py +514 -0
  42. package/artemis-main/apps/admin_console/services/task_queue_service.py +1719 -0
  43. package/artemis-main/apps/admin_console/services/worker_process_io.py +144 -0
  44. package/artemis-main/apps/showcase_ui/README.md +31 -0
  45. package/artemis-main/apps/showcase_ui/angular.json +132 -0
  46. package/artemis-main/apps/showcase_ui/package-lock.json +9472 -0
  47. package/artemis-main/apps/showcase_ui/package.json +39 -0
  48. package/artemis-main/apps/showcase_ui/proxy.conf.json +23 -0
  49. package/artemis-main/apps/showcase_ui/public/favicon.ico +0 -0
  50. package/artemis-main/apps/showcase_ui/public/logo.png +0 -0
  51. package/artemis-main/apps/showcase_ui/public/logo.svg +56 -0
  52. package/artemis-main/apps/showcase_ui/src/app/app.component.html +24 -0
  53. package/artemis-main/apps/showcase_ui/src/app/app.component.scss +49 -0
  54. package/artemis-main/apps/showcase_ui/src/app/app.component.spec.ts +42 -0
  55. package/artemis-main/apps/showcase_ui/src/app/app.component.ts +32 -0
  56. package/artemis-main/apps/showcase_ui/src/app/app.config.ts +29 -0
  57. package/artemis-main/apps/showcase_ui/src/app/app.routes.ts +27 -0
  58. package/artemis-main/apps/showcase_ui/src/app/components/agent-stream/agent-stream-startup.spec.ts +108 -0
  59. package/artemis-main/apps/showcase_ui/src/app/components/agent-stream/agent-stream.component.html +1436 -0
  60. package/artemis-main/apps/showcase_ui/src/app/components/agent-stream/agent-stream.component.scss +3973 -0
  61. package/artemis-main/apps/showcase_ui/src/app/components/agent-stream/agent-stream.component.ts +2235 -0
  62. package/artemis-main/apps/showcase_ui/src/app/components/chat-interface/chat-interface.component.html +380 -0
  63. package/artemis-main/apps/showcase_ui/src/app/components/chat-interface/chat-interface.component.scss +1171 -0
  64. package/artemis-main/apps/showcase_ui/src/app/components/chat-interface/chat-interface.component.ts +270 -0
  65. package/artemis-main/apps/showcase_ui/src/app/components/floating-video-player/floating-video-player.component.html +433 -0
  66. package/artemis-main/apps/showcase_ui/src/app/components/floating-video-player/floating-video-player.component.scss +987 -0
  67. package/artemis-main/apps/showcase_ui/src/app/components/floating-video-player/floating-video-player.component.ts +632 -0
  68. package/artemis-main/apps/showcase_ui/src/app/components/legacy-agent-stream/legacy-agent-stream.component.html +130 -0
  69. package/artemis-main/apps/showcase_ui/src/app/components/legacy-agent-stream/legacy-agent-stream.component.scss +406 -0
  70. package/artemis-main/apps/showcase_ui/src/app/components/legacy-agent-stream/legacy-agent-stream.component.ts +74 -0
  71. package/artemis-main/apps/showcase_ui/src/app/components/nav-switcher/nav-switcher.component.scss +115 -0
  72. package/artemis-main/apps/showcase_ui/src/app/components/nav-switcher/nav-switcher.component.ts +52 -0
  73. package/artemis-main/apps/showcase_ui/src/app/core/data/smart-tasks.data.ts +334 -0
  74. package/artemis-main/apps/showcase_ui/src/app/core/i18n/language.service.spec.ts +55 -0
  75. package/artemis-main/apps/showcase_ui/src/app/core/i18n/language.service.ts +61 -0
  76. package/artemis-main/apps/showcase_ui/src/app/core/i18n/translate.pipe.ts +11 -0
  77. package/artemis-main/apps/showcase_ui/src/app/core/i18n/zh-cn.ts +651 -0
  78. package/artemis-main/apps/showcase_ui/src/app/core/models/markdown.model.ts +43 -0
  79. package/artemis-main/apps/showcase_ui/src/app/core/models/pro-tuning.model.spec.ts +47 -0
  80. package/artemis-main/apps/showcase_ui/src/app/core/models/pro-tuning.model.ts +165 -0
  81. package/artemis-main/apps/showcase_ui/src/app/core/models/session.model.ts +85 -0
  82. package/artemis-main/apps/showcase_ui/src/app/core/models/stream.model.ts +246 -0
  83. package/artemis-main/apps/showcase_ui/src/app/core/models/system.model.ts +118 -0
  84. package/artemis-main/apps/showcase_ui/src/app/core/services/task-recommendation.service.ts +111 -0
  85. package/artemis-main/apps/showcase_ui/src/app/pages/home/home.component.html +2130 -0
  86. package/artemis-main/apps/showcase_ui/src/app/pages/home/home.component.scss +5204 -0
  87. package/artemis-main/apps/showcase_ui/src/app/pages/home/home.component.ts +1189 -0
  88. package/artemis-main/apps/showcase_ui/src/app/pages/legacy-workspace/legacy-workspace.component.html +39 -0
  89. package/artemis-main/apps/showcase_ui/src/app/pages/legacy-workspace/legacy-workspace.component.scss +151 -0
  90. package/artemis-main/apps/showcase_ui/src/app/pages/legacy-workspace/legacy-workspace.component.ts +58 -0
  91. package/artemis-main/apps/showcase_ui/src/app/pages/workspace/workspace.component.html +201 -0
  92. package/artemis-main/apps/showcase_ui/src/app/pages/workspace/workspace.component.scss +886 -0
  93. package/artemis-main/apps/showcase_ui/src/app/pages/workspace/workspace.component.ts +324 -0
  94. package/artemis-main/apps/showcase_ui/src/app/services/agent.service.spec.ts +418 -0
  95. package/artemis-main/apps/showcase_ui/src/app/services/agent.service.ts +2012 -0
  96. package/artemis-main/apps/showcase_ui/src/app/services/system.service.spec.ts +173 -0
  97. package/artemis-main/apps/showcase_ui/src/app/services/system.service.ts +620 -0
  98. package/artemis-main/apps/showcase_ui/src/app/utils/action-formatter.util.spec.ts +513 -0
  99. package/artemis-main/apps/showcase_ui/src/app/utils/action-formatter.util.ts +1015 -0
  100. package/artemis-main/apps/showcase_ui/src/app/utils/image-overlay.util.spec.ts +85 -0
  101. package/artemis-main/apps/showcase_ui/src/app/utils/image-overlay.util.ts +662 -0
  102. package/artemis-main/apps/showcase_ui/src/app/utils/markdown-parser.util.spec.ts +70 -0
  103. package/artemis-main/apps/showcase_ui/src/app/utils/markdown-parser.util.ts +383 -0
  104. package/artemis-main/apps/showcase_ui/src/app/utils/recording-timeline.util.spec.ts +107 -0
  105. package/artemis-main/apps/showcase_ui/src/app/utils/recording-timeline.util.ts +123 -0
  106. package/artemis-main/apps/showcase_ui/src/app/utils/run-info.util.spec.ts +92 -0
  107. package/artemis-main/apps/showcase_ui/src/app/utils/run-info.util.ts +116 -0
  108. package/artemis-main/apps/showcase_ui/src/app/utils/stream-aggregator.util.spec.ts +640 -0
  109. package/artemis-main/apps/showcase_ui/src/app/utils/stream-aggregator.util.ts +1021 -0
  110. package/artemis-main/apps/showcase_ui/src/app/utils/tool-formatter.util.spec.ts +375 -0
  111. package/artemis-main/apps/showcase_ui/src/app/utils/tool-formatter.util.ts +1419 -0
  112. package/artemis-main/apps/showcase_ui/src/index.html +37 -0
  113. package/artemis-main/apps/showcase_ui/src/main.ts +22 -0
  114. package/artemis-main/apps/showcase_ui/src/styles.scss +60 -0
  115. package/artemis-main/apps/showcase_ui/tsconfig.app.json +23 -0
  116. package/artemis-main/apps/showcase_ui/tsconfig.json +27 -0
  117. package/artemis-main/apps/showcase_ui/tsconfig.spec.json +23 -0
  118. package/artemis-main/artemis/__init__.py +44 -0
  119. package/artemis-main/artemis/__main__.py +18 -0
  120. package/artemis-main/artemis/_legacy_imports.py +94 -0
  121. package/artemis-main/artemis/_version.py +30 -0
  122. package/artemis-main/artemis/agents/__init__.py +17 -0
  123. package/artemis-main/artemis/agents/checker/checker.json +9 -0
  124. package/artemis-main/artemis/agents/checker/checker.py +859 -0
  125. package/artemis-main/artemis/agents/diagnoser/diagnoser.json +23 -0
  126. package/artemis-main/artemis/agents/diagnoser/diagnoser.py +608 -0
  127. package/artemis-main/artemis/agents/explorer/constants.py +52 -0
  128. package/artemis-main/artemis/agents/explorer/explorer.json +57 -0
  129. package/artemis-main/artemis/agents/explorer/explorer.py +473 -0
  130. package/artemis-main/artemis/agents/explorer/geometry.py +73 -0
  131. package/artemis-main/artemis/agents/explorer/native_runner.py +878 -0
  132. package/artemis-main/artemis/agents/explorer/perception_tools.py +632 -0
  133. package/artemis-main/artemis/agents/explorer/run_setup.py +403 -0
  134. package/artemis-main/artemis/agents/explorer/screen_index.py +266 -0
  135. package/artemis-main/artemis/agents/explorer/tiers.py +110 -0
  136. package/artemis-main/artemis/agents/explorer/tool_declarations.py +394 -0
  137. package/artemis-main/artemis/agents/explorer/universal_runner.py +317 -0
  138. package/artemis-main/artemis/agents/flash/context_compressor.py +452 -0
  139. package/artemis-main/artemis/agents/flash/flash_runner.md +51 -0
  140. package/artemis-main/artemis/agents/flash/flash_summarizer.md +53 -0
  141. package/artemis-main/artemis/agents/flash/flash_summarizer_single.md +54 -0
  142. package/artemis-main/artemis/agents/flash/runner.py +1167 -0
  143. package/artemis-main/artemis/agents/flash/summarizer.py +511 -0
  144. package/artemis-main/artemis/agents/history_analyzer/history_analyzer.md +8 -0
  145. package/artemis-main/artemis/agents/history_analyzer/history_analyzer.py +182 -0
  146. package/artemis-main/artemis/agents/image_processor/image_processor.md +74 -0
  147. package/artemis-main/artemis/agents/image_processor/image_processor.py +322 -0
  148. package/artemis-main/artemis/agents/log_analyzer/log_analyzer.md +15 -0
  149. package/artemis-main/artemis/agents/log_analyzer/log_analyzer.py +403 -0
  150. package/artemis-main/artemis/agents/log_analyzer/log_reader_sub_agent.md +8 -0
  151. package/artemis-main/artemis/agents/log_analyzer/output_analyzer.md +11 -0
  152. package/artemis-main/artemis/agents/log_analyzer/output_analyzer.py +163 -0
  153. package/artemis-main/artemis/agents/object_detector/object_detector.json +9 -0
  154. package/artemis-main/artemis/agents/object_detector/object_detector.py +211 -0
  155. package/artemis-main/artemis/agents/operator/operator.json +3 -0
  156. package/artemis-main/artemis/agents/operator/operator.py +1394 -0
  157. package/artemis-main/artemis/agents/operator/prompts.py +1064 -0
  158. package/artemis-main/artemis/agents/outputter/outputter.md +36 -0
  159. package/artemis-main/artemis/agents/outputter/outputter.py +552 -0
  160. package/artemis-main/artemis/agents/planner/planner.json +32 -0
  161. package/artemis-main/artemis/agents/planner/planner.py +436 -0
  162. package/artemis-main/artemis/agents/prompt_assembly.py +79 -0
  163. package/artemis-main/artemis/agents/summarizer/summarizer.py +190 -0
  164. package/artemis-main/artemis/agents/validator/action_execution.py +98 -0
  165. package/artemis-main/artemis/agents/validator/categories.py +33 -0
  166. package/artemis-main/artemis/agents/validator/execution_loop.py +488 -0
  167. package/artemis-main/artemis/agents/validator/incidents.py +120 -0
  168. package/artemis-main/artemis/agents/validator/pixel_safety_net.md +15 -0
  169. package/artemis-main/artemis/agents/validator/precondition_pixel.py +320 -0
  170. package/artemis-main/artemis/agents/validator/precondition_xml.py +672 -0
  171. package/artemis-main/artemis/agents/validator/tool_declarations.py +332 -0
  172. package/artemis-main/artemis/agents/validator/validator.py +188 -0
  173. package/artemis-main/artemis/agents/video_analyzer/__init__.py +15 -0
  174. package/artemis-main/artemis/agents/video_analyzer/audio_native.py +543 -0
  175. package/artemis-main/artemis/agents/video_analyzer/blackboard.py +821 -0
  176. package/artemis-main/artemis/agents/video_analyzer/chunk_agentic.py +455 -0
  177. package/artemis-main/artemis/agents/video_analyzer/chunk_conversation.py +491 -0
  178. package/artemis-main/artemis/agents/video_analyzer/chunk_native.py +437 -0
  179. package/artemis-main/artemis/agents/video_analyzer/conflict_resolution.py +137 -0
  180. package/artemis-main/artemis/agents/video_analyzer/gemini_files.py +162 -0
  181. package/artemis-main/artemis/agents/video_analyzer/native_coordinator.py +541 -0
  182. package/artemis-main/artemis/agents/video_analyzer/reliability.py +128 -0
  183. package/artemis-main/artemis/agents/video_analyzer/universal_engine.py +487 -0
  184. package/artemis-main/artemis/agents/video_analyzer/universal_tools.py +323 -0
  185. package/artemis-main/artemis/agents/video_analyzer/video_analyzer.md +51 -0
  186. package/artemis-main/artemis/agents/video_analyzer/video_analyzer.py +875 -0
  187. package/artemis-main/artemis/agents/video_analyzer/video_sub_agent.md +15 -0
  188. package/artemis-main/artemis/agents/video_analyzer/video_sub_agent_audio.md +12 -0
  189. package/artemis-main/artemis/clients/accessibility_client.py +438 -0
  190. package/artemis-main/artemis/clients/screen_client_factory.py +403 -0
  191. package/artemis-main/artemis/config/__init__.py +309 -0
  192. package/artemis-main/artemis/config/agent.py +1098 -0
  193. package/artemis-main/artemis/config/constants.py +151 -0
  194. package/artemis-main/artemis/config/core/__init__.py +24 -0
  195. package/artemis-main/artemis/config/core/engine.py +118 -0
  196. package/artemis-main/artemis/config/core/interpolator.py +78 -0
  197. package/artemis-main/artemis/config/llm.py +267 -0
  198. package/artemis-main/artemis/config/paths.py +244 -0
  199. package/artemis-main/artemis/config/runtime.py +197 -0
  200. package/artemis-main/artemis/config/settings.py +315 -0
  201. package/artemis-main/artemis/constants.py +47 -0
  202. package/artemis-main/artemis/context.py +289 -0
  203. package/artemis-main/artemis/controllers/__init__.py +41 -0
  204. package/artemis-main/artemis/controllers/controller_factory.py +41 -0
  205. package/artemis-main/artemis/controllers/device_controller.py +29 -0
  206. package/artemis-main/artemis/controllers/types.py +37 -0
  207. package/artemis-main/artemis/controllers/unified_controller.py +649 -0
  208. package/artemis-main/artemis/core/__init__.py +21 -0
  209. package/artemis-main/artemis/core/diagnostics/__init__.py +90 -0
  210. package/artemis-main/artemis/core/diagnostics/adb_keys.py +270 -0
  211. package/artemis-main/artemis/core/diagnostics/adb_server_connection.py +330 -0
  212. package/artemis-main/artemis/core/diagnostics/device_smoke.py +364 -0
  213. package/artemis-main/artemis/core/diagnostics/emulator_manager.py +501 -0
  214. package/artemis-main/artemis/core/diagnostics/engine.py +438 -0
  215. package/artemis-main/artemis/core/diagnostics/hierarchy_parity.py +216 -0
  216. package/artemis-main/artemis/core/diagnostics/probes/adb_probe.py +887 -0
  217. package/artemis-main/artemis/core/diagnostics/probes/base.py +45 -0
  218. package/artemis-main/artemis/core/diagnostics/probes/credentials_probe.py +296 -0
  219. package/artemis-main/artemis/core/diagnostics/probes/host_probe.py +427 -0
  220. package/artemis-main/artemis/core/diagnostics/probes/runtime_probe.py +169 -0
  221. package/artemis-main/artemis/core/diagnostics/probes/toolchain_probe.py +139 -0
  222. package/artemis-main/artemis/core/diagnostics/readiness.py +83 -0
  223. package/artemis-main/artemis/core/diagnostics/schema.py +118 -0
  224. package/artemis-main/artemis/core/tool_declaration.py +43 -0
  225. package/artemis-main/artemis/core/tool_failure.py +56 -0
  226. package/artemis-main/artemis/data_engine/context_vars.py +24 -0
  227. package/artemis-main/artemis/data_engine/engine.py +1615 -0
  228. package/artemis-main/artemis/data_engine/history_reader.py +142 -0
  229. package/artemis-main/artemis/data_engine/models.py +131 -0
  230. package/artemis-main/artemis/data_engine/storage.py +1245 -0
  231. package/artemis-main/artemis/data_engine/trace.py +727 -0
  232. package/artemis-main/artemis/drivers/__init__.py +48 -0
  233. package/artemis-main/artemis/drivers/android/adb_driver.py +507 -0
  234. package/artemis-main/artemis/drivers/android/hierarchy.py +64 -0
  235. package/artemis-main/artemis/drivers/android/input_ime.py +120 -0
  236. package/artemis-main/artemis/drivers/android/recorder.py +57 -0
  237. package/artemis-main/artemis/drivers/base.py +290 -0
  238. package/artemis-main/artemis/drivers/cloud/cloud_driver.py +117 -0
  239. package/artemis-main/artemis/drivers/factory.py +80 -0
  240. package/artemis-main/artemis/drivers/mock/mock_driver.py +198 -0
  241. package/artemis-main/artemis/drivers/types.py +67 -0
  242. package/artemis-main/artemis/graph/checkpoints.py +1243 -0
  243. package/artemis-main/artemis/graph/graph.py +1055 -0
  244. package/artemis-main/artemis/graph/perception.py +234 -0
  245. package/artemis-main/artemis/graph/state.py +151 -0
  246. package/artemis-main/artemis/graph/visibility.py +238 -0
  247. package/artemis-main/artemis/interfaces/cli/__init__.py +19 -0
  248. package/artemis-main/artemis/interfaces/cli/commands/__init__.py +15 -0
  249. package/artemis-main/artemis/interfaces/cli/commands/batch.py +278 -0
  250. package/artemis-main/artemis/interfaces/cli/commands/doctor.py +468 -0
  251. package/artemis-main/artemis/interfaces/cli/commands/helper.py +226 -0
  252. package/artemis-main/artemis/interfaces/cli/commands/init.py +193 -0
  253. package/artemis-main/artemis/interfaces/cli/commands/mcp.py +862 -0
  254. package/artemis-main/artemis/interfaces/cli/commands/run.py +463 -0
  255. package/artemis-main/artemis/interfaces/cli/commands/server.py +151 -0
  256. package/artemis-main/artemis/interfaces/cli/commands/server_lifecycle.py +328 -0
  257. package/artemis-main/artemis/interfaces/cli/commands/trace.py +111 -0
  258. package/artemis-main/artemis/interfaces/cli/commands/ui.py +404 -0
  259. package/artemis-main/artemis/interfaces/cli/main.py +109 -0
  260. package/artemis-main/artemis/interfaces/sdk/__init__.py +31 -0
  261. package/artemis-main/artemis/interfaces/sdk/client.py +19 -0
  262. package/artemis-main/artemis/interfaces/sdk/task.py +72 -0
  263. package/artemis-main/artemis/llm/__init__.py +25 -0
  264. package/artemis-main/artemis/llm/fake_model.py +179 -0
  265. package/artemis-main/artemis/llm/google/__init__.py +57 -0
  266. package/artemis-main/artemis/llm/google/provider.py +185 -0
  267. package/artemis-main/artemis/llm/google/usage.py +148 -0
  268. package/artemis-main/artemis/llm/parser.py +32 -0
  269. package/artemis-main/artemis/llm/reliability.py +267 -0
  270. package/artemis-main/artemis/llm/router.py +389 -0
  271. package/artemis-main/artemis/llm/schemas.py +36 -0
  272. package/artemis-main/artemis/llm/structured.py +196 -0
  273. package/artemis-main/artemis/main.py +58 -0
  274. package/artemis-main/artemis/mcp/__init__.py +20 -0
  275. package/artemis-main/artemis/mcp/action_executor.py +760 -0
  276. package/artemis-main/artemis/mcp/action_manifest.py +320 -0
  277. package/artemis-main/artemis/mcp/action_names.py +172 -0
  278. package/artemis-main/artemis/mcp/action_server.py +202 -0
  279. package/artemis-main/artemis/mcp/action_session.py +245 -0
  280. package/artemis-main/artemis/mcp/action_specs.py +868 -0
  281. package/artemis-main/artemis/mcp/action_types.py +86 -0
  282. package/artemis-main/artemis/mcp/actuators/__init__.py +21 -0
  283. package/artemis-main/artemis/mcp/actuators/adb.py +414 -0
  284. package/artemis-main/artemis/mcp/actuators/base.py +113 -0
  285. package/artemis-main/artemis/mcp/actuators/mock.py +70 -0
  286. package/artemis-main/artemis/mcp/adb_server.py +490 -0
  287. package/artemis-main/artemis/mcp/observation.py +133 -0
  288. package/artemis-main/artemis/mcp/xml_search_server.py +297 -0
  289. package/artemis-main/artemis/memory/__init__.py +90 -0
  290. package/artemis-main/artemis/memory/chunking.py +1843 -0
  291. package/artemis-main/artemis/memory/context_policy.py +226 -0
  292. package/artemis-main/artemis/memory/step_capsule.md +73 -0
  293. package/artemis-main/artemis/memory/step_memory.py +281 -0
  294. package/artemis-main/artemis/memory/transcript.py +798 -0
  295. package/artemis-main/artemis/platform/__init__.py +61 -0
  296. package/artemis-main/artemis/platform/base.py +104 -0
  297. package/artemis-main/artemis/platform/darwin.py +184 -0
  298. package/artemis-main/artemis/platform/linux.py +207 -0
  299. package/artemis-main/artemis/platform/windows.py +193 -0
  300. package/artemis-main/artemis/resources/__init__.py +46 -0
  301. package/artemis-main/artemis/runtime/__init__.py +108 -0
  302. package/artemis-main/artemis/runtime/adb_endpoint.py +173 -0
  303. package/artemis-main/artemis/runtime/awake_lease.py +283 -0
  304. package/artemis-main/artemis/runtime/awake_service.py +343 -0
  305. package/artemis-main/artemis/runtime/cancel_requests.py +254 -0
  306. package/artemis-main/artemis/runtime/daemon_client.py +422 -0
  307. package/artemis-main/artemis/runtime/device_lock.py +1013 -0
  308. package/artemis-main/artemis/runtime/device_pool.py +478 -0
  309. package/artemis-main/artemis/runtime/helper_manager.py +1030 -0
  310. package/artemis-main/artemis/runtime/process_probe.py +98 -0
  311. package/artemis-main/artemis/runtime/server_lifecycle.py +435 -0
  312. package/artemis-main/artemis/runtime/supervisor.py +121 -0
  313. package/artemis-main/artemis/runtime/trace_store.py +375 -0
  314. package/artemis-main/artemis/sdk/__init__.py +26 -0
  315. package/artemis-main/artemis/sdk/agent.py +485 -0
  316. package/artemis-main/artemis/sdk/builders/__init__.py +29 -0
  317. package/artemis-main/artemis/sdk/builders/agent_config_builder.py +474 -0
  318. package/artemis-main/artemis/sdk/constants.py +15 -0
  319. package/artemis-main/artemis/sdk/run_outcome.py +58 -0
  320. package/artemis-main/artemis/sdk/types/__init__.py +61 -0
  321. package/artemis-main/artemis/sdk/types/agent.py +89 -0
  322. package/artemis-main/artemis/sdk/utils.py +28 -0
  323. package/artemis-main/artemis/services/llm.py +1040 -0
  324. package/artemis-main/artemis/services/token_meter.py +267 -0
  325. package/artemis-main/artemis/telemetry/__init__.py +35 -0
  326. package/artemis-main/artemis/telemetry/models.py +64 -0
  327. package/artemis-main/artemis/telemetry/replay.py +34 -0
  328. package/artemis-main/artemis/telemetry/storage/json_storage.py +47 -0
  329. package/artemis-main/artemis/telemetry/tracer.py +85 -0
  330. package/artemis-main/artemis/toolchain/__init__.py +52 -0
  331. package/artemis-main/artemis/toolchain/descriptors.py +92 -0
  332. package/artemis-main/artemis/toolchain/resolver.py +185 -0
  333. package/artemis-main/artemis/tools/base.py +232 -0
  334. package/artemis-main/artemis/tools/command_tool.py +1240 -0
  335. package/artemis-main/artemis/tools/committee_prompts.json +5 -0
  336. package/artemis-main/artemis/tools/committee_tool.py +422 -0
  337. package/artemis-main/artemis/tools/diagnoser_submit_answer_tool.py +87 -0
  338. package/artemis-main/artemis/tools/diagnostic_tool.py +120 -0
  339. package/artemis-main/artemis/tools/explorer_tool.py +581 -0
  340. package/artemis-main/artemis/tools/history/__init__.py +398 -0
  341. package/artemis-main/artemis/tools/history/replay.py +120 -0
  342. package/artemis-main/artemis/tools/history/screenshot.py +139 -0
  343. package/artemis-main/artemis/tools/history/search.py +563 -0
  344. package/artemis-main/artemis/tools/image_processor_tool.py +124 -0
  345. package/artemis-main/artemis/tools/index.py +47 -0
  346. package/artemis-main/artemis/tools/log_tool.py +108 -0
  347. package/artemis-main/artemis/tools/mobile/launch_app.py +132 -0
  348. package/artemis-main/artemis/tools/mobile/log_utils.py +138 -0
  349. package/artemis-main/artemis/tools/mobile/ocr.py +202 -0
  350. package/artemis-main/artemis/tools/mobile/read_hierarchy.py +108 -0
  351. package/artemis-main/artemis/tools/mobile/read_logs.py +116 -0
  352. package/artemis-main/artemis/tools/mobile/search_logs.py +260 -0
  353. package/artemis-main/artemis/tools/object_detection_tool.py +244 -0
  354. package/artemis-main/artemis/tools/scratchpad.py +819 -0
  355. package/artemis-main/artemis/tools/system/adb_shell.py +31 -0
  356. package/artemis-main/artemis/tools/tool_wrapper.py +270 -0
  357. package/artemis-main/artemis/tools/video_tool.py +261 -0
  358. package/artemis-main/artemis/tools/wait_tool.py +97 -0
  359. package/artemis-main/artemis/utils/app_launch_utils.py +207 -0
  360. package/artemis-main/artemis/utils/coordinates.py +606 -0
  361. package/artemis-main/artemis/utils/credentials_validator.py +201 -0
  362. package/artemis-main/artemis/utils/cv_canvas.py +189 -0
  363. package/artemis-main/artemis/utils/cython_compat.py +45 -0
  364. package/artemis-main/artemis/utils/element_hit_test.py +80 -0
  365. package/artemis-main/artemis/utils/image_diff.py +335 -0
  366. package/artemis-main/artemis/utils/image_hash.py +80 -0
  367. package/artemis-main/artemis/utils/notes.py +470 -0
  368. package/artemis-main/artemis/utils/ocr_api.py +105 -0
  369. package/artemis-main/artemis/utils/ocr_xml_fusion.py +318 -0
  370. package/artemis-main/artemis/utils/plan_grammar.py +513 -0
  371. package/artemis-main/artemis/utils/python_executor.py +213 -0
  372. package/artemis-main/artemis/utils/startup_progress.py +60 -0
  373. package/artemis-main/artemis/utils/task_tree.py +1174 -0
  374. package/artemis-main/artemis/utils/text.py +37 -0
  375. package/artemis-main/artemis/utils/ui_filter.py +651 -0
  376. package/artemis-main/artemis/utils/video.py +685 -0
  377. package/artemis-main/artemis/utils/visualization.py +802 -0
  378. package/artemis-main/config/artemis.example.jsonc +12 -0
  379. package/artemis-main/mcp_server/README.md +111 -0
  380. package/artemis-main/mcp_server/__init__.py +21 -0
  381. package/artemis-main/mcp_server/__main__.py +20 -0
  382. package/artemis-main/mcp_server/background/__init__.py +15 -0
  383. package/artemis-main/mcp_server/background/task_runner.py +453 -0
  384. package/artemis-main/mcp_server/base.py +49 -0
  385. package/artemis-main/mcp_server/notifiers/__init__.py +74 -0
  386. package/artemis-main/mcp_server/notifiers/agentapi.py +289 -0
  387. package/artemis-main/mcp_server/notifiers/base.py +45 -0
  388. package/artemis-main/mcp_server/notifiers/composite.py +87 -0
  389. package/artemis-main/mcp_server/notifiers/desktop.py +108 -0
  390. package/artemis-main/mcp_server/notifiers/file.py +69 -0
  391. package/artemis-main/mcp_server/notifiers/script.py +90 -0
  392. package/artemis-main/mcp_server/notifiers/webhook.py +87 -0
  393. package/artemis-main/mcp_server/rules.md +68 -0
  394. package/artemis-main/mcp_server/server.py +106 -0
  395. package/artemis-main/mcp_server/tools/__init__.py +29 -0
  396. package/artemis-main/mcp_server/tools/device_state.py +107 -0
  397. package/artemis-main/mcp_server/tools/diagnose.py +1249 -0
  398. package/artemis-main/mcp_server/tools/inspect_trace.py +394 -0
  399. package/artemis-main/mcp_server/tools/task_manager.py +627 -0
  400. package/artemis-main/mcp_server/tools/task_runner.py +548 -0
  401. package/artemis-main/mcp_server/utils/__init__.py +15 -0
  402. package/artemis-main/mcp_server/utils/device_utils.py +180 -0
  403. package/artemis-main/mcp_server/utils/env_utils.py +65 -0
  404. package/artemis-main/packages/artemis-accessibility-helper/ArtemisAccessibilityHelper.apk +0 -0
  405. package/artemis-main/packages/artemis-accessibility-helper/app/build.gradle.kts +35 -0
  406. package/artemis-main/packages/artemis-accessibility-helper/app/src/main/AndroidManifest.xml +49 -0
  407. package/artemis-main/packages/artemis-accessibility-helper/app/src/main/java/com/artemis/helper/A11yNode.java +274 -0
  408. package/artemis-main/packages/artemis-accessibility-helper/app/src/main/java/com/artemis/helper/ArtemisAccessibilityService.java +227 -0
  409. package/artemis-main/packages/artemis-accessibility-helper/app/src/main/java/com/artemis/helper/CommandServer.java +463 -0
  410. package/artemis-main/packages/artemis-accessibility-helper/app/src/main/java/com/artemis/helper/DisplayUtils.java +111 -0
  411. package/artemis-main/packages/artemis-accessibility-helper/app/src/main/java/com/artemis/helper/GestureController.java +232 -0
  412. package/artemis-main/packages/artemis-accessibility-helper/app/src/main/java/com/artemis/helper/HierarchyDumper.java +832 -0
  413. package/artemis-main/packages/artemis-accessibility-helper/app/src/main/java/com/artemis/helper/TokenReceiver.java +36 -0
  414. package/artemis-main/packages/artemis-accessibility-helper/app/src/main/java/com/artemis/helper/TokenStore.java +38 -0
  415. package/artemis-main/packages/artemis-accessibility-helper/app/src/main/java/com/artemis/helper/XmlUtils.java +102 -0
  416. package/artemis-main/packages/artemis-accessibility-helper/app/src/main/res/values/strings.xml +4 -0
  417. package/artemis-main/packages/artemis-accessibility-helper/app/src/main/res/xml/accessibility_service_config.xml +10 -0
  418. package/artemis-main/packages/artemis-accessibility-helper/build.gradle.kts +3 -0
  419. package/artemis-main/packages/artemis-accessibility-helper/build_apk.sh +215 -0
  420. package/artemis-main/packages/artemis-accessibility-helper/debug.keystore +0 -0
  421. package/artemis-main/packages/artemis-accessibility-helper/gradle.properties +2 -0
  422. package/artemis-main/packages/artemis-accessibility-helper/helper_manifest.json +7 -0
  423. package/artemis-main/packages/artemis-accessibility-helper/settings.gradle.kts +17 -0
  424. package/artemis-main/packages/artemis-client/DESIGN.md +89 -0
  425. package/artemis-main/packages/artemis-client/README.md +112 -0
  426. package/artemis-main/packages/artemis-client/examples/basic.py +30 -0
  427. package/artemis-main/packages/artemis-client/pyproject.toml +36 -0
  428. package/artemis-main/packages/artemis-client/src/artemis_client/__init__.py +45 -0
  429. package/artemis-main/packages/artemis-client/src/artemis_client/client.py +401 -0
  430. package/artemis-main/packages/artemis-client/src/artemis_client/errors.py +66 -0
  431. package/artemis-main/packages/artemis-client/src/artemis_client/models.py +212 -0
  432. package/artemis-main/packages/artemis-client/src/artemis_client/py.typed +1 -0
  433. package/artemis-main/packages/artemis-client/src/artemis_client/transport.py +134 -0
  434. package/artemis-main/packages/artemis-client/tests/test_client.py +289 -0
  435. package/artemis-main/packages/artemis-client/tests/test_transport.py +85 -0
  436. package/artemis-main/pyproject.toml +259 -0
  437. package/artemis-main/pyright-core.json +17 -0
  438. package/artemis-main/scripts/check_dependency_sources.py +68 -0
  439. package/artemis-main/scripts/install_deps.ps1 +520 -0
  440. package/artemis-main/scripts/install_deps.sh +533 -0
  441. package/artemis-main/scripts/quality_ratchet.py +97 -0
  442. package/artemis-main/scripts/real_device_video_acceptance.py +483 -0
  443. package/artemis-main/scripts/run_edge_case_part.py +327 -0
  444. package/artemis-main/scripts/run_ui.py +242 -0
  445. package/artemis-main/scripts/start.ps1 +500 -0
  446. package/artemis-main/scripts/test_all_edge_cases.py +269 -0
  447. package/artemis-main/scripts/verify_concurrency_and_devices.py +385 -0
  448. package/artemis-main/setup.py +164 -0
  449. package/artemis-main/start.bat +35 -0
  450. package/artemis-main/start.sh +423 -0
  451. package/artemis-main/third_party/mobile_use/LICENSE +190 -0
  452. package/artemis-main/third_party/mobile_use/METADATA +27 -0
  453. package/artemis-main/third_party/mobile_use/NOTICE +12 -0
  454. package/artemis-main/third_party/mobile_use/__init__.py +19 -0
  455. package/artemis-main/third_party/mobile_use/agents/__init__.py +13 -0
  456. package/artemis-main/third_party/mobile_use/agents/hopper/__init__.py +13 -0
  457. package/artemis-main/third_party/mobile_use/agents/hopper/hopper.md +22 -0
  458. package/artemis-main/third_party/mobile_use/agents/hopper/hopper.py +80 -0
  459. package/artemis-main/third_party/mobile_use/clients/__init__.py +13 -0
  460. package/artemis-main/third_party/mobile_use/clients/ui_automator_client.py +482 -0
  461. package/artemis-main/third_party/mobile_use/config/__init__.py +13 -0
  462. package/artemis-main/third_party/mobile_use/config/llm.py +183 -0
  463. package/artemis-main/third_party/mobile_use/config/output.py +184 -0
  464. package/artemis-main/third_party/mobile_use/context.py +119 -0
  465. package/artemis-main/third_party/mobile_use/controllers/__init__.py +13 -0
  466. package/artemis-main/third_party/mobile_use/controllers/platform_specific_commands_controller.py +151 -0
  467. package/artemis-main/third_party/mobile_use/controllers/types.py +92 -0
  468. package/artemis-main/third_party/mobile_use/controllers/unified_controller.py +183 -0
  469. package/artemis-main/third_party/mobile_use/graph.py +54 -0
  470. package/artemis-main/third_party/mobile_use/llm_service.py +193 -0
  471. package/artemis-main/third_party/mobile_use/main.py +130 -0
  472. package/artemis-main/third_party/mobile_use/sdk/__init__.py +13 -0
  473. package/artemis-main/third_party/mobile_use/sdk/agent.py +1060 -0
  474. package/artemis-main/third_party/mobile_use/sdk/builders/__init__.py +13 -0
  475. package/artemis-main/third_party/mobile_use/sdk/builders/agent_config_builder.py +225 -0
  476. package/artemis-main/third_party/mobile_use/sdk/builders/index.py +33 -0
  477. package/artemis-main/third_party/mobile_use/sdk/builders/task_request_builder.py +243 -0
  478. package/artemis-main/third_party/mobile_use/sdk/types/__init__.py +13 -0
  479. package/artemis-main/third_party/mobile_use/sdk/types/agent.py +98 -0
  480. package/artemis-main/third_party/mobile_use/sdk/types/exceptions.py +109 -0
  481. package/artemis-main/third_party/mobile_use/sdk/types/task.py +234 -0
  482. package/artemis-main/third_party/mobile_use/testing.py +59 -0
  483. package/artemis-main/third_party/mobile_use/tools/__init__.py +13 -0
  484. package/artemis-main/third_party/mobile_use/tools/index.py +44 -0
  485. package/artemis-main/third_party/mobile_use/tools/mobile/__init__.py +13 -0
  486. package/artemis-main/third_party/mobile_use/tools/mobile/launch_app.py +89 -0
  487. package/artemis-main/third_party/mobile_use/tools/tool_wrapper.py +52 -0
  488. package/artemis-main/third_party/mobile_use/utils/__init__.py +13 -0
  489. package/artemis-main/third_party/mobile_use/utils/app_launch_utils.py +217 -0
  490. package/artemis-main/third_party/mobile_use/utils/cli_helpers.py +60 -0
  491. package/artemis-main/third_party/mobile_use/utils/decorators.py +158 -0
  492. package/artemis-main/third_party/mobile_use/utils/file.py +31 -0
  493. package/artemis-main/third_party/mobile_use/utils/logger.py +216 -0
  494. package/artemis-main/third_party/mobile_use/utils/media.py +213 -0
  495. package/artemis-main/third_party/mobile_use/utils/shell_utils.py +39 -0
  496. package/artemis-main/third_party/mobile_use/utils/video.py +398 -0
  497. package/artemis-main/uv.lock +4908 -0
  498. package/bin/android-midscene-automation.js +1 -5
  499. package/bin/artemis-config.mjs +15 -0
  500. package/bin/artemis-embedded.py +56 -0
  501. package/bin/start-artemis.mjs +30 -0
  502. package/bin/start-dev.mjs +112 -0
  503. package/bin/test_artemis_embedded.py +44 -0
  504. package/package.json +4 -2
  505. package/server/artemis.ts +34 -0
  506. package/server/http-api.ts +12 -0
  507. package/src/App.vue +10 -5
  508. package/src/api.ts +5 -0
  509. package/src/appium-recorder/AppiumPage.vue +1 -1
  510. package/src/appium-recorder/components/ComponentInspector.vue +1 -1
  511. package/src/appium-recorder/components/FlowNodeSearch.vue +3 -3
  512. package/src/appium-recorder/components/ImageCheckDialog.vue +3 -3
  513. package/src/appium-recorder/components/RecorderWorkspace.vue +4 -4
  514. package/src/appium-recorder/flow-appearance.ts +4 -4
  515. package/src/components/AppSidebar.vue +8 -7
  516. package/src/components/MidsceneMaintenanceNotice.vue +17 -0
  517. package/src/components/analytics/AnalysisCharts.vue +4 -4
  518. package/src/components/analytics/ReportAnalysis.vue +1 -1
  519. package/src/components/generator/TestCaseFileUpload.vue +1 -1
  520. package/src/components/help/HelpCenter.vue +56 -37
  521. package/src/pages/AiGeneratorPage.vue +1 -1
  522. package/src/pages/ArtemisPage.vue +72 -0
  523. package/src/pages/ConfigPage.vue +18 -19
  524. package/src/style.css +340 -121
  525. package/src/types.ts +1 -1
@@ -0,0 +1,47 @@
1
+ # Copyright 2026 Google LLC
2
+ #
3
+ # Licensed under the Apache License, Version 2.0 (the "License");
4
+ # you may not use this file except in compliance with the License.
5
+ # You may obtain a copy of the License at
6
+ #
7
+ # http://www.apache.org/licenses/LICENSE-2.0
8
+ #
9
+ # Unless required by applicable law or agreed to in writing, software
10
+ # distributed under the License is distributed on an "AS IS" BASIS,
11
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
+ # See the License for the specific language governing permissions and
13
+ # limitations under the License.
14
+
15
+ from langchain_core.tools import BaseTool
16
+
17
+ from artemis.context import ArtemisContext
18
+ from artemis.data_engine.trace import trace_langchain_tool
19
+ from artemis.tools.tool_wrapper import ToolWrapper
20
+ from third_party.mobile_use.tools.index import (
21
+ get_tools_from_wrappers as build_tools_from_wrappers,
22
+ )
23
+
24
+
25
+ def get_tools_from_wrappers(
26
+ ctx: "ArtemisContext",
27
+ wrappers: list[ToolWrapper],
28
+ ) -> list[BaseTool]:
29
+ """Instantiate the available tools of ``wrappers``, traced into the data engine."""
30
+ return build_tools_from_wrappers(
31
+ ctx, wrappers, wrap_tool=lambda t: trace_langchain_tool(t, ctx)
32
+ )
33
+
34
+
35
+ def get_tool_by_name(name: str, tools: list[BaseTool]) -> BaseTool | None:
36
+ """Get a tool by name, stripping any prefixes like 'default_api:'."""
37
+ normalized_name = name
38
+ if ":" in normalized_name:
39
+ normalized_name = normalized_name.split(":")[-1]
40
+
41
+ for t in tools:
42
+ t_name = t.name
43
+ if ":" in t_name:
44
+ t_name = t_name.split(":")[-1]
45
+ if t_name == normalized_name:
46
+ return t
47
+ return None
@@ -0,0 +1,108 @@
1
+ # Copyright 2026 Google LLC
2
+ #
3
+ # Licensed under the Apache License, Version 2.0 (the "License");
4
+ # you may not use this file except in compliance with the License.
5
+ # You may obtain a copy of the License at
6
+ #
7
+ # http://www.apache.org/licenses/LICENSE-2.0
8
+ #
9
+ # Unless required by applicable law or agreed to in writing, software
10
+ # distributed under the License is distributed on an "AS IS" BASIS,
11
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
+ # See the License for the specific language governing permissions and
13
+ # limitations under the License.
14
+
15
+ """Universal Log Analyzer tool for inspecting and analyzing Android system logs."""
16
+
17
+ from typing import Any
18
+
19
+ from langchain_core.tools import BaseTool
20
+ from pydantic import BaseModel, Field
21
+
22
+ from artemis.core.tool_failure import ToolFailure
23
+ from artemis.agents.log_analyzer.log_analyzer import LogAnalyzerNode
24
+ from artemis.context import ArtemisContext
25
+ from artemis.data_engine.trace import trace_langchain_tool
26
+ from artemis.drivers.base import BaseDeviceDriver
27
+ from artemis.graph.state import State
28
+ from artemis.tools.base import ArtemisTool
29
+ from artemis.tools.tool_wrapper import ToolWrapper
30
+ from artemis.utils.cython_compat import CyFunctionDetector
31
+ from third_party.mobile_use.utils.logger import get_logger
32
+
33
+ logger = get_logger(__name__)
34
+
35
+
36
+ # pylint: disable=too-few-public-methods
37
+ class AnalyzeLogsArgs(BaseModel):
38
+ """Arguments schema for invoking the Log Analyzer subagent."""
39
+
40
+ model_config = {"ignored_types": (CyFunctionDetector,)}
41
+ specific_query: str = Field(
42
+ ...,
43
+ description=(
44
+ "Instructions for the log analyzer, specifying what to search for or analyze."
45
+ ),
46
+ )
47
+
48
+
49
+ class AnalyzeLogsTool(ArtemisTool):
50
+ """Universal tool for analyzing Android logs using a dedicated hybrid log analyzer agent."""
51
+
52
+ def __init__(self):
53
+ super().__init__(
54
+ name="analyze_logs",
55
+ description=(
56
+ "[DIAGNOSTIC] Analyzes Android logs using a dedicated hybrid log analyzer agent. "
57
+ "Use this when you need to investigate failures, check for specific error "
58
+ "logs, or understand system events."
59
+ ),
60
+ args_schema=AnalyzeLogsArgs,
61
+ category="custom",
62
+ )
63
+
64
+ # pylint: disable=too-many-arguments,too-many-positional-arguments
65
+ async def execute(
66
+ self,
67
+ driver: BaseDeviceDriver | None = None, # pylint: disable=unused-argument
68
+ ctx: ArtemisContext | None = None,
69
+ specific_query: str | None = None,
70
+ state: State | None = None,
71
+ **kwargs: Any,
72
+ ) -> Any:
73
+ query = (
74
+ specific_query
75
+ if specific_query is not None
76
+ else (
77
+ kwargs.get("specific_query")
78
+ or kwargs.get("SpecificQuery")
79
+ or kwargs.get("query")
80
+ or kwargs.get("Query")
81
+ or ""
82
+ )
83
+ )
84
+ st = state if state is not None else kwargs.get("state")
85
+ logger.info(f"analyze_logs tool called with query: '{query}'")
86
+ if ctx is None:
87
+ return ToolFailure("Error: ArtemisContext is required for LogAnalyzer.")
88
+ analyst = LogAnalyzerNode(ctx)
89
+ result = await analyst.run(query, st)
90
+ return result
91
+
92
+
93
+ # Universal tool instance & aliases
94
+ analyze_logs = AnalyzeLogsTool()
95
+ AnalyzeLogs = AnalyzeLogsTool
96
+ LogTool = AnalyzeLogsTool
97
+
98
+
99
+ def get_analyze_logs_tool(ctx: ArtemisContext) -> BaseTool:
100
+ """Exports analyze_logs as a LangChain BaseTool."""
101
+ return trace_langchain_tool(analyze_logs.to_langchain_tool(ctx), ctx)
102
+
103
+
104
+ analyze_logs_wrapper = ToolWrapper(
105
+ tool_fn_getter=get_analyze_logs_tool,
106
+ on_success_fn=lambda output: f"Log Analyzer replied:\n{output}",
107
+ on_failure_fn=lambda error: f"Log Analyzer failed: {error}",
108
+ )
@@ -0,0 +1,132 @@
1
+ # Copyright 2026 Google LLC
2
+ #
3
+ # Licensed under the Apache License, Version 2.0 (the "License");
4
+ # you may not use this file except in compliance with the License.
5
+ # You may obtain a copy of the License at
6
+ #
7
+ # http://www.apache.org/licenses/LICENSE-2.0
8
+ #
9
+ # Unless required by applicable law or agreed to in writing, software
10
+ # distributed under the License is distributed on an "AS IS" BASIS,
11
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
+ # See the License for the specific language governing permissions and
13
+ # limitations under the License.
14
+
15
+ from typing import Any
16
+
17
+ from langchain_core.messages import ToolMessage
18
+ from langchain_core.tools import BaseTool
19
+ from pydantic import BaseModel, Field
20
+
21
+ from artemis.context import ArtemisContext
22
+ from artemis.data_engine.trace import trace_langchain_tool
23
+ from artemis.drivers.base import BaseDeviceDriver
24
+ from artemis.graph.state import State
25
+ from artemis.tools.base import ArtemisTool, ToolCategory
26
+ from artemis.tools.tool_wrapper import ToolWrapper
27
+ from artemis.utils.cython_compat import CyFunctionDetector
28
+ from third_party.mobile_use.tools.mobile.launch_app import LAUNCH_APP_DOCSTRING, find_package
29
+ from third_party.mobile_use.utils.app_launch_utils import launch_app_with_retries
30
+
31
+
32
+ class LaunchAppArgs(BaseModel):
33
+ """Arguments schema for launching an application."""
34
+
35
+ model_config = {"ignored_types": (CyFunctionDetector,)}
36
+ app_name: str = Field(
37
+ ...,
38
+ description="The natural language name of the application to launch.",
39
+ )
40
+
41
+
42
+ class LaunchAppTool(ArtemisTool):
43
+ """Universal tool for finding and launching an application on the device."""
44
+
45
+ def __init__(self, category: ToolCategory = "action"):
46
+ super().__init__(
47
+ name="launch_app",
48
+ description=LAUNCH_APP_DOCSTRING,
49
+ args_schema=LaunchAppArgs,
50
+ category=category,
51
+ )
52
+
53
+ # pylint: disable=too-many-arguments,too-many-positional-arguments,too-many-locals
54
+ async def execute(
55
+ self,
56
+ driver: BaseDeviceDriver | None = None,
57
+ ctx: ArtemisContext | None = None,
58
+ app_name: str | None = None,
59
+ tool_call_id: str | None = None,
60
+ state: State | None = None,
61
+ **kwargs: Any,
62
+ ) -> Any:
63
+ app = (
64
+ app_name
65
+ if app_name is not None
66
+ else (kwargs.get("app_name") or kwargs.get("AppName") or "")
67
+ )
68
+ tcid = tool_call_id if tool_call_id is not None else kwargs.get("tool_call_id")
69
+ st = state if state is not None else kwargs.get("state")
70
+
71
+ success = False
72
+ error_msg = None
73
+
74
+ try:
75
+ if not app:
76
+ raise ValueError("app_name parameter is required.")
77
+
78
+ if ctx is not None:
79
+ package_name = await find_package(ctx=ctx, app_name=app)
80
+ if not package_name:
81
+ success = False
82
+ outcome = f"Failed to launch app '{app}': Package not found."
83
+ error_msg = "Package not found."
84
+ else:
85
+ success, error_msg = await launch_app_with_retries(
86
+ ctx=ctx, app_package=package_name
87
+ )
88
+ outcome = (
89
+ f"Launched app '{app}' ({package_name}); foreground confirmed."
90
+ if success
91
+ else f"Failed to launch app '{app}': {error_msg}"
92
+ )
93
+ elif driver is not None and hasattr(driver, "launch_app"):
94
+ success = await driver.launch_app(app)
95
+ outcome = f"Launched app '{app}'." if success else f"Failed to launch app '{app}'."
96
+ error_msg = None if success else "Launch failed."
97
+ else:
98
+ success = False
99
+ outcome = "Error during launch app: No driver or context provided."
100
+ error_msg = "No driver or context provided."
101
+ except Exception as e: # pylint: disable=broad-exception-caught
102
+ success = False
103
+ outcome = f"Error during launch app: {e}"
104
+ error_msg = str(e)
105
+
106
+ if st is not None:
107
+ additional_kwargs = {} if success else ({"error": error_msg} if error_msg else {})
108
+ return ToolMessage(
109
+ tool_call_id=tcid or "",
110
+ content=outcome,
111
+ additional_kwargs=additional_kwargs,
112
+ status="success" if success else "error",
113
+ )
114
+
115
+ return outcome
116
+
117
+
118
+ # Universal tool instance & aliases
119
+ launch_app = LaunchAppTool()
120
+ LaunchApp = LaunchAppTool
121
+
122
+
123
+ def get_launch_app_tool(ctx: ArtemisContext) -> BaseTool:
124
+ """Exports launch_app as a LangChain BaseTool."""
125
+ return trace_langchain_tool(launch_app.to_langchain_tool(ctx), ctx)
126
+
127
+
128
+ launch_app_wrapper = ToolWrapper(
129
+ tool_fn_getter=get_launch_app_tool,
130
+ on_success_fn=lambda *args, **kwargs: "Launch dispatched",
131
+ on_failure_fn=lambda *args, **kwargs: "Failure",
132
+ )
@@ -0,0 +1,138 @@
1
+ # Copyright 2026 Google LLC
2
+ #
3
+ # Licensed under the Apache License, Version 2.0 (the "License");
4
+ # you may not use this file except in compliance with the License.
5
+ # You may obtain a copy of the License at
6
+ #
7
+ # http://www.apache.org/licenses/LICENSE-2.0
8
+ #
9
+ # Unless required by applicable law or agreed to in writing, software
10
+ # distributed under the License is distributed on an "AS IS" BASIS,
11
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
+ # See the License for the specific language governing permissions and
13
+ # limitations under the License.
14
+
15
+ from datetime import datetime, timedelta
16
+ import time
17
+ from artemis.context import ArtemisContext
18
+ from third_party.mobile_use.controllers.platform_specific_commands_controller import get_adb_device
19
+ from third_party.mobile_use.utils.logger import get_logger
20
+
21
+ logger = get_logger(__name__)
22
+
23
+
24
+ def parse_log_time(time_str: str) -> datetime:
25
+ """Parses a log timestamp string into a datetime object."""
26
+ now = datetime.now()
27
+ current_year = now.year
28
+
29
+ def _try_parse(year: int) -> datetime | None:
30
+ for fmt in ("%Y-%m-%d %H:%M:%S.%f", "%Y-%m-%d %H:%M:%S"):
31
+ try:
32
+ return datetime.strptime(f"{year}-{time_str}", fmt)
33
+ except ValueError:
34
+ pass
35
+ return None
36
+
37
+ dt = _try_parse(current_year)
38
+ if dt is None:
39
+ raise ValueError(f"Unsupported time format: {time_str}")
40
+
41
+ # If parsed time is in the future (allowing 1 hour grace for clock drift),
42
+ # we assume it belongs to the previous year.
43
+ if dt > now + timedelta(hours=1):
44
+ prev_dt = _try_parse(current_year - 1)
45
+ if prev_dt is not None:
46
+ dt = prev_dt
47
+
48
+ return dt
49
+
50
+
51
+ def resolve_time(
52
+ time_input: str | float | None, session_start: float | None
53
+ ) -> tuple[str | None, datetime | None]:
54
+ """Resolves relative or absolute time input into formatted string and datetime."""
55
+ if time_input is None:
56
+ return None, None
57
+
58
+ if isinstance(time_input, (int, float)) or (
59
+ isinstance(time_input, str) and time_input.replace(".", "", 1).isdigit()
60
+ ):
61
+ # It's relative seconds
62
+ if session_start is None:
63
+ raise ValueError("Cannot resolve relative time without session start time.")
64
+ rel_sec = float(time_input)
65
+ abs_ts = session_start + rel_sec
66
+
67
+ t_struct = time.localtime(abs_ts)
68
+ t_str = time.strftime("%m-%d %H:%M:%S", t_struct)
69
+ ms = int((abs_ts - int(abs_ts)) * 1000)
70
+ formatted_str = f"{t_str}.{ms:03d}"
71
+
72
+ dt = datetime.fromtimestamp(abs_ts)
73
+ return formatted_str, dt
74
+
75
+ # Assume it's absolute time string "MM-DD HH:MM:SS.ms"
76
+ try:
77
+ dt = parse_log_time(str(time_input))
78
+ except ValueError as e:
79
+ raise ValueError(f"Unsupported time format: {time_input}") from e
80
+ return str(time_input), dt
81
+
82
+
83
+ # pylint: disable=too-many-locals
84
+ def fetch_and_filter_logs(
85
+ ctx: ArtemisContext,
86
+ lines: int = 1000,
87
+ since_time: str | None = None,
88
+ until_time: str | None = None,
89
+ ) -> str:
90
+ """Fetches logs from ADB device and filters them by time if needed."""
91
+ device = get_adb_device(ctx)
92
+
93
+ session_start = ctx.data_engine.session_start_time if ctx.data_engine else None
94
+
95
+ since_str, since_dt = resolve_time(since_time, session_start)
96
+ _, until_dt = resolve_time(until_time, session_start)
97
+
98
+ if since_str:
99
+ try:
100
+ logger.info(f"Attempting to fetch logs since {since_str}...")
101
+ logs = device.shell(f'logcat -v threadtime -t "{since_str}"')
102
+ except Exception as e: # pylint: disable=broad-exception-caught
103
+ logger.warning(
104
+ f"Failed to fetch logs since time directly: {e}. Falling back"
105
+ " to reading lines and filtering."
106
+ )
107
+ logs = device.shell(f"logcat -v threadtime -t {lines}")
108
+ else:
109
+ logger.info(f"Reading last {lines} lines of logs...")
110
+ logs = device.shell(f"logcat -v threadtime -t {lines}")
111
+
112
+ if since_dt or until_dt:
113
+ filtered_lines = []
114
+ for line in logs.splitlines():
115
+ if len(line) >= 18:
116
+ log_time_str = line[:18]
117
+ try:
118
+ log_dt = parse_log_time(log_time_str)
119
+
120
+ if since_dt and log_dt < since_dt:
121
+ continue
122
+ if until_dt and log_dt > until_dt:
123
+ continue
124
+
125
+ filtered_lines.append(line)
126
+ except ValueError:
127
+ pass
128
+ # Cap to the requested line limit so timestamp fetches don't overflow context
129
+ if 0 < lines < len(filtered_lines):
130
+ filtered_lines = filtered_lines[-lines:]
131
+ logs = "\n".join(filtered_lines)
132
+ else:
133
+ # Ensure fallback capping even when no datetime parsing occurred
134
+ log_lines = logs.splitlines()
135
+ if 0 < lines < len(log_lines):
136
+ logs = "\n".join(log_lines[-lines:])
137
+
138
+ return logs
@@ -0,0 +1,202 @@
1
+ # Copyright 2026 Google LLC
2
+ #
3
+ # Licensed under the Apache License, Version 2.0 (the "License");
4
+ # you may not use this file except in compliance with the License.
5
+ # You may obtain a copy of the License at
6
+ #
7
+ # http://www.apache.org/licenses/LICENSE-2.0
8
+ #
9
+ # Unless required by applicable law or agreed to in writing, software
10
+ # distributed under the License is distributed on an "AS IS" BASIS,
11
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
+ # See the License for the specific language governing permissions and
13
+ # limitations under the License.
14
+
15
+ import base64
16
+ import json
17
+ from pathlib import Path
18
+ from typing import Any
19
+
20
+ from langchain_core.messages import ToolMessage
21
+ from langchain_core.tools import BaseTool
22
+ from pydantic import BaseModel
23
+
24
+ from artemis.context import ArtemisContext
25
+ from artemis.data_engine.trace import trace_langchain_tool
26
+ from artemis.drivers.base import BaseDeviceDriver
27
+ from artemis.graph.state import State
28
+ from artemis.tools.base import ArtemisTool, ToolCategory
29
+ from artemis.tools.tool_wrapper import ToolWrapper
30
+ from artemis.utils.ocr_api import is_ocr_configured, perform_ocr
31
+ from third_party.mobile_use.utils.logger import get_logger
32
+
33
+
34
+ class OcrArgs(BaseModel):
35
+ """Arguments schema for OCR recognition."""
36
+
37
+
38
+ logger = get_logger(__name__)
39
+
40
+ OCR_DOCSTRING = (
41
+ "[EXPLORER] Performs OCR on the current screen and returns the detected"
42
+ " text along with their coordinates."
43
+ )
44
+
45
+
46
+ # pylint: disable=too-many-branches,too-many-locals,too-many-statements
47
+ async def run_ocr_core(
48
+ ctx: ArtemisContext | None = None,
49
+ state: State | None = None,
50
+ driver: BaseDeviceDriver | None = None,
51
+ ) -> str:
52
+ """Universal OCR core function: reads screenshot from state or driver and returns results."""
53
+ screenshot_b64 = None
54
+ screenshot_path = getattr(state, "latest_screenshot", None) if state else None
55
+
56
+ if screenshot_path:
57
+ if not Path(screenshot_path).exists():
58
+ logger.error(f"Screenshot file does not exist: {screenshot_path}")
59
+ raise FileNotFoundError(f"Screenshot file does not exist: {screenshot_path}")
60
+
61
+ try:
62
+ with open(screenshot_path, "rb") as f:
63
+ screenshot_b64 = base64.b64encode(f.read()).decode("utf-8")
64
+ logger.info(f"OCR loaded screenshot from file: {screenshot_path}")
65
+ except Exception as e: # pylint: disable=broad-exception-caught
66
+ logger.error(f"Failed to read screenshot from {screenshot_path}: {e}")
67
+ raise e
68
+ elif driver is not None and hasattr(driver, "get_screen_data"):
69
+ try:
70
+ screen_data = await driver.get_screen_data()
71
+ if hasattr(screen_data, "screenshot_base64") and screen_data.screenshot_base64:
72
+ screenshot_b64 = screen_data.screenshot_base64
73
+ elif hasattr(screen_data, "screenshot_bytes") and screen_data.screenshot_bytes:
74
+ screenshot_b64 = base64.b64encode(screen_data.screenshot_bytes).decode("utf-8")
75
+ elif hasattr(screen_data, "base64") and screen_data.base64:
76
+ screenshot_b64 = screen_data.base64
77
+ except Exception as e: # pylint: disable=broad-exception-caught
78
+ logger.error(f"Failed to capture screenshot from driver: {e}")
79
+ raise e
80
+ else:
81
+ logger.error("No screenshot path found in state.latest_screenshot and no driver available")
82
+ raise ValueError(
83
+ "No screenshot path found in state.latest_screenshot and no driver available"
84
+ )
85
+
86
+ if not screenshot_b64:
87
+ raise ValueError("Failed to obtain screenshot for OCR.")
88
+
89
+ width = 1080
90
+ height = 2400
91
+ try:
92
+ if ctx and hasattr(ctx, "device") and ctx.device:
93
+ w = getattr(ctx.device, "device_width", 1080)
94
+ h = getattr(ctx.device, "device_height", 2400)
95
+ if isinstance(w, int):
96
+ width = w
97
+ if isinstance(h, int):
98
+ height = h
99
+ elif driver and hasattr(driver, "screen_size") and driver.screen_size:
100
+ width, height = driver.screen_size
101
+ except Exception as e: # pylint: disable=broad-exception-caught
102
+ logger.warning(f"Failed to get device resolution for OCR normalization: {e}")
103
+
104
+ results = await perform_ocr(screenshot_b64)
105
+ if results:
106
+ normalized_results = []
107
+ for item in results:
108
+ text = item.get("text")
109
+ vertices = item.get("position") or []
110
+
111
+ # Calculate center point
112
+ x_coords = [v.get("x", 0) for v in vertices]
113
+ y_coords = [v.get("y", 0) for v in vertices]
114
+
115
+ if x_coords and y_coords:
116
+ min_x, max_x = min(x_coords), max(x_coords)
117
+ min_y, max_y = min(y_coords), max(y_coords)
118
+
119
+ center_x = (min_x + max_x) / 2
120
+ center_y = (min_y + max_y) / 2
121
+
122
+ # Normalize to 0-1000
123
+ norm_x = int(max(0, min(1000, center_x * 1000 / width)))
124
+ norm_y = int(max(0, min(1000, center_y * 1000 / height)))
125
+
126
+ normalized_results.append({"text": text, "coordinates": [norm_x, norm_y]})
127
+ else:
128
+ normalized_results.append({"text": text, "coordinates": None})
129
+
130
+ return json.dumps(normalized_results, ensure_ascii=False, indent=2)
131
+ return "No text detected on the screen."
132
+
133
+
134
+ class OcrRecognitionTool(ArtemisTool):
135
+ """Universal tool for performing OCR on the current screen."""
136
+
137
+ def __init__(self, category: ToolCategory = "explorer"):
138
+ super().__init__(
139
+ name="ocr_recognition",
140
+ description=OCR_DOCSTRING,
141
+ args_schema=OcrArgs,
142
+ category=category,
143
+ )
144
+
145
+ def is_available(self, ctx: Any = None) -> bool:
146
+ return is_ocr_configured()
147
+
148
+ # pylint: disable=too-many-arguments,too-many-positional-arguments
149
+ async def execute(
150
+ self,
151
+ driver: BaseDeviceDriver | None = None,
152
+ ctx: ArtemisContext | None = None,
153
+ tool_call_id: str | None = None,
154
+ state: State | None = None,
155
+ **kwargs: Any,
156
+ ) -> Any:
157
+ tcid = tool_call_id if tool_call_id is not None else kwargs.get("tool_call_id")
158
+ st = state if state is not None else kwargs.get("state")
159
+
160
+ has_failed = False
161
+ output = ""
162
+ error_message = None
163
+
164
+ try:
165
+ output = await run_ocr_core(ctx=ctx, state=st, driver=driver)
166
+ except Exception as e: # pylint: disable=broad-exception-caught
167
+ has_failed = True
168
+ error_message = f"Failed to perform OCR: {str(e)}"
169
+
170
+ if st is not None:
171
+ return ToolMessage(
172
+ tool_call_id=tcid or "",
173
+ content=ocr_recognition_wrapper.on_failure_fn(error_message)
174
+ if has_failed
175
+ else ocr_recognition_wrapper.on_success_fn(output),
176
+ additional_kwargs={"error": error_message} if has_failed else {},
177
+ status="error" if has_failed else "success",
178
+ )
179
+
180
+ if has_failed:
181
+ return ocr_recognition_wrapper.on_failure_fn(error_message)
182
+ return ocr_recognition_wrapper.on_success_fn(output)
183
+
184
+
185
+ # Universal tool instance & aliases
186
+ ocr_recognition = OcrRecognitionTool()
187
+ OcrRecognition = OcrRecognitionTool
188
+ OCRRecognition = OcrRecognitionTool
189
+ OcrTool = OcrRecognitionTool
190
+
191
+
192
+ def get_ocr_tool(ctx: ArtemisContext) -> BaseTool:
193
+ """Exports ocr_recognition as a LangChain BaseTool."""
194
+ return trace_langchain_tool(ocr_recognition.to_langchain_tool(ctx), ctx)
195
+
196
+
197
+ ocr_recognition_wrapper = ToolWrapper(
198
+ tool_fn_getter=get_ocr_tool,
199
+ on_success_fn=lambda output: f"OCR Recognition successful. Results:\n{output}",
200
+ on_failure_fn=lambda error: f"OCR Recognition failed: {error}",
201
+ is_available_fn=lambda ctx: is_ocr_configured(),
202
+ )