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,403 @@
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
+ import json
17
+ from pathlib import Path
18
+ from typing import Annotated, Any
19
+ from langchain_core.messages import HumanMessage, SystemMessage, ToolMessage
20
+ from langchain_core.tools import BaseTool, StructuredTool
21
+ from pydantic import BaseModel, Field
22
+ from artemis.utils.cython_compat import CyFunctionDetector
23
+
24
+
25
+ class SpawnLogReaderArgs(BaseModel):
26
+ model_config = {"ignored_types": (CyFunctionDetector,)}
27
+ specific_query: str = Field(
28
+ ...,
29
+ description=(
30
+ "Specific instructions for the sub-agent, including what to look for and analyze."
31
+ ),
32
+ )
33
+
34
+
35
+ from langgraph.prebuilt import InjectedState
36
+
37
+ from artemis.core.tool_failure import ToolFailure, is_tool_failure
38
+ from artemis.context import ArtemisContext
39
+ from artemis.data_engine.trace import (
40
+ CURRENT_TRACE_ID,
41
+ TraceSpan,
42
+ trace,
43
+ )
44
+ from artemis.graph.state import State
45
+ from artemis.services.llm import acomplete, get_llm, invoke_llm_with_timeout_message
46
+ from artemis.tools.index import get_tool_by_name
47
+ from artemis.tools.mobile.log_utils import fetch_and_filter_logs
48
+ from artemis.tools.mobile.read_logs import get_read_logs_tool
49
+ from artemis.tools.mobile.search_logs import (
50
+ get_search_logs_tool,
51
+ search_and_merge_logs,
52
+ )
53
+ from artemis.tools.tool_wrapper import (
54
+ get_tool_result_content,
55
+ invoke_tool_with_injection,
56
+ )
57
+ from third_party.mobile_use.utils.logger import get_logger
58
+
59
+ logger = get_logger(__name__)
60
+
61
+
62
+ class LogAnalyzerNode:
63
+ def __init__(self, ctx: ArtemisContext):
64
+ self.ctx = ctx
65
+ self._log_cache = {}
66
+ self._raw_log_cache = {}
67
+
68
+ def _wrap_with_caching(self, tool: BaseTool) -> BaseTool:
69
+ """Wraps a tool with caching logic based on tool name and arguments."""
70
+ original_coroutine = tool.coroutine
71
+
72
+ async def cached_coroutine(**kwargs):
73
+ if tool.name == "read_logs":
74
+ # Intercept read_logs to cache raw logs
75
+ lines = kwargs.get("lines", 200)
76
+ # Since we might have since_time/until_time, let's keep it simple or key by all.
77
+ # The instruction says: "cache the returned string based on the lines parameter."
78
+ # We can also include since_time and until_time in the cache key to be safe, but let's follow the lines-based requirement.
79
+ cache_key = (
80
+ lines,
81
+ kwargs.get("since_time"),
82
+ kwargs.get("until_time"),
83
+ )
84
+ if cache_key in self._raw_log_cache:
85
+ logger.info(f"Raw log cache hit for read_logs with key {cache_key}")
86
+ return self._raw_log_cache[cache_key]
87
+
88
+ result = await original_coroutine(**kwargs)
89
+ if isinstance(result, str) and not is_tool_failure(result):
90
+ self._raw_log_cache[cache_key] = result
91
+ return result
92
+
93
+ elif tool.name == "search_logs":
94
+ lines = kwargs.get("lines", 10000)
95
+ since_time = kwargs.get("since_time")
96
+ until_time = kwargs.get("until_time")
97
+ raw_cache_key = (lines, since_time, until_time)
98
+
99
+ # Check if we have the raw logs cached
100
+ if raw_cache_key in self._raw_log_cache:
101
+ logger.info(
102
+ "Raw log cache hit for search_logs with key"
103
+ f" {raw_cache_key}. Running local search."
104
+ )
105
+ raw_logs = self._raw_log_cache[raw_cache_key]
106
+ # Run search_and_merge_logs locally
107
+
108
+ try:
109
+ return search_and_merge_logs(
110
+ logs=raw_logs,
111
+ keyword=kwargs.get("keyword"),
112
+ context_lines=kwargs.get("context_lines", 0),
113
+ is_regex=kwargs.get("is_regex", False),
114
+ )
115
+ except Exception as e:
116
+ logger.error(f"Failed to search logs locally: {e}")
117
+ return ToolFailure(f"Error searching logs: {e}")
118
+
119
+ # If not cached, fetch them once via read_logs (or the original tool, which will fetch them)
120
+ # Note: search_logs itself fetches and filters. We can let the original_coroutine run,
121
+ # but to cache the raw logs, we need the raw logs.
122
+ # Wait, search_logs returns the SEARCHED results, not the raw logs.
123
+ # So if we just run original_coroutine, we don't get the raw logs to cache them!
124
+ # To cache them, we should fetch them using get_read_logs_tool or fetch_and_filter_logs,
125
+ # cache them, and then run search_and_merge_logs.
126
+ logger.info(
127
+ "Raw log cache miss for search_logs with key"
128
+ f" {raw_cache_key}. Fetching raw logs first."
129
+ )
130
+
131
+ try:
132
+ raw_logs = fetch_and_filter_logs(
133
+ ctx=self.ctx,
134
+ lines=lines,
135
+ since_time=since_time,
136
+ until_time=until_time,
137
+ )
138
+ self._raw_log_cache[raw_cache_key] = raw_logs
139
+
140
+ return search_and_merge_logs(
141
+ logs=raw_logs,
142
+ keyword=kwargs.get("keyword"),
143
+ context_lines=kwargs.get("context_lines", 0),
144
+ is_regex=kwargs.get("is_regex", False),
145
+ )
146
+ except Exception as e:
147
+ logger.error(f"Failed to fetch and search logs: {e}")
148
+ return ToolFailure(f"Error searching logs: {e}")
149
+
150
+ # Fallback to standard tool caching for other tools
151
+ serialized_args = json.dumps(kwargs, sort_keys=True)
152
+ cache_key = f"{tool.name}:{serialized_args}"
153
+ if cache_key in self._log_cache:
154
+ logger.info(f"Cache hit for tool {tool.name} with args {kwargs}")
155
+ return self._log_cache[cache_key]
156
+
157
+ logger.info(
158
+ f"Cache miss for tool {tool.name} with args {kwargs}. Invoking original tool."
159
+ )
160
+ result = await original_coroutine(**kwargs)
161
+ self._log_cache[cache_key] = result
162
+ return result
163
+
164
+ return StructuredTool.from_function(
165
+ coroutine=cached_coroutine,
166
+ name=tool.name,
167
+ description=tool.description,
168
+ args_schema=tool.args_schema,
169
+ )
170
+
171
+ async def _run_agent_loop(
172
+ self,
173
+ base_llm: Any,
174
+ tools_for_binding: list[Any],
175
+ tools: list[BaseTool],
176
+ current_messages: list[Any],
177
+ state: State,
178
+ max_iterations: int = 10,
179
+ agent_name: str = "Agent",
180
+ ) -> str:
181
+ outcome = "No result"
182
+
183
+ for i in range(max_iterations):
184
+ if i == max_iterations - 1:
185
+ current_messages.append(
186
+ HumanMessage(
187
+ content=(
188
+ "[WARNING] This is your final iteration; all tools"
189
+ " are stripped, and you must provide your final"
190
+ " answer/summary directly."
191
+ )
192
+ )
193
+ )
194
+ llm = base_llm.bind_tools(tools=[])
195
+ else:
196
+ if i > 0:
197
+ current_messages.append(
198
+ HumanMessage(
199
+ content=(
200
+ "[WARNING] You have not completed the analysis"
201
+ f" yet (iteration {i + 1} of {max_iterations})."
202
+ )
203
+ )
204
+ )
205
+ llm = base_llm.bind_tools(tools=tools_for_binding)
206
+
207
+ response = await invoke_llm_with_timeout_message(acomplete(llm, current_messages))
208
+
209
+ if response.content:
210
+ outcome = response.content
211
+
212
+ if not response.tool_calls:
213
+ break
214
+
215
+ if i == max_iterations - 1:
216
+ tool_names = ", ".join(tc["name"] for tc in response.tool_calls)
217
+ warning = (
218
+ f"\n\n[Warning: {agent_name} reached the maximum iteration"
219
+ f" limit of {max_iterations} and could not execute tool(s):"
220
+ f" {tool_names}]"
221
+ )
222
+ if outcome == "No result":
223
+ outcome = (
224
+ f"{agent_name} reached the maximum iteration limit of"
225
+ f" {max_iterations} while attempting to call tools:"
226
+ f" {tool_names}"
227
+ )
228
+ else:
229
+ outcome += warning
230
+ break
231
+
232
+ current_messages.append(response)
233
+
234
+ # Handle tool calls in parallel
235
+ async def run_tool(tc) -> ToolMessage:
236
+ tool_name = tc["name"].split(":")[-1] if ":" in tc["name"] else tc["name"]
237
+ logger.info(f"{agent_name} requested tool: {tool_name}")
238
+
239
+ result = ""
240
+ status = "success"
241
+ try:
242
+ if ":" in tool_name:
243
+ tool_to_run = get_tool_by_name(tool_name, tools)
244
+ else:
245
+ tool_to_run = next((t for t in tools if t.name == tool_name), None)
246
+ if tool_to_run:
247
+ args = dict(tc["args"])
248
+ with TraceSpan(name=tool_name, ctx=self.ctx) as span:
249
+ span.payload = {"args": args}
250
+ try:
251
+ result_obj = await invoke_tool_with_injection(
252
+ tool=tool_to_run,
253
+ args=args,
254
+ tool_call_id=tc["id"],
255
+ state=state,
256
+ )
257
+ result = get_tool_result_content(result_obj)
258
+ if isinstance(result, list):
259
+ result = "\n".join(map(str, result))
260
+ elif not isinstance(result, str):
261
+ result = str(result)
262
+ span.result = result
263
+ if is_tool_failure(result_obj):
264
+ status = "error"
265
+ span.status = "failed"
266
+ span.error = result
267
+ except Exception as e:
268
+ status = "error"
269
+ span.status = "failed"
270
+ span.error = str(e)
271
+ raise e
272
+ else:
273
+ result = f"Error: Tool {tool_name} not supported"
274
+ status = "error"
275
+ except Exception as e:
276
+ logger.error(f"Error running tool {tool_name}: {e}")
277
+ result = f"Error running tool {tool_name}: {e}"
278
+ status = "error"
279
+
280
+ return ToolMessage(
281
+ tool_call_id=tc["id"],
282
+ content=result,
283
+ status=status,
284
+ )
285
+
286
+ active_tool_calls = [
287
+ tc
288
+ for tc in response.tool_calls
289
+ if (tc["name"].split(":")[-1] if ":" in tc["name"] else tc["name"])
290
+ != "google_search"
291
+ ]
292
+ tool_outputs = await asyncio.gather(*(run_tool(tc) for tc in active_tool_calls))
293
+
294
+ for tm in tool_outputs:
295
+ current_messages.append(tm)
296
+
297
+ return outcome
298
+
299
+ @trace(type="agent", name="log_analyzer")
300
+ async def run(self, prompt: str, state: State) -> str:
301
+ """Runs the main Log Analyzer and returns its summary."""
302
+ prompt_path = Path(__file__).parent.joinpath("log_analyzer.md")
303
+ if not prompt_path.exists():
304
+ raise FileNotFoundError(f"System prompt file not found at {prompt_path}")
305
+
306
+ system_prompt = prompt_path.read_text(encoding="utf-8")
307
+
308
+ llm = get_llm(ctx=self.ctx, name="log_analyzer")
309
+
310
+ all_tools = [
311
+ self._wrap_with_caching(get_read_logs_tool(self.ctx)),
312
+ self._wrap_with_caching(get_search_logs_tool(self.ctx)),
313
+ self._get_spawn_log_reader_tool(),
314
+ ]
315
+
316
+ current_messages = [
317
+ SystemMessage(content=system_prompt),
318
+ HumanMessage(content=prompt),
319
+ ]
320
+
321
+ return await self._run_agent_loop(
322
+ base_llm=llm,
323
+ tools_for_binding=all_tools,
324
+ tools=all_tools,
325
+ current_messages=current_messages,
326
+ state=state,
327
+ max_iterations=10,
328
+ agent_name="Log Analyzer",
329
+ )
330
+
331
+ @trace(type="agent", name="log_reader_sub_agent")
332
+ async def _run_log_reader(self, specific_query: str, state: State) -> str:
333
+ """Runs the Log Reader Sub-Agent and returns its summary."""
334
+ sub_prompt_path = Path(__file__).parent.joinpath("log_reader_sub_agent.md")
335
+ if not sub_prompt_path.exists():
336
+ sub_prompt_path.write_text(
337
+ "# Log Reader Sub-Agent\nYou are a specialized agent for"
338
+ " reading and analyzing Android logs.\nYour job is to fulfill"
339
+ " the specific prompt given by the Main Agent.\nYou have access"
340
+ " to tools: `read_logs`.\nAnalyze the logs carefully and"
341
+ " provide a clear, structured summary of your findings.\n",
342
+ encoding="utf-8",
343
+ )
344
+
345
+ sub_system_prompt = sub_prompt_path.read_text(encoding="utf-8")
346
+
347
+ llm = get_llm(ctx=self.ctx, name="log_reader_sub_agent")
348
+
349
+ sub_tools = [self._wrap_with_caching(get_read_logs_tool(self.ctx))]
350
+
351
+ # Extract the list of keys (metadata) currently stored in _log_cache.
352
+ cache_keys = list(self._log_cache.keys())
353
+
354
+ current_messages = [
355
+ SystemMessage(content=sub_system_prompt),
356
+ ]
357
+
358
+ # Append a system message listing the available cache keys so the sub-agent knows what data can be retrieved instantly.
359
+ if cache_keys:
360
+ current_messages.append(
361
+ SystemMessage(
362
+ content=(
363
+ "The following log reads are already cached and can be"
364
+ " retrieved instantly without cost:\n"
365
+ )
366
+ + "\n".join(f"- {k}" for k in cache_keys)
367
+ )
368
+ )
369
+
370
+ current_messages.append(HumanMessage(content=specific_query))
371
+
372
+ return await self._run_agent_loop(
373
+ base_llm=llm,
374
+ tools_for_binding=sub_tools,
375
+ tools=sub_tools,
376
+ current_messages=current_messages,
377
+ state=state,
378
+ max_iterations=10,
379
+ agent_name="Log Reader Sub-Agent",
380
+ )
381
+
382
+ def _get_spawn_log_reader_tool(self) -> BaseTool:
383
+
384
+ async def spawn_log_reader(
385
+ specific_query: str,
386
+ state: Annotated[State, InjectedState] = None,
387
+ ) -> str:
388
+ """[SPAWN SUB-AGENT] Delegates complex log analysis to a sub-agent.
389
+
390
+ - Use when: Task requires deep analysis, cross-referencing, or
391
+ reading huge log volumes. - Note: The sub-agent has no search tools
392
+ and can only read raw logs. - Input: specific_query (str):
393
+ Instructions for the sub-agent. - Output: A structured summary
394
+ report from the sub-agent.
395
+ """
396
+ logger.info(f"Spawning Log Reader Sub-Agent with query: '{specific_query}'")
397
+ return await self._run_log_reader(specific_query, state)
398
+
399
+ return StructuredTool.from_function(
400
+ coroutine=spawn_log_reader,
401
+ name="spawn_log_reader",
402
+ args_schema=SpawnLogReaderArgs,
403
+ )
@@ -0,0 +1,8 @@
1
+ # Log Deep Analysis Instructions
2
+
3
+ Fulfill the specific log analysis prompt by reading and synthesizing Android logs.
4
+
5
+ ## Guidelines
6
+ 1. **Direct Answer**: Address the exact questions or patterns requested in the prompt.
7
+ 2. **Synthesis**: Do not output raw, unsorted logs. Provide a structured summary of findings.
8
+ 3. **Precision**: Include relevant timestamps, line numbers, and error codes to support your conclusions.
@@ -0,0 +1,11 @@
1
+ # Task Output Analyzer
2
+
3
+ You are a specialized agent for analyzing the stdout/stderr output of a command executed on an Android device.
4
+ The user (another agent) will ask you a question about the command's execution output.
5
+ Analyze the output carefully to answer their question.
6
+
7
+ You have access to the following tools to read and search the full output:
8
+ - `read_task_output`: Reads a range of lines from the output.
9
+ - `search_task_output`: Searches the output for a keyword or regex.
10
+
11
+ Be concise, accurate, and direct in your answer.
@@ -0,0 +1,163 @@
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 pathlib import Path
16
+ from typing import Annotated
17
+ from langchain_core.messages import HumanMessage, SystemMessage, ToolMessage
18
+ from langchain_core.tools import tool
19
+ from artemis.core.tool_failure import ToolFailure, is_tool_failure
20
+ from artemis.context import ArtemisContext
21
+ from artemis.services.llm import get_llm, invoke_llm_with_timeout_message
22
+ from artemis.tools.mobile.search_logs import search_and_merge_logs
23
+ from third_party.mobile_use.utils.logger import get_logger
24
+
25
+ logger = get_logger(__name__)
26
+
27
+
28
+ class TaskOutputAnalyzerNode:
29
+ """Agent that analyzes large command/task execution outputs using LLM and tools."""
30
+
31
+ def __init__(self, ctx: ArtemisContext):
32
+ self.ctx = ctx
33
+
34
+ async def run(
35
+ self,
36
+ command: str,
37
+ output_text: str,
38
+ query: str,
39
+ ) -> str:
40
+ prompt_path = Path(__file__).parent.joinpath("output_analyzer.md")
41
+ if not prompt_path.exists():
42
+ raise FileNotFoundError(f"System prompt file not found at {prompt_path}")
43
+
44
+ system_prompt = prompt_path.read_text(encoding="utf-8")
45
+
46
+ # Get the LLM for output_analyzer (falls back to log_analyzer)
47
+ llm = get_llm(ctx=self.ctx, name="output_analyzer")
48
+
49
+ # Local tools created with closure on output_text
50
+ @tool
51
+ def read_task_output(
52
+ start_line: Annotated[int, "The 1-indexed line number to start reading from."],
53
+ end_line: Annotated[int, "The 1-indexed line number to stop reading at (inclusive)."],
54
+ ) -> str:
55
+ """Reads a specific range of lines from the task output.
56
+
57
+ Use this tool when you know the line number range to read a specific
58
+ portion of the output.
59
+ """
60
+ lines = output_text.splitlines()
61
+ if not lines:
62
+ return "Task output is empty."
63
+ start = max(1, start_line)
64
+ end = min(len(lines), end_line)
65
+ if start > end:
66
+ return (
67
+ f"Error: start_line {start_line} is greater than end_line"
68
+ f" {end_line} or task output length {len(lines)}."
69
+ )
70
+ segment = lines[start - 1 : end]
71
+ return "\n".join(segment)
72
+
73
+ @tool
74
+ def search_task_output(
75
+ keyword: Annotated[
76
+ str,
77
+ "The keyword or regular expression to search for in the output.",
78
+ ],
79
+ is_regex: Annotated[
80
+ bool,
81
+ "Whether to treat the keyword as a regular expression. Default is False.",
82
+ ] = False,
83
+ context_lines: Annotated[
84
+ int,
85
+ "Number of context lines to include before and after the"
86
+ " matched line. Default is 5.",
87
+ ] = 5,
88
+ ) -> str:
89
+ """Searches the task output for a keyword or regular expression, returning matching lines and context.
90
+
91
+ Use this tool to quickly locate error messages, warnings, or
92
+ specific key-value pairs in large outputs.
93
+ """
94
+ return search_and_merge_logs(
95
+ logs=output_text,
96
+ keyword=keyword,
97
+ context_lines=context_lines,
98
+ is_regex=is_regex,
99
+ )
100
+
101
+ tools = [read_task_output, search_task_output]
102
+ llm = llm.bind_tools(tools=tools)
103
+
104
+ # Optimize prompt context with preview if output is large
105
+ lines = output_text.splitlines()
106
+ if len(lines) <= 400:
107
+ log_preview = f"Full Output:\n{output_text}"
108
+ else:
109
+ first_part = "\n".join(lines[:200])
110
+ last_part = "\n".join(lines[-200:])
111
+ log_preview = (
112
+ f"Note: Output is very large ({len(lines)} lines). Here is a"
113
+ " preview of the start and end of the output:\n\n--- START OF"
114
+ f" OUTPUT ---\n{first_part}\n--- TRUNCATED {len(lines) - 400}"
115
+ f" LINES ---\n--- END OF OUTPUT ---\n{last_part}\n\nYou can use"
116
+ " `read_task_output` or `search_task_output` to fetch any"
117
+ " lines in the middle if needed."
118
+ )
119
+
120
+ user_content = f"Command Executed: {command}\n\n{log_preview}\n\nQuestion: {query}"
121
+
122
+ current_messages = [
123
+ SystemMessage(content=system_prompt),
124
+ HumanMessage(content=user_content),
125
+ ]
126
+
127
+ max_iterations = 5
128
+ outcome = "Failed to analyze output."
129
+
130
+ for _ in range(max_iterations):
131
+ response = await invoke_llm_with_timeout_message(llm.ainvoke(current_messages))
132
+
133
+ if response.content:
134
+ outcome = response.content
135
+
136
+ if not response.tool_calls:
137
+ break
138
+
139
+ current_messages.append(response)
140
+
141
+ for tc in response.tool_calls:
142
+ tool_name = tc["name"]
143
+ args = tc["args"]
144
+ logger.info(f"Output Analyzer requested tool: {tool_name}")
145
+
146
+ tool_to_run = next((t for t in tools if t.name == tool_name), None)
147
+ if tool_to_run:
148
+ try:
149
+ result = tool_to_run.invoke(args)
150
+ except Exception as e:
151
+ result = ToolFailure(f"Failed to run tool {tool_name}: {e}")
152
+ else:
153
+ result = ToolFailure(f"Error: Tool {tool_name} not found.")
154
+
155
+ current_messages.append(
156
+ ToolMessage(
157
+ tool_call_id=tc["id"],
158
+ content=str(result),
159
+ status="error" if is_tool_failure(result) else "success",
160
+ )
161
+ )
162
+
163
+ return outcome
@@ -0,0 +1,9 @@
1
+ {
2
+ "templates": [
3
+ "Point to the following objects in the provided image: {labels_str}.",
4
+ "Detect the location of the following objects in the image: {labels_str}.",
5
+ "Locate the following UI components on the screen: {labels_str}."
6
+ ],
7
+ "global_timeout": 30.0,
8
+ "instructions": "Return a JSON list containing the detected objects. Each object must have a 'point' array [y, x] (normalized to 0-1000) and a 'label' string.\n\nExample output:\n[\n {\"point\": [500, 250], \"label\": \"search icon\"}\n]\n\nIf no objects are found, return an empty list []."
9
+ }