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,31 @@
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 artemis.tools.command_tool import (
16
+ RunAdbCommandArgs,
17
+ RunAdbCommandTool,
18
+ RunAdbCommand,
19
+ run_adb_command,
20
+ )
21
+
22
+ # Backward-compatibility alias
23
+ AdbShellArgs = RunAdbCommandArgs
24
+
25
+ __all__ = [
26
+ "RunAdbCommandArgs",
27
+ "RunAdbCommandTool",
28
+ "RunAdbCommand",
29
+ "run_adb_command",
30
+ "AdbShellArgs",
31
+ ]
@@ -0,0 +1,270 @@
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 collections.abc import Callable
16
+ import functools
17
+ import inspect
18
+ import re
19
+ import time
20
+ from typing import Annotated, Any, Literal, get_args, get_origin
21
+ import uuid
22
+
23
+ from langchain_core.messages import BaseMessage, HumanMessage, ToolMessage
24
+ from langchain_core.tools import BaseTool
25
+ from langchain_core.tools.base import InjectedToolCallId
26
+ from langgraph.prebuilt import InjectedState
27
+
28
+ from artemis.core.tool_failure import is_tool_failure
29
+ from artemis.data_engine import engine as engine_mod
30
+ from artemis.data_engine.trace import CURRENT_TRACE_ID, smart_serialize
31
+ from artemis.graph.state import State
32
+ from third_party.mobile_use.tools.tool_wrapper import CompositeToolWrapper, ToolWrapper
33
+
34
+ __all__ = [
35
+ "CompositeToolWrapper",
36
+ "ToolWrapper",
37
+ "get_tool_result_content",
38
+ "invoke_tool_with_injection",
39
+ "resolve_image_carrier",
40
+ "split_multimodal_result",
41
+ "tool_result_messages",
42
+ ]
43
+
44
+
45
+ # pylint: disable=too-many-branches,too-many-locals,too-many-statements
46
+ async def invoke_tool_with_injection(
47
+ tool: BaseTool,
48
+ args: dict,
49
+ tool_call_id: str,
50
+ state: State | None = None,
51
+ record_trace: bool = True,
52
+ ) -> Any:
53
+ """Invokes a LangChain tool, manually injecting arguments marked with
54
+
55
+ InjectedState or InjectedToolCallId if calling the underlying function
56
+ directly.
57
+ Automatically records type='tool' trace in DataEngine.
58
+ """
59
+ final_args = dict(args)
60
+
61
+ def _real_callable(candidate: Any) -> Callable | None:
62
+ """Return a routine or partial whose signature supports direct injection."""
63
+ target = candidate.func if isinstance(candidate, functools.partial) else candidate
64
+ if inspect.isroutine(target):
65
+ return candidate
66
+ return None
67
+
68
+ # For StructuredTool, the original function might be in tool.func or tool.coroutine
69
+ func = None
70
+ if getattr(tool, "coroutine", None):
71
+ func = _real_callable(tool.coroutine)
72
+ if func is None and getattr(tool, "func", None):
73
+ func = _real_callable(tool.func)
74
+
75
+ if func:
76
+ try:
77
+ sig = inspect.signature(func)
78
+ for param_name, param in sig.parameters.items():
79
+ annotation = param.annotation
80
+ # Handle Annotated types
81
+ if get_origin(annotation) is Annotated:
82
+ args_list = get_args(annotation)
83
+ if InjectedState in args_list:
84
+ final_args[param_name] = state
85
+ elif InjectedToolCallId in args_list:
86
+ final_args[param_name] = tool_call_id
87
+ except (ValueError, TypeError):
88
+ # If inspection fails, fall back to just using provided args
89
+ pass
90
+
91
+ trace_id = uuid.uuid4()
92
+ parent_id = CURRENT_TRACE_ID.get()
93
+ token = CURRENT_TRACE_ID.set(trace_id)
94
+ start_time = time.time()
95
+
96
+ live_engine = getattr(engine_mod, "_CURRENT_DATA_ENGINE", None)
97
+
98
+ if record_trace and live_engine:
99
+ step_id = getattr(live_engine, "current_step_id", None)
100
+ live_engine.record_trace(
101
+ type="tool",
102
+ name=tool.name,
103
+ payload={"args": {k: smart_serialize(v) for k, v in args.items()}},
104
+ status="running",
105
+ parent_trace_id=parent_id,
106
+ step_id=step_id,
107
+ trace_id=trace_id,
108
+ )
109
+
110
+ execution_status = "running"
111
+ execution_result = None
112
+ execution_error = None
113
+ try:
114
+ # Call the function
115
+ if inspect.iscoroutinefunction(func):
116
+ result = await func(**final_args)
117
+ else:
118
+ result = func(**final_args)
119
+ execution_result = result
120
+ if is_tool_failure(result):
121
+ # A structural failure is traced as one; the text is the error.
122
+ execution_status = "failed"
123
+ execution_error = result
124
+ else:
125
+ execution_status = "success"
126
+ return result
127
+ except Exception as e:
128
+ execution_status = "failed"
129
+ execution_error = e
130
+ raise e
131
+ finally:
132
+ duration = time.time() - start_time
133
+ if record_trace and live_engine:
134
+ step_id = getattr(live_engine, "current_step_id", None)
135
+ payload = {"args": {k: smart_serialize(v) for k, v in args.items()}}
136
+
137
+ if execution_status == "success":
138
+ payload["result"] = smart_serialize(get_tool_result_content(execution_result))
139
+ elif execution_status == "failed":
140
+ payload["error"] = str(execution_error)
141
+ else:
142
+ execution_status = "terminated"
143
+ payload["error"] = "Execution terminated unexpectedly."
144
+
145
+ live_engine.record_trace(
146
+ type="tool",
147
+ name=tool.name,
148
+ payload=payload,
149
+ status=execution_status,
150
+ duration=duration,
151
+ parent_trace_id=parent_id,
152
+ step_id=step_id,
153
+ trace_id=trace_id,
154
+ )
155
+ CURRENT_TRACE_ID.reset(token)
156
+ else:
157
+ # Fallback to ainvoke if no direct func available; tools that declare
158
+ # a "state" argument still receive the graph state.
159
+ tool_args = getattr(tool, "args", None)
160
+ if state is not None and isinstance(tool_args, dict) and "state" in tool_args:
161
+ final_args["state"] = state
162
+ return await tool.ainvoke(final_args)
163
+
164
+
165
+ def get_tool_result_content(result: Any) -> Any:
166
+ """Extracts the content from a tool result, handling ToolMessage returns."""
167
+ if isinstance(result, ToolMessage):
168
+ return result.content
169
+ return result
170
+
171
+
172
+ # --- Multimodal tool results ------------------------------------------------------------
173
+
174
+ ImageCarrier = Literal["tool", "human"]
175
+
176
+ #: Providers whose tool-result messages may carry image parts directly.
177
+ _TOOL_IMAGE_PROVIDERS = frozenset({"google", "anthropic"})
178
+
179
+ _STEP_RE = re.compile(r"\bstep\s+(\d+)", re.IGNORECASE)
180
+
181
+
182
+ def split_multimodal_result(result: Any) -> tuple[str, list[dict[str, Any]]]:
183
+ """Splits a tool result into its text and its image content blocks.
184
+
185
+ Plain strings have no images; content-block lists contribute their text
186
+ blocks (joined by newlines) and their ``image_url`` / ``image`` blocks.
187
+ ``ToolMessage`` results are unwrapped first.
188
+ """
189
+ content = get_tool_result_content(result)
190
+ if content is None:
191
+ return "", []
192
+ if isinstance(content, list):
193
+ texts: list[str] = []
194
+ images: list[dict[str, Any]] = []
195
+ for block in content:
196
+ if isinstance(block, dict) and block.get("type") in ("image_url", "image"):
197
+ images.append(block)
198
+ elif isinstance(block, dict) and block.get("type") == "text":
199
+ texts.append(str(block.get("text") or ""))
200
+ else:
201
+ texts.append(str(block))
202
+ return "\n".join(t for t in texts if t), images
203
+ return content if isinstance(content, str) else str(content), []
204
+
205
+
206
+ def resolve_image_carrier(llm_or_provider: Any) -> ImageCarrier:
207
+ """Which message carries images returned by a tool for this LLM.
208
+
209
+ Gemini (direct API) and Anthropic accept image parts inside the tool
210
+ result message itself (``"tool"``). Vertex AI and the OpenAI-compatible
211
+ providers (openai / openrouter / xai / ...) reject them there, so the
212
+ image travels in a ``HumanMessage`` that immediately follows the textual
213
+ tool result (``"human"``). Accepts a provider name, a provider enum, or an
214
+ LLM whose ``endpoint.provider`` names the provider that actually issues
215
+ the request (a fallback model is judged by its own provider).
216
+ """
217
+ provider: Any = llm_or_provider
218
+ if provider is not None and not isinstance(provider, str):
219
+ # An LLM (RobustChatModelWrapper) names its provider on ``endpoint``;
220
+ # anything without an endpoint is taken as the provider itself.
221
+ endpoint = getattr(provider, "endpoint", None)
222
+ if endpoint is not None:
223
+ provider = getattr(endpoint, "provider", None)
224
+ value = str(getattr(provider, "value", provider) or "").lower()
225
+ return "tool" if value in _TOOL_IMAGE_PROVIDERS else "human"
226
+
227
+
228
+ def tool_result_messages(
229
+ tool_call_id: str,
230
+ result: Any,
231
+ *,
232
+ name: str | None = None,
233
+ status: str = "success",
234
+ image_carrier: ImageCarrier | None = None,
235
+ llm: Any = None,
236
+ ) -> list[BaseMessage]:
237
+ """The conversation messages that deliver one tool result to the model.
238
+
239
+ Text-only results become a single ``ToolMessage``. Results carrying images
240
+ (e.g. ``get_step_screenshot``) are delivered according to the image
241
+ carrier — given explicitly or resolved from ``llm`` — so every agent loop
242
+ handles multimodal tool results the same way and no loop ever pastes
243
+ base64 into a text field. The tool call id, name and status are preserved.
244
+ """
245
+ text, images = split_multimodal_result(result)
246
+ extra = {"name": name} if name else {}
247
+ if not images:
248
+ content = get_tool_result_content(result)
249
+ if not isinstance(content, (str, list)):
250
+ content = text
251
+ return [ToolMessage(tool_call_id=tool_call_id, content=content, status=status, **extra)]
252
+
253
+ carrier = image_carrier or resolve_image_carrier(llm)
254
+ if carrier == "tool":
255
+ blocks: list[dict[str, Any]] = [{"type": "text", "text": text}] if text else []
256
+ return [
257
+ ToolMessage(
258
+ tool_call_id=tool_call_id, content=[*blocks, *images], status=status, **extra
259
+ )
260
+ ]
261
+
262
+ match = _STEP_RE.search(text)
263
+ caption = f"[Screenshot returned by {name or 'the tool'}"
264
+ if match:
265
+ caption += f" for step {match.group(1)}"
266
+ caption += "]"
267
+ return [
268
+ ToolMessage(tool_call_id=tool_call_id, content=text, status=status, **extra),
269
+ HumanMessage(content=[{"type": "text", "text": caption}, *images]),
270
+ ]
@@ -0,0 +1,261 @@
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
+ """Video Analyzer utility for analyzing video content using Gemini models.
16
+
17
+ This utility sends video files to video-capable Gemini models for analysis
18
+ and returns text descriptions based on the provided prompt.
19
+ It also exposes a tool for agents to perform structured video analysis.
20
+ """
21
+
22
+ from typing import Any, Literal
23
+
24
+ from langchain_core.messages import ToolMessage
25
+ from langchain_core.tools import BaseTool
26
+ from pydantic import BaseModel, Field
27
+
28
+ from artemis.core.tool_failure import ToolFailure
29
+ from artemis.agents.video_analyzer.video_analyzer import VideoAnalyzer
30
+ from artemis.context import ArtemisContext
31
+ from artemis.core.tool_declaration import ToolDeclaration
32
+ from artemis.data_engine.trace import trace_langchain_tool
33
+ from artemis.drivers.base import BaseDeviceDriver
34
+ from artemis.graph.state import State
35
+ from artemis.tools.base import ArtemisTool, ToolCategory
36
+ from artemis.tools.tool_wrapper import ToolWrapper
37
+ from artemis.utils.cython_compat import CyFunctionDetector
38
+ from third_party.mobile_use.utils.logger import get_logger
39
+
40
+ logger = get_logger(__name__)
41
+
42
+
43
+ class VideoAnalyzerArgs(BaseModel):
44
+ """Arguments schema for video analyzer tool."""
45
+
46
+ model_config = {"ignored_types": (CyFunctionDetector,)}
47
+ time_description: str = Field(
48
+ ...,
49
+ description=(
50
+ "The specific time range to analyze, relative to the start of the"
51
+ " recording (e.g., 'from 5s to 12s', 'from 20s to the end')."
52
+ ),
53
+ )
54
+ purpose: str = Field(
55
+ ...,
56
+ description=(
57
+ "The specific query, intent, or question you want the video"
58
+ " analyzer to answer about the visual or audio content in this"
59
+ " segment."
60
+ ),
61
+ )
62
+
63
+
64
+ OPERATOR_VIDEO_ANALYZER_DOCSTRING = (
65
+ "[VIDEO] Use this tool to analyze any specific time range of the video"
66
+ " recording. This tool delegates to a video analysis subagent"
67
+ " with full access to the continuous screen recording of your entire"
68
+ " execution session.\n\n"
69
+ "- This tool overcomes your limitation of only receiving discrete"
70
+ " screenshots. Common use cases include (but are not limited to):\n"
71
+ " * Watching media files: Play the video on the device screen, use"
72
+ " `wait_for_delay` until the video finishes playing, and then call this"
73
+ " tool to analyze the recording.\n"
74
+ " * Bridging the gap: Inspecting what happened between your last action"
75
+ " and the current screenshot.\n"
76
+ " * Memory retrieval: Recalling fast-moving or transient information from"
77
+ " a previous step.\n"
78
+ " * Catching rapid changes: Investigating on-screen information that"
79
+ " changes too quickly for static screenshots to capture.\n"
80
+ " - Please specify a narrow time range when possible to ensure faster"
81
+ " processing.\n"
82
+ " - If only audio analysis is needed, state it clearly in your purpose.\n"
83
+ )
84
+
85
+ #: Flash-profile declaration of the same tool (FlashRunner binds JSON-schema
86
+ #: declarations rather than LangChain tools). Description and parameters are
87
+ #: derived from the operator contract above so the two never drift.
88
+ VIDEO_ANALYZER_TOOL = ToolDeclaration(
89
+ name="video_analyzer",
90
+ description=OPERATOR_VIDEO_ANALYZER_DOCSTRING,
91
+ parameters={
92
+ "type": "object",
93
+ "properties": {
94
+ field_name: {
95
+ "type": "string",
96
+ "description": field_info.description or "",
97
+ }
98
+ for field_name, field_info in VideoAnalyzerArgs.model_fields.items()
99
+ },
100
+ "required": list(VideoAnalyzerArgs.model_fields),
101
+ },
102
+ )
103
+
104
+ DIAGNOSER_VIDEO_ANALYZER_DOCSTRING = (
105
+ "[DIAGNOSTIC] Analyzes a video segment for failure diagnosis and"
106
+ " forensics.\n\n"
107
+ "Use this to verify if an action succeeded, check for error popups, or"
108
+ " investigate visual anomalies when a step fails.\n"
109
+ "Provide a narrow time range (e.g., 'from 10s to 15s') to ensure speed.\n"
110
+ )
111
+
112
+
113
+ class VideoAnalyzerTool(ArtemisTool):
114
+ """Universal tool for analyzing video content using Gemini models."""
115
+
116
+ def __init__(
117
+ self,
118
+ role: Literal["operator", "diagnoser"] = "operator",
119
+ description: str | None = None,
120
+ category: ToolCategory = "perception",
121
+ ):
122
+ chosen_description = description or (
123
+ OPERATOR_VIDEO_ANALYZER_DOCSTRING
124
+ if role == "operator"
125
+ else DIAGNOSER_VIDEO_ANALYZER_DOCSTRING
126
+ )
127
+ super().__init__(
128
+ name="video_analyzer",
129
+ description=chosen_description,
130
+ args_schema=VideoAnalyzerArgs,
131
+ category=category,
132
+ )
133
+ self.role = role
134
+
135
+ # pylint: disable=too-many-arguments,too-many-positional-arguments,too-many-locals
136
+ async def execute(
137
+ self,
138
+ driver: BaseDeviceDriver | None = None, # pylint: disable=unused-argument
139
+ ctx: ArtemisContext | None = None,
140
+ time_description: str | None = None,
141
+ purpose: str | None = None,
142
+ tool_call_id: str | None = None,
143
+ state: State | None = None,
144
+ **kwargs: Any,
145
+ ) -> Any:
146
+ time_desc = (
147
+ time_description
148
+ if time_description is not None
149
+ else (kwargs.get("time_description") or kwargs.get("TimeDescription") or "")
150
+ )
151
+ purp = (
152
+ purpose
153
+ if purpose is not None
154
+ else (kwargs.get("purpose") or kwargs.get("Purpose") or "")
155
+ )
156
+ tcid = tool_call_id if tool_call_id is not None else kwargs.get("tool_call_id")
157
+ st = state if state is not None else kwargs.get("state")
158
+
159
+ try:
160
+ logger.info(f"video_analyzer called for range: {time_desc}")
161
+ if ctx is None:
162
+ raise ValueError("ArtemisContext is required for VideoAnalyzer.")
163
+ agent = VideoAnalyzer(ctx)
164
+ agent_outcome, status = await agent.run(time_desc, purp)
165
+ except Exception as e: # pylint: disable=broad-exception-caught
166
+ logger.error(f"Error running video analyzer: {e}")
167
+ agent_outcome = f"Error running video analyzer: {e}"
168
+ status = "error"
169
+
170
+ if st is not None:
171
+ return ToolMessage(
172
+ tool_call_id=tcid or "",
173
+ content=agent_outcome,
174
+ status=status,
175
+ )
176
+
177
+ if status == "failed":
178
+ return f"Video analysis failed: {agent_outcome}"
179
+ return agent_outcome
180
+
181
+
182
+ # Universal tool instance & aliases
183
+ video_analyzer = VideoAnalyzerTool()
184
+
185
+
186
+ class VideoAnalyzerPureTool(ArtemisTool):
187
+ """Universal pure tool for video analysis returning text results directly."""
188
+
189
+ def __init__(self):
190
+ super().__init__(
191
+ name="video_analyzer_pure",
192
+ description=OPERATOR_VIDEO_ANALYZER_DOCSTRING,
193
+ args_schema=VideoAnalyzerArgs,
194
+ category="perception",
195
+ )
196
+
197
+ # pylint: disable=too-many-arguments,too-many-positional-arguments
198
+ async def execute(
199
+ self,
200
+ driver: BaseDeviceDriver | None = None, # pylint: disable=unused-argument
201
+ ctx: ArtemisContext | None = None,
202
+ time_description: str | None = None,
203
+ purpose: str | None = None,
204
+ **kwargs: Any,
205
+ ) -> str:
206
+ time_desc = (
207
+ time_description
208
+ if time_description is not None
209
+ else (kwargs.get("time_description") or kwargs.get("TimeDescription") or "")
210
+ )
211
+ purp = (
212
+ purpose
213
+ if purpose is not None
214
+ else (kwargs.get("purpose") or kwargs.get("Purpose") or "")
215
+ )
216
+
217
+ try:
218
+ logger.info(f"video_analyzer (pure) called for range: {time_desc}")
219
+ if ctx is None:
220
+ raise ValueError("ArtemisContext is required for VideoAnalyzer.")
221
+ agent = VideoAnalyzer(ctx)
222
+ agent_outcome, status = await agent.run(time_desc, purp)
223
+ if status == "failed":
224
+ return ToolFailure(f"Video analysis failed: {agent_outcome}")
225
+ return agent_outcome
226
+ except Exception as e: # pylint: disable=broad-exception-caught
227
+ logger.error(f"Error in pure video_analyzer tool: {e}")
228
+ return ToolFailure(f"Error running video analyzer: {e}")
229
+
230
+
231
+ # Universal pure tool instance & aliases
232
+ video_analyzer_pure = VideoAnalyzerPureTool()
233
+ VideoAnalyzerPure = VideoAnalyzerPureTool
234
+
235
+
236
+ def get_video_analyzer_tool(
237
+ ctx: ArtemisContext, role: Literal["operator", "diagnoser"] = "operator"
238
+ ) -> BaseTool:
239
+ """Creates a tool that allows agents to perform structured video analysis.
240
+
241
+ The tool acts as a Main Agent following the Dynamic Video Analyzer plan.
242
+ """
243
+ tool_inst = VideoAnalyzerTool(role=role)
244
+ return trace_langchain_tool(tool_inst.to_langchain_tool(ctx), ctx)
245
+
246
+
247
+ def get_video_analyzer_tool_pure(ctx: ArtemisContext) -> BaseTool:
248
+ """Creates a pure LangChain tool for video analysis that returns the text result directly.
249
+
250
+ Suitable for agents running outside of LangGraph (like the outputter).
251
+ """
252
+ return trace_langchain_tool(
253
+ video_analyzer_pure.to_langchain_tool(ctx, name="video_analyzer"), ctx
254
+ )
255
+
256
+
257
+ video_analyzer_wrapper = ToolWrapper(
258
+ tool_fn_getter=get_video_analyzer_tool,
259
+ on_success_fn=lambda output: f"Video Analyzer replied:\n{output}",
260
+ on_failure_fn=lambda error: f"Video Analyzer failed: {error}",
261
+ )
@@ -0,0 +1,97 @@
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 asyncio
16
+ from typing import Any
17
+
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.tools.base import ArtemisTool, ToolCategory
25
+ from artemis.tools.tool_wrapper import ToolWrapper
26
+ from artemis.utils.cython_compat import CyFunctionDetector
27
+ from third_party.mobile_use.utils.logger import get_logger
28
+
29
+ logger = get_logger(__name__)
30
+
31
+
32
+ class WaitArgs(BaseModel):
33
+ """Arguments schema for wait tool."""
34
+
35
+ model_config = {"ignored_types": (CyFunctionDetector,)}
36
+ seconds: int = Field(
37
+ ...,
38
+ description=("The duration to wait/sleep in seconds. Minimum is 1, maximum is 60."),
39
+ )
40
+
41
+
42
+ WAIT_DOCSTRING = (
43
+ "[SHELL] Pauses execution for a specified duration in seconds.\n\n"
44
+ "Use this to wait for active background operations to progress."
45
+ )
46
+
47
+
48
+ class WaitTool(ArtemisTool):
49
+ """Universal tool for pausing execution for a specified duration in seconds."""
50
+
51
+ def __init__(self, category: ToolCategory = "system"):
52
+ super().__init__(
53
+ name="wait",
54
+ description=WAIT_DOCSTRING,
55
+ args_schema=WaitArgs,
56
+ category=category,
57
+ )
58
+
59
+ # pylint: disable=too-many-arguments,too-many-positional-arguments
60
+ async def execute(
61
+ self,
62
+ driver: BaseDeviceDriver | None = None, # pylint: disable=unused-argument
63
+ ctx: ArtemisContext | None = None, # pylint: disable=unused-argument
64
+ seconds: int | None = None,
65
+ **kwargs: Any,
66
+ ) -> str:
67
+ sec = (
68
+ seconds
69
+ if seconds is not None
70
+ else (kwargs.get("seconds") or kwargs.get("Seconds") or 1)
71
+ )
72
+ if sec < 1:
73
+ sec = 1
74
+ elif sec > 60:
75
+ sec = 60
76
+
77
+ logger.info(f"Diagnoser waiting for {sec} seconds...")
78
+ await asyncio.sleep(sec)
79
+ return f"Waited {sec} seconds."
80
+
81
+
82
+ # Universal tool instance & aliases
83
+ wait = WaitTool()
84
+ Wait = WaitTool
85
+
86
+
87
+ def get_wait_tool(ctx: ArtemisContext) -> BaseTool:
88
+ """Exports wait as a LangChain BaseTool."""
89
+ return trace_langchain_tool(wait.to_langchain_tool(ctx), ctx)
90
+
91
+
92
+ wait_wrapper = ToolWrapper(
93
+ tool_fn_getter=get_wait_tool,
94
+ on_success_fn=lambda seconds: f"Waited {seconds}s",
95
+ on_failure_fn=lambda err: f"Wait failed: {err}",
96
+ )
97
+ export_wait_tool = get_wait_tool