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,398 @@
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
+ """Shared history search, step replay, and screenshot tools.
16
+
17
+ Each tool's args_schema defines both its LangChain interface for Pro and its
18
+ ToolDeclaration for Flash. The MCP trace inspector calls the underlying
19
+ functions with an OfflineHistoryReader.
20
+ """
21
+
22
+ from __future__ import annotations
23
+
24
+ from typing import Any, Literal
25
+
26
+ from langchain_core.tools import BaseTool
27
+ from pydantic import BaseModel, Field
28
+
29
+ from artemis.core.tool_failure import ToolFailure
30
+ from artemis.core.tool_declaration import ToolDeclaration
31
+ from artemis.data_engine.trace import trace_langchain_tool
32
+ from artemis.tools.base import ArtemisTool
33
+ from artemis.tools.history.replay import (
34
+ DEFAULT_MAX_STEPS as DEFAULT_REPLAY_MAX_STEPS,
35
+ )
36
+ from artemis.tools.history.replay import (
37
+ DEFAULT_MAX_TOKENS as DEFAULT_REPLAY_MAX_TOKENS,
38
+ )
39
+ from artemis.tools.history.replay import replay_steps_text
40
+ from artemis.tools.history.screenshot import ScreenshotResult, load_step_screenshot
41
+ from artemis.tools.history.search import search_history_text
42
+ from artemis.tools.tool_wrapper import ToolWrapper
43
+ from third_party.mobile_use.utils.logger import get_logger
44
+
45
+ logger = get_logger(__name__)
46
+
47
+ __all__ = [
48
+ "HISTORY_TOOLS",
49
+ "HISTORY_TOOL_DECLARATIONS",
50
+ "HISTORY_TOOL_NAMES",
51
+ "HISTORY_TOOL_WRAPPERS",
52
+ "GET_STEP_SCREENSHOT_TOOL",
53
+ "REPLAY_STEPS_TOOL",
54
+ "SEARCH_HISTORY_TOOL",
55
+ "GetStepScreenshotArgs",
56
+ "GetStepScreenshotTool",
57
+ "ReplayStepsArgs",
58
+ "ReplayStepsTool",
59
+ "ScreenshotResult",
60
+ "SearchHistoryArgs",
61
+ "SearchHistoryTool",
62
+ "get_history_tools",
63
+ "get_replay_steps_tool",
64
+ "get_search_history_tool",
65
+ "get_step_screenshot",
66
+ "get_step_screenshot_tool",
67
+ "history_available",
68
+ "history_tool_by_name",
69
+ "history_tool_declarations",
70
+ "load_step_screenshot",
71
+ "replay_steps",
72
+ "replay_steps_text",
73
+ "search_history",
74
+ "search_history_available",
75
+ "search_history_text",
76
+ ]
77
+
78
+
79
+ # --- Configuration -----------------------------------------------------------------------
80
+
81
+
82
+ def _memory_config(warn: bool = True) -> Any:
83
+ try:
84
+ from artemis.config import load_agent_config
85
+
86
+ return load_agent_config().memory
87
+ except Exception as e:
88
+ if warn:
89
+ logger.debug(f"memory config unavailable, using defaults: {e}")
90
+ return None
91
+
92
+
93
+ def _recall_config(warn: bool = True) -> Any:
94
+ return getattr(_memory_config(warn), "recall", None)
95
+
96
+
97
+ def _replay_config(warn: bool = True) -> Any:
98
+ return getattr(_memory_config(warn), "replay", None)
99
+
100
+
101
+ def history_available(ctx: Any) -> bool:
102
+ """History tools need a stored history to read (a DataEngine session)."""
103
+ return ctx is not None and getattr(ctx, "data_engine", None) is not None
104
+
105
+
106
+ def search_history_available(ctx: Any) -> bool:
107
+ """``search_history`` is additionally gated by ``agent.memory.recall.enabled``."""
108
+ if not history_available(ctx):
109
+ return False
110
+ return bool(getattr(_recall_config(warn=False), "enabled", True))
111
+
112
+
113
+ # --- search_history ----------------------------------------------------------------------
114
+
115
+
116
+ class SearchHistoryArgs(BaseModel):
117
+ """Arguments schema for the search_history tool."""
118
+
119
+ query: str = Field(
120
+ default="",
121
+ description=(
122
+ "Keywords to search for across the whole execution history"
123
+ " (screen descriptions, actions, results, reasoning, tool calls,"
124
+ " notes, on-screen text, package/activity names). Case-insensitive;"
125
+ " every whitespace-separated term is matched independently. May be"
126
+ " empty when step_range is given."
127
+ ),
128
+ )
129
+ step_range: list[int] | None = Field(
130
+ default=None,
131
+ description=(
132
+ "Optional [start, end] step-number range (inclusive) to restrict the"
133
+ " search — e.g. the range shown in a compressed-history block. Also"
134
+ " returns the full per-step action ledger of the range."
135
+ ),
136
+ )
137
+ max_results: int = Field(
138
+ default=5,
139
+ ge=1,
140
+ description="Maximum results to return (server-side cap still applies).",
141
+ )
142
+
143
+
144
+ SEARCH_HISTORY_DESCRIPTION = (
145
+ "Deterministic keyword / step-range lookup over the full stored execution"
146
+ " history: step summaries, exact actions and results, reasoning, tool"
147
+ " calls, notes, on-screen text (OCR/UI tree, incl. package/activity names)"
148
+ " and compressed-history ledgers. Every hit carries its step number; use"
149
+ " `replay_steps` for the full record of a step and `get_step_screenshot`"
150
+ " for its image."
151
+ )
152
+
153
+
154
+ class SearchHistoryTool(ArtemisTool):
155
+ """Deterministic search over the stored execution history."""
156
+
157
+ def __init__(self):
158
+ super().__init__(
159
+ name="search_history",
160
+ description=SEARCH_HISTORY_DESCRIPTION,
161
+ args_schema=SearchHistoryArgs,
162
+ category="memory",
163
+ )
164
+
165
+ def is_available(self, ctx: Any = None) -> bool:
166
+ return search_history_available(ctx)
167
+
168
+ # pylint: disable=too-many-arguments
169
+ async def execute(
170
+ self,
171
+ driver: Any = None, # pylint: disable=unused-argument
172
+ ctx: Any = None,
173
+ query: str = "",
174
+ step_range: list[int] | None = None,
175
+ max_results: int = 5,
176
+ **kwargs: Any,
177
+ ) -> str:
178
+ reader = getattr(ctx, "data_engine", None) if ctx else None
179
+ if reader is None:
180
+ return ToolFailure("search_history unavailable: no active execution history.")
181
+ if not query and not step_range:
182
+ return ToolFailure(
183
+ "search_history needs a query and/or a step_range — e.g."
184
+ ' search_history(query="login timeout") or'
185
+ ' search_history(query="", step_range=[1, 40]).'
186
+ )
187
+ try:
188
+ return search_history_text(
189
+ reader,
190
+ query=query or "",
191
+ step_range=step_range,
192
+ max_results=int(max_results or 5),
193
+ recall_config=_recall_config(),
194
+ )
195
+ except Exception as e:
196
+ logger.error(f"search_history failed: {e}")
197
+ return ToolFailure(f"search_history failed: {e}")
198
+
199
+
200
+ # --- replay_steps ------------------------------------------------------------------------
201
+
202
+
203
+ class ReplayStepsArgs(BaseModel):
204
+ """Arguments schema for the replay_steps tool."""
205
+
206
+ start_step: int = Field(..., description="First step to replay (1-based step number).")
207
+ end_step: int | None = Field(
208
+ default=None,
209
+ description=(
210
+ "Last step of the inclusive range to replay; omit to replay the single"
211
+ " start_step. Reversed bounds are swapped."
212
+ ),
213
+ )
214
+
215
+
216
+ REPLAY_STEPS_DESCRIPTION = (
217
+ "Replays recorded steps exactly as the executing agent's own context showed"
218
+ " them: what the screen showed, its reasoning, every tool call (name,"
219
+ " arguments, result), the planned action, any pre-execution interception"
220
+ " and the execution result. Coordinates normalized [x, y]. Up to"
221
+ f" {DEFAULT_REPLAY_MAX_STEPS} steps per call. Screenshots are not included"
222
+ " — fetch them with `get_step_screenshot`."
223
+ )
224
+
225
+
226
+ class ReplayStepsTool(ArtemisTool):
227
+ """Full replay of a range of recorded steps."""
228
+
229
+ def __init__(self):
230
+ super().__init__(
231
+ name="replay_steps",
232
+ description=REPLAY_STEPS_DESCRIPTION,
233
+ args_schema=ReplayStepsArgs,
234
+ category="memory",
235
+ )
236
+
237
+ def is_available(self, ctx: Any = None) -> bool:
238
+ return history_available(ctx)
239
+
240
+ async def execute(
241
+ self,
242
+ driver: Any = None, # pylint: disable=unused-argument
243
+ ctx: Any = None,
244
+ start_step: Any = None,
245
+ end_step: Any = None,
246
+ **kwargs: Any,
247
+ ) -> str:
248
+ reader = getattr(ctx, "data_engine", None) if ctx else None
249
+ if reader is None:
250
+ return ToolFailure("Error: no execution history available.")
251
+ cfg = _replay_config()
252
+ try:
253
+ return replay_steps_text(
254
+ reader,
255
+ start_step,
256
+ end_step,
257
+ max_steps=int(getattr(cfg, "max_steps", None) or DEFAULT_REPLAY_MAX_STEPS),
258
+ max_tokens=int(getattr(cfg, "max_tokens", None) or DEFAULT_REPLAY_MAX_TOKENS),
259
+ )
260
+ except Exception as e:
261
+ logger.error(f"replay_steps failed: {e}")
262
+ return ToolFailure(f"replay_steps failed: {e}")
263
+
264
+
265
+ # --- get_step_screenshot -----------------------------------------------------------------
266
+
267
+
268
+ class GetStepScreenshotArgs(BaseModel):
269
+ """Arguments schema for the get_step_screenshot tool."""
270
+
271
+ step_number: int = Field(..., description="The step whose screenshot to attach (1-based).")
272
+ which: Literal["pre", "post", "overlay"] = Field(
273
+ default="pre",
274
+ description=(
275
+ "'pre' = screen observed at the start of the step; 'post' = screen"
276
+ " after the action (when recorded); 'overlay' = the pre screenshot"
277
+ " with the step's action drawn on it."
278
+ ),
279
+ )
280
+
281
+
282
+ GET_STEP_SCREENSHOT_DESCRIPTION = (
283
+ "Attaches one recorded screenshot of a step: 'pre' (screen observed at the"
284
+ " start of the step), 'post' (screen after the action, when recorded) or"
285
+ " 'overlay' (the pre screenshot with the step's action drawn on it — tap"
286
+ " point / swipe path — to check the action landed on the intended element)."
287
+ )
288
+
289
+
290
+ class GetStepScreenshotTool(ArtemisTool):
291
+ """One recorded step screenshot, attached to the conversation."""
292
+
293
+ def __init__(self):
294
+ super().__init__(
295
+ name="get_step_screenshot",
296
+ description=GET_STEP_SCREENSHOT_DESCRIPTION,
297
+ args_schema=GetStepScreenshotArgs,
298
+ category="memory",
299
+ )
300
+
301
+ def is_available(self, ctx: Any = None) -> bool:
302
+ return history_available(ctx)
303
+
304
+ async def execute(
305
+ self,
306
+ driver: Any = None, # pylint: disable=unused-argument
307
+ ctx: Any = None,
308
+ step_number: Any = None,
309
+ which: str = "pre",
310
+ **kwargs: Any,
311
+ ) -> list[dict[str, Any]] | str:
312
+ reader = getattr(ctx, "data_engine", None) if ctx else None
313
+ if reader is None:
314
+ return ToolFailure("Error: no execution history available.")
315
+ try:
316
+ return load_step_screenshot(reader, step_number, which).to_content_blocks()
317
+ except Exception as e:
318
+ logger.error(f"get_step_screenshot failed: {e}")
319
+ return ToolFailure(f"get_step_screenshot failed: {e}")
320
+
321
+
322
+ # --- Instances, declarations, exports ------------------------------------------------------
323
+
324
+ search_history = SearchHistoryTool()
325
+ replay_steps = ReplayStepsTool()
326
+ get_step_screenshot = GetStepScreenshotTool()
327
+
328
+ HISTORY_TOOLS: tuple[ArtemisTool, ...] = (search_history, replay_steps, get_step_screenshot)
329
+ HISTORY_TOOL_NAMES: frozenset[str] = frozenset(t.name for t in HISTORY_TOOLS)
330
+
331
+ #: Flash-profile declarations, derived from the very same args schemas.
332
+ SEARCH_HISTORY_TOOL: ToolDeclaration = search_history.to_tool_declaration()
333
+ REPLAY_STEPS_TOOL: ToolDeclaration = replay_steps.to_tool_declaration()
334
+ GET_STEP_SCREENSHOT_TOOL: ToolDeclaration = get_step_screenshot.to_tool_declaration()
335
+ HISTORY_TOOL_DECLARATIONS: tuple[ToolDeclaration, ...] = (
336
+ SEARCH_HISTORY_TOOL,
337
+ REPLAY_STEPS_TOOL,
338
+ GET_STEP_SCREENSHOT_TOOL,
339
+ )
340
+
341
+
342
+ def history_tool_by_name(name: str) -> ArtemisTool | None:
343
+ """The shared tool instance behind a history tool name (Flash dispatch)."""
344
+ raw = name.split(":")[-1] if ":" in name else name
345
+ return next((t for t in HISTORY_TOOLS if t.name == raw), None)
346
+
347
+
348
+ def history_tool_declarations(ctx: Any) -> list[ToolDeclaration]:
349
+ """Declarations of the history tools available for ``ctx`` (Flash runner)."""
350
+ return [tool.to_tool_declaration() for tool in HISTORY_TOOLS if tool.is_available(ctx)]
351
+
352
+
353
+ def get_search_history_tool(ctx: Any) -> BaseTool:
354
+ """Exports search_history as a traced LangChain BaseTool."""
355
+ return trace_langchain_tool(search_history.to_langchain_tool(ctx), ctx)
356
+
357
+
358
+ def get_replay_steps_tool(ctx: Any) -> BaseTool:
359
+ """Exports replay_steps as a traced LangChain BaseTool."""
360
+ return trace_langchain_tool(replay_steps.to_langchain_tool(ctx), ctx)
361
+
362
+
363
+ def get_step_screenshot_tool(ctx: Any) -> BaseTool:
364
+ """Exports get_step_screenshot as a traced LangChain BaseTool."""
365
+ return trace_langchain_tool(get_step_screenshot.to_langchain_tool(ctx), ctx)
366
+
367
+
368
+ def get_history_tools(ctx: Any) -> list[BaseTool]:
369
+ """All three history tools as LangChain tools (read-only agents mount them as a set)."""
370
+ return [get_search_history_tool(ctx), get_replay_steps_tool(ctx), get_step_screenshot_tool(ctx)]
371
+
372
+
373
+ search_history_wrapper = ToolWrapper(
374
+ tool_fn_getter=get_search_history_tool,
375
+ on_success_fn=lambda *a, **k: "Searched history",
376
+ on_failure_fn=lambda err: f"search_history failed: {err}",
377
+ is_available_fn=search_history_available,
378
+ )
379
+
380
+ replay_steps_wrapper = ToolWrapper(
381
+ tool_fn_getter=get_replay_steps_tool,
382
+ on_success_fn=lambda *a, **k: "Replayed steps",
383
+ on_failure_fn=lambda err: f"replay_steps failed: {err}",
384
+ is_available_fn=history_available,
385
+ )
386
+
387
+ get_step_screenshot_wrapper = ToolWrapper(
388
+ tool_fn_getter=get_step_screenshot_tool,
389
+ on_success_fn=lambda *a, **k: "Fetched step screenshot",
390
+ on_failure_fn=lambda err: f"get_step_screenshot failed: {err}",
391
+ is_available_fn=history_available,
392
+ )
393
+
394
+ HISTORY_TOOL_WRAPPERS: tuple[ToolWrapper, ...] = (
395
+ search_history_wrapper,
396
+ replay_steps_wrapper,
397
+ get_step_screenshot_wrapper,
398
+ )
@@ -0,0 +1,120 @@
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
+ """replay_steps: full replay of a step range, exactly as the executing agent saw it.
16
+
17
+ Each step is rendered with :func:`~artemis.utils.task_tree.render_step_replay`
18
+ (``T+mm:ss`` step header, ``[Screen]`` description, reasoning, every
19
+ non-internal tool call with name / arguments / result, the executed action,
20
+ safety-net interception and the execution result). Two loose bounds keep one tool result finite (config
21
+ ``agent.memory.replay``): at most ``max_steps`` steps per call, and a
22
+ ``max_tokens`` budget applied by dropping *whole trailing steps* — a replayed
23
+ step is never cut mid-sentence. Screenshots are deliberately not part of the
24
+ replay; ``get_step_screenshot`` fetches them one at a time.
25
+ """
26
+
27
+ from __future__ import annotations
28
+
29
+ from typing import Any
30
+
31
+ from artemis.core.tool_failure import ToolFailure
32
+ from artemis.utils.task_tree import render_step_replay
33
+
34
+ DEFAULT_MAX_STEPS = 5
35
+ DEFAULT_MAX_TOKENS = 12000
36
+
37
+
38
+ def _estimate_tokens(text: str) -> int:
39
+ return len(text) // 4
40
+
41
+
42
+ def replay_steps_text(
43
+ reader: Any,
44
+ start_step: Any,
45
+ end_step: Any = None,
46
+ *,
47
+ max_steps: int = DEFAULT_MAX_STEPS,
48
+ max_tokens: int = DEFAULT_MAX_TOKENS,
49
+ ) -> str:
50
+ """Replays steps ``start_step``..``end_step`` (inclusive, 1-based) from ``reader``."""
51
+ if reader is None:
52
+ return ToolFailure("Error: no execution history available.")
53
+ try:
54
+ start = int(start_step)
55
+ end = start if end_step is None else int(end_step)
56
+ except (TypeError, ValueError):
57
+ return ToolFailure(
58
+ f"Error: start_step and end_step must be integers, got {start_step!r} and {end_step!r}."
59
+ )
60
+ if start > end:
61
+ start, end = end, start
62
+
63
+ max_steps = max(1, int(max_steps or DEFAULT_MAX_STEPS))
64
+ notes: list[str] = []
65
+ if end - start + 1 > max_steps:
66
+ end = start + max_steps - 1
67
+ notes.append(
68
+ f"(Only {max_steps} steps are replayed per call: showing Steps {start}–{end}."
69
+ f" Call again from Step {end + 1} for the rest.)"
70
+ )
71
+
72
+ try:
73
+ steps: list[dict] = reader.get_agent_friendly_steps_in_range(start, end) or []
74
+ except Exception as e:
75
+ return ToolFailure(f"Error loading steps {start}–{end}: {e}")
76
+ if not steps:
77
+ if start == end:
78
+ return ToolFailure(f"Error: step {start} not found.")
79
+ return ToolFailure(f"Error: no recorded steps in range {start}–{end}.")
80
+
81
+ budget_tokens = max(1, int(max_tokens or DEFAULT_MAX_TOKENS))
82
+ # Step headers carry the session clock (``T+mm:ss``) every agent prompt
83
+ # and search_history use; without a session start they fall back to the
84
+ # stored relative time.
85
+ session_start = getattr(reader, "session_start_time", None)
86
+ rendered: list[str] = []
87
+ shown: list[dict] = []
88
+ used = 0
89
+ dropped = 0
90
+ for step in steps:
91
+ if dropped:
92
+ dropped += 1
93
+ continue
94
+ try:
95
+ text = render_step_replay(step, session_start=session_start)
96
+ except Exception as e:
97
+ return ToolFailure(f"Error rendering steps {start}–{end}: {e}")
98
+ cost = _estimate_tokens(text) + 1
99
+ # The first step is always replayed; afterwards the budget drops whole
100
+ # trailing steps rather than truncating a step in the middle.
101
+ if rendered and used + cost > budget_tokens:
102
+ dropped = 1
103
+ continue
104
+ rendered.append(text)
105
+ shown.append(step)
106
+ used += cost
107
+
108
+ if dropped:
109
+ last_shown = shown[-1].get("step_number", start)
110
+ first_shown = shown[0].get("step_number", start)
111
+ notes.append(
112
+ f"(Token budget reached: Steps {first_shown}–{last_shown} replayed;"
113
+ f" {dropped} more step(s) in the range not shown."
114
+ f" Call again from Step {last_shown + 1}.)"
115
+ )
116
+
117
+ text = "\n\n".join(rendered)
118
+ if notes:
119
+ text += "\n\n" + "\n".join(notes)
120
+ return text
@@ -0,0 +1,139 @@
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
+ """get_step_screenshot: one recorded screenshot of one step, on demand.
16
+
17
+ ``pre`` is the screen observed at the start of the step, ``post`` the screen
18
+ after its action (when recorded), ``overlay`` the pre screenshot with the
19
+ step's action drawn on it (tap point / swipe path) so a reviewer can check
20
+ that the action landed on the intended element. The overlay is drawn from the
21
+ step's *raw* stored action (physical pixels), never from the normalized twin.
22
+ """
23
+
24
+ from __future__ import annotations
25
+
26
+ import base64
27
+ from dataclasses import dataclass
28
+ from pathlib import Path
29
+ import sqlite3
30
+ from typing import Any
31
+
32
+ from artemis.core.tool_failure import ToolFailure
33
+ from artemis.utils.visualization import overlay_action_on_screenshot
34
+ from third_party.mobile_use.utils.logger import get_logger
35
+
36
+ logger = get_logger(__name__)
37
+
38
+ WHICH_CHOICES = ("pre", "post", "overlay")
39
+
40
+
41
+ @dataclass
42
+ class ScreenshotResult:
43
+ """One screenshot lookup: a caption plus the JPEG bytes when a file exists."""
44
+
45
+ step_number: int
46
+ which: str
47
+ description: str
48
+ image_bytes: bytes | None = None
49
+ #: True when ``which == "overlay"`` and the action was actually drawn.
50
+ overlay_drawn: bool = False
51
+ #: True when the lookup could not serve the request (bad arguments, no
52
+ #: history, unknown step, unreadable file). A step that simply has no
53
+ #: screenshot of the requested kind is an answer, not an error.
54
+ is_error: bool = False
55
+
56
+ def to_content_blocks(self) -> list[dict[str, Any]] | str:
57
+ """The tool's return value: content blocks with the image, the
58
+ plain-text answer when there is no image to attach, or a
59
+ :class:`ToolFailure` (status ``error`` downstream) when the lookup
60
+ failed."""
61
+ if self.is_error:
62
+ return ToolFailure(self.description)
63
+ if not self.image_bytes:
64
+ return self.description
65
+ encoded = base64.b64encode(self.image_bytes).decode("utf-8")
66
+ return [
67
+ {"type": "text", "text": self.description},
68
+ {"type": "image_url", "image_url": {"url": f"data:image/jpeg;base64,{encoded}"}},
69
+ ]
70
+
71
+
72
+ def _failure(step_number: int, which: str, description: str) -> ScreenshotResult:
73
+ return ScreenshotResult(step_number, which, description, is_error=True)
74
+
75
+
76
+ def load_step_screenshot(reader: Any, step_number: Any, which: str = "pre") -> ScreenshotResult:
77
+ """Loads (and for ``overlay`` annotates) one step screenshot from ``reader``."""
78
+ which = str(which or "pre").lower()
79
+ try:
80
+ number = int(step_number)
81
+ except (TypeError, ValueError):
82
+ return _failure(-1, which, f"Error: step_number must be an integer, got {step_number!r}.")
83
+ if which not in WHICH_CHOICES:
84
+ return _failure(
85
+ number, which, f"Error: 'which' must be 'pre', 'post' or 'overlay', got '{which}'."
86
+ )
87
+ if reader is None:
88
+ return _failure(number, which, "Error: no execution history available.")
89
+
90
+ source = "pre" if which == "overlay" else which
91
+ try:
92
+ path = reader.get_step_image_path(number, source)
93
+ except (sqlite3.Error, OSError, ValueError) as e:
94
+ return _failure(number, which, f"Error looking up step {number}: {e}")
95
+ if path is None:
96
+ try:
97
+ record = reader.get_step_record(number)
98
+ except (sqlite3.Error, ValueError):
99
+ record = None
100
+ if record is None:
101
+ return _failure(number, which, f"Error: step {number} not found.")
102
+ return ScreenshotResult(
103
+ number, which, f"No {source}-action screenshot recorded for step {number}."
104
+ )
105
+ try:
106
+ image_bytes = Path(path).read_bytes()
107
+ except OSError as e:
108
+ return _failure(number, which, f"Error reading screenshot: {e}")
109
+
110
+ if which != "overlay":
111
+ return ScreenshotResult(
112
+ number, which, f"Screenshot of step {number} ({which}-action) is attached.", image_bytes
113
+ )
114
+
115
+ try:
116
+ record = reader.get_step_record(number)
117
+ except (sqlite3.Error, ValueError):
118
+ record = None
119
+ action = getattr(record, "action_taken", None) if record is not None else None
120
+ try:
121
+ annotated = overlay_action_on_screenshot(image_bytes, action)
122
+ except (TypeError, ValueError, OSError) as e:
123
+ logger.debug(f"Action overlay for step {number} failed: {e}")
124
+ annotated = None
125
+ if annotated is None:
126
+ return ScreenshotResult(
127
+ number,
128
+ which,
129
+ f"Screenshot of step {number} (pre-action; the step's action could not be"
130
+ " drawn, plain screenshot) is attached.",
131
+ image_bytes,
132
+ )
133
+ return ScreenshotResult(
134
+ number,
135
+ which,
136
+ f"Screenshot of step {number} (pre-action with the step's action drawn on it) is attached.",
137
+ annotated,
138
+ overlay_drawn=True,
139
+ )