android-midscene-automation 0.2.4 → 1.1.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (525) hide show
  1. package/CHANGELOG.md +10 -0
  2. package/README.md +159 -29
  3. package/artemis-main/.dockerignore +15 -0
  4. package/artemis-main/.env.example +34 -0
  5. package/artemis-main/CONTRIBUTING.md +35 -0
  6. package/artemis-main/Dockerfile +68 -0
  7. package/artemis-main/LICENSE +202 -0
  8. package/artemis-main/Makefile +115 -0
  9. package/artemis-main/README.md +322 -0
  10. package/artemis-main/README_CN.md +320 -0
  11. package/artemis-main/apps/__init__.py +15 -0
  12. package/artemis-main/apps/admin_console/__init__.py +19 -0
  13. package/artemis-main/apps/admin_console/core/__init__.py +13 -0
  14. package/artemis-main/apps/admin_console/core/config.py +43 -0
  15. package/artemis-main/apps/admin_console/core/security.py +178 -0
  16. package/artemis-main/apps/admin_console/core/state.py +243 -0
  17. package/artemis-main/apps/admin_console/database/__init__.py +13 -0
  18. package/artemis-main/apps/admin_console/database/connection.py +60 -0
  19. package/artemis-main/apps/admin_console/database/repositories/__init__.py +13 -0
  20. package/artemis-main/apps/admin_console/database/repositories/session_repository.py +564 -0
  21. package/artemis-main/apps/admin_console/database/repositories/step_repository.py +512 -0
  22. package/artemis-main/apps/admin_console/database/repositories/trace_repository.py +133 -0
  23. package/artemis-main/apps/admin_console/index.html +3787 -0
  24. package/artemis-main/apps/admin_console/replay_manager.py +2585 -0
  25. package/artemis-main/apps/admin_console/routers/__init__.py +13 -0
  26. package/artemis-main/apps/admin_console/routers/media.py +198 -0
  27. package/artemis-main/apps/admin_console/routers/replay.py +91 -0
  28. package/artemis-main/apps/admin_console/routers/sessions.py +239 -0
  29. package/artemis-main/apps/admin_console/routers/steps.py +109 -0
  30. package/artemis-main/apps/admin_console/routers/stream.py +56 -0
  31. package/artemis-main/apps/admin_console/routers/system.py +761 -0
  32. package/artemis-main/apps/admin_console/routers/tasks.py +517 -0
  33. package/artemis-main/apps/admin_console/schemas/__init__.py +13 -0
  34. package/artemis-main/apps/admin_console/schemas/task_schema.py +47 -0
  35. package/artemis-main/apps/admin_console/server.py +508 -0
  36. package/artemis-main/apps/admin_console/services/__init__.py +13 -0
  37. package/artemis-main/apps/admin_console/services/device_stream_service.py +137 -0
  38. package/artemis-main/apps/admin_console/services/ipc_service.py +262 -0
  39. package/artemis-main/apps/admin_console/services/media_service.py +623 -0
  40. package/artemis-main/apps/admin_console/services/model_service.py +148 -0
  41. package/artemis-main/apps/admin_console/services/task_preset_catalog.py +514 -0
  42. package/artemis-main/apps/admin_console/services/task_queue_service.py +1719 -0
  43. package/artemis-main/apps/admin_console/services/worker_process_io.py +144 -0
  44. package/artemis-main/apps/showcase_ui/README.md +31 -0
  45. package/artemis-main/apps/showcase_ui/angular.json +132 -0
  46. package/artemis-main/apps/showcase_ui/package-lock.json +9472 -0
  47. package/artemis-main/apps/showcase_ui/package.json +39 -0
  48. package/artemis-main/apps/showcase_ui/proxy.conf.json +23 -0
  49. package/artemis-main/apps/showcase_ui/public/favicon.ico +0 -0
  50. package/artemis-main/apps/showcase_ui/public/logo.png +0 -0
  51. package/artemis-main/apps/showcase_ui/public/logo.svg +56 -0
  52. package/artemis-main/apps/showcase_ui/src/app/app.component.html +24 -0
  53. package/artemis-main/apps/showcase_ui/src/app/app.component.scss +49 -0
  54. package/artemis-main/apps/showcase_ui/src/app/app.component.spec.ts +42 -0
  55. package/artemis-main/apps/showcase_ui/src/app/app.component.ts +32 -0
  56. package/artemis-main/apps/showcase_ui/src/app/app.config.ts +29 -0
  57. package/artemis-main/apps/showcase_ui/src/app/app.routes.ts +27 -0
  58. package/artemis-main/apps/showcase_ui/src/app/components/agent-stream/agent-stream-startup.spec.ts +108 -0
  59. package/artemis-main/apps/showcase_ui/src/app/components/agent-stream/agent-stream.component.html +1436 -0
  60. package/artemis-main/apps/showcase_ui/src/app/components/agent-stream/agent-stream.component.scss +3973 -0
  61. package/artemis-main/apps/showcase_ui/src/app/components/agent-stream/agent-stream.component.ts +2235 -0
  62. package/artemis-main/apps/showcase_ui/src/app/components/chat-interface/chat-interface.component.html +380 -0
  63. package/artemis-main/apps/showcase_ui/src/app/components/chat-interface/chat-interface.component.scss +1171 -0
  64. package/artemis-main/apps/showcase_ui/src/app/components/chat-interface/chat-interface.component.ts +270 -0
  65. package/artemis-main/apps/showcase_ui/src/app/components/floating-video-player/floating-video-player.component.html +433 -0
  66. package/artemis-main/apps/showcase_ui/src/app/components/floating-video-player/floating-video-player.component.scss +987 -0
  67. package/artemis-main/apps/showcase_ui/src/app/components/floating-video-player/floating-video-player.component.ts +632 -0
  68. package/artemis-main/apps/showcase_ui/src/app/components/legacy-agent-stream/legacy-agent-stream.component.html +130 -0
  69. package/artemis-main/apps/showcase_ui/src/app/components/legacy-agent-stream/legacy-agent-stream.component.scss +406 -0
  70. package/artemis-main/apps/showcase_ui/src/app/components/legacy-agent-stream/legacy-agent-stream.component.ts +74 -0
  71. package/artemis-main/apps/showcase_ui/src/app/components/nav-switcher/nav-switcher.component.scss +115 -0
  72. package/artemis-main/apps/showcase_ui/src/app/components/nav-switcher/nav-switcher.component.ts +52 -0
  73. package/artemis-main/apps/showcase_ui/src/app/core/data/smart-tasks.data.ts +334 -0
  74. package/artemis-main/apps/showcase_ui/src/app/core/i18n/language.service.spec.ts +55 -0
  75. package/artemis-main/apps/showcase_ui/src/app/core/i18n/language.service.ts +61 -0
  76. package/artemis-main/apps/showcase_ui/src/app/core/i18n/translate.pipe.ts +11 -0
  77. package/artemis-main/apps/showcase_ui/src/app/core/i18n/zh-cn.ts +651 -0
  78. package/artemis-main/apps/showcase_ui/src/app/core/models/markdown.model.ts +43 -0
  79. package/artemis-main/apps/showcase_ui/src/app/core/models/pro-tuning.model.spec.ts +47 -0
  80. package/artemis-main/apps/showcase_ui/src/app/core/models/pro-tuning.model.ts +165 -0
  81. package/artemis-main/apps/showcase_ui/src/app/core/models/session.model.ts +85 -0
  82. package/artemis-main/apps/showcase_ui/src/app/core/models/stream.model.ts +246 -0
  83. package/artemis-main/apps/showcase_ui/src/app/core/models/system.model.ts +118 -0
  84. package/artemis-main/apps/showcase_ui/src/app/core/services/task-recommendation.service.ts +111 -0
  85. package/artemis-main/apps/showcase_ui/src/app/pages/home/home.component.html +2130 -0
  86. package/artemis-main/apps/showcase_ui/src/app/pages/home/home.component.scss +5204 -0
  87. package/artemis-main/apps/showcase_ui/src/app/pages/home/home.component.ts +1189 -0
  88. package/artemis-main/apps/showcase_ui/src/app/pages/legacy-workspace/legacy-workspace.component.html +39 -0
  89. package/artemis-main/apps/showcase_ui/src/app/pages/legacy-workspace/legacy-workspace.component.scss +151 -0
  90. package/artemis-main/apps/showcase_ui/src/app/pages/legacy-workspace/legacy-workspace.component.ts +58 -0
  91. package/artemis-main/apps/showcase_ui/src/app/pages/workspace/workspace.component.html +201 -0
  92. package/artemis-main/apps/showcase_ui/src/app/pages/workspace/workspace.component.scss +886 -0
  93. package/artemis-main/apps/showcase_ui/src/app/pages/workspace/workspace.component.ts +324 -0
  94. package/artemis-main/apps/showcase_ui/src/app/services/agent.service.spec.ts +418 -0
  95. package/artemis-main/apps/showcase_ui/src/app/services/agent.service.ts +2012 -0
  96. package/artemis-main/apps/showcase_ui/src/app/services/system.service.spec.ts +173 -0
  97. package/artemis-main/apps/showcase_ui/src/app/services/system.service.ts +620 -0
  98. package/artemis-main/apps/showcase_ui/src/app/utils/action-formatter.util.spec.ts +513 -0
  99. package/artemis-main/apps/showcase_ui/src/app/utils/action-formatter.util.ts +1015 -0
  100. package/artemis-main/apps/showcase_ui/src/app/utils/image-overlay.util.spec.ts +85 -0
  101. package/artemis-main/apps/showcase_ui/src/app/utils/image-overlay.util.ts +662 -0
  102. package/artemis-main/apps/showcase_ui/src/app/utils/markdown-parser.util.spec.ts +70 -0
  103. package/artemis-main/apps/showcase_ui/src/app/utils/markdown-parser.util.ts +383 -0
  104. package/artemis-main/apps/showcase_ui/src/app/utils/recording-timeline.util.spec.ts +107 -0
  105. package/artemis-main/apps/showcase_ui/src/app/utils/recording-timeline.util.ts +123 -0
  106. package/artemis-main/apps/showcase_ui/src/app/utils/run-info.util.spec.ts +92 -0
  107. package/artemis-main/apps/showcase_ui/src/app/utils/run-info.util.ts +116 -0
  108. package/artemis-main/apps/showcase_ui/src/app/utils/stream-aggregator.util.spec.ts +640 -0
  109. package/artemis-main/apps/showcase_ui/src/app/utils/stream-aggregator.util.ts +1021 -0
  110. package/artemis-main/apps/showcase_ui/src/app/utils/tool-formatter.util.spec.ts +375 -0
  111. package/artemis-main/apps/showcase_ui/src/app/utils/tool-formatter.util.ts +1419 -0
  112. package/artemis-main/apps/showcase_ui/src/index.html +37 -0
  113. package/artemis-main/apps/showcase_ui/src/main.ts +22 -0
  114. package/artemis-main/apps/showcase_ui/src/styles.scss +60 -0
  115. package/artemis-main/apps/showcase_ui/tsconfig.app.json +23 -0
  116. package/artemis-main/apps/showcase_ui/tsconfig.json +27 -0
  117. package/artemis-main/apps/showcase_ui/tsconfig.spec.json +23 -0
  118. package/artemis-main/artemis/__init__.py +44 -0
  119. package/artemis-main/artemis/__main__.py +18 -0
  120. package/artemis-main/artemis/_legacy_imports.py +94 -0
  121. package/artemis-main/artemis/_version.py +30 -0
  122. package/artemis-main/artemis/agents/__init__.py +17 -0
  123. package/artemis-main/artemis/agents/checker/checker.json +9 -0
  124. package/artemis-main/artemis/agents/checker/checker.py +859 -0
  125. package/artemis-main/artemis/agents/diagnoser/diagnoser.json +23 -0
  126. package/artemis-main/artemis/agents/diagnoser/diagnoser.py +608 -0
  127. package/artemis-main/artemis/agents/explorer/constants.py +52 -0
  128. package/artemis-main/artemis/agents/explorer/explorer.json +57 -0
  129. package/artemis-main/artemis/agents/explorer/explorer.py +473 -0
  130. package/artemis-main/artemis/agents/explorer/geometry.py +73 -0
  131. package/artemis-main/artemis/agents/explorer/native_runner.py +878 -0
  132. package/artemis-main/artemis/agents/explorer/perception_tools.py +632 -0
  133. package/artemis-main/artemis/agents/explorer/run_setup.py +403 -0
  134. package/artemis-main/artemis/agents/explorer/screen_index.py +266 -0
  135. package/artemis-main/artemis/agents/explorer/tiers.py +110 -0
  136. package/artemis-main/artemis/agents/explorer/tool_declarations.py +394 -0
  137. package/artemis-main/artemis/agents/explorer/universal_runner.py +317 -0
  138. package/artemis-main/artemis/agents/flash/context_compressor.py +452 -0
  139. package/artemis-main/artemis/agents/flash/flash_runner.md +51 -0
  140. package/artemis-main/artemis/agents/flash/flash_summarizer.md +53 -0
  141. package/artemis-main/artemis/agents/flash/flash_summarizer_single.md +54 -0
  142. package/artemis-main/artemis/agents/flash/runner.py +1167 -0
  143. package/artemis-main/artemis/agents/flash/summarizer.py +511 -0
  144. package/artemis-main/artemis/agents/history_analyzer/history_analyzer.md +8 -0
  145. package/artemis-main/artemis/agents/history_analyzer/history_analyzer.py +182 -0
  146. package/artemis-main/artemis/agents/image_processor/image_processor.md +74 -0
  147. package/artemis-main/artemis/agents/image_processor/image_processor.py +322 -0
  148. package/artemis-main/artemis/agents/log_analyzer/log_analyzer.md +15 -0
  149. package/artemis-main/artemis/agents/log_analyzer/log_analyzer.py +403 -0
  150. package/artemis-main/artemis/agents/log_analyzer/log_reader_sub_agent.md +8 -0
  151. package/artemis-main/artemis/agents/log_analyzer/output_analyzer.md +11 -0
  152. package/artemis-main/artemis/agents/log_analyzer/output_analyzer.py +163 -0
  153. package/artemis-main/artemis/agents/object_detector/object_detector.json +9 -0
  154. package/artemis-main/artemis/agents/object_detector/object_detector.py +211 -0
  155. package/artemis-main/artemis/agents/operator/operator.json +3 -0
  156. package/artemis-main/artemis/agents/operator/operator.py +1394 -0
  157. package/artemis-main/artemis/agents/operator/prompts.py +1064 -0
  158. package/artemis-main/artemis/agents/outputter/outputter.md +36 -0
  159. package/artemis-main/artemis/agents/outputter/outputter.py +552 -0
  160. package/artemis-main/artemis/agents/planner/planner.json +32 -0
  161. package/artemis-main/artemis/agents/planner/planner.py +436 -0
  162. package/artemis-main/artemis/agents/prompt_assembly.py +79 -0
  163. package/artemis-main/artemis/agents/summarizer/summarizer.py +190 -0
  164. package/artemis-main/artemis/agents/validator/action_execution.py +98 -0
  165. package/artemis-main/artemis/agents/validator/categories.py +33 -0
  166. package/artemis-main/artemis/agents/validator/execution_loop.py +488 -0
  167. package/artemis-main/artemis/agents/validator/incidents.py +120 -0
  168. package/artemis-main/artemis/agents/validator/pixel_safety_net.md +15 -0
  169. package/artemis-main/artemis/agents/validator/precondition_pixel.py +320 -0
  170. package/artemis-main/artemis/agents/validator/precondition_xml.py +672 -0
  171. package/artemis-main/artemis/agents/validator/tool_declarations.py +332 -0
  172. package/artemis-main/artemis/agents/validator/validator.py +188 -0
  173. package/artemis-main/artemis/agents/video_analyzer/__init__.py +15 -0
  174. package/artemis-main/artemis/agents/video_analyzer/audio_native.py +543 -0
  175. package/artemis-main/artemis/agents/video_analyzer/blackboard.py +821 -0
  176. package/artemis-main/artemis/agents/video_analyzer/chunk_agentic.py +455 -0
  177. package/artemis-main/artemis/agents/video_analyzer/chunk_conversation.py +491 -0
  178. package/artemis-main/artemis/agents/video_analyzer/chunk_native.py +437 -0
  179. package/artemis-main/artemis/agents/video_analyzer/conflict_resolution.py +137 -0
  180. package/artemis-main/artemis/agents/video_analyzer/gemini_files.py +162 -0
  181. package/artemis-main/artemis/agents/video_analyzer/native_coordinator.py +541 -0
  182. package/artemis-main/artemis/agents/video_analyzer/reliability.py +128 -0
  183. package/artemis-main/artemis/agents/video_analyzer/universal_engine.py +487 -0
  184. package/artemis-main/artemis/agents/video_analyzer/universal_tools.py +323 -0
  185. package/artemis-main/artemis/agents/video_analyzer/video_analyzer.md +51 -0
  186. package/artemis-main/artemis/agents/video_analyzer/video_analyzer.py +875 -0
  187. package/artemis-main/artemis/agents/video_analyzer/video_sub_agent.md +15 -0
  188. package/artemis-main/artemis/agents/video_analyzer/video_sub_agent_audio.md +12 -0
  189. package/artemis-main/artemis/clients/accessibility_client.py +438 -0
  190. package/artemis-main/artemis/clients/screen_client_factory.py +403 -0
  191. package/artemis-main/artemis/config/__init__.py +309 -0
  192. package/artemis-main/artemis/config/agent.py +1098 -0
  193. package/artemis-main/artemis/config/constants.py +151 -0
  194. package/artemis-main/artemis/config/core/__init__.py +24 -0
  195. package/artemis-main/artemis/config/core/engine.py +118 -0
  196. package/artemis-main/artemis/config/core/interpolator.py +78 -0
  197. package/artemis-main/artemis/config/llm.py +267 -0
  198. package/artemis-main/artemis/config/paths.py +244 -0
  199. package/artemis-main/artemis/config/runtime.py +197 -0
  200. package/artemis-main/artemis/config/settings.py +315 -0
  201. package/artemis-main/artemis/constants.py +47 -0
  202. package/artemis-main/artemis/context.py +289 -0
  203. package/artemis-main/artemis/controllers/__init__.py +41 -0
  204. package/artemis-main/artemis/controllers/controller_factory.py +41 -0
  205. package/artemis-main/artemis/controllers/device_controller.py +29 -0
  206. package/artemis-main/artemis/controllers/types.py +37 -0
  207. package/artemis-main/artemis/controllers/unified_controller.py +649 -0
  208. package/artemis-main/artemis/core/__init__.py +21 -0
  209. package/artemis-main/artemis/core/diagnostics/__init__.py +90 -0
  210. package/artemis-main/artemis/core/diagnostics/adb_keys.py +270 -0
  211. package/artemis-main/artemis/core/diagnostics/adb_server_connection.py +330 -0
  212. package/artemis-main/artemis/core/diagnostics/device_smoke.py +364 -0
  213. package/artemis-main/artemis/core/diagnostics/emulator_manager.py +501 -0
  214. package/artemis-main/artemis/core/diagnostics/engine.py +438 -0
  215. package/artemis-main/artemis/core/diagnostics/hierarchy_parity.py +216 -0
  216. package/artemis-main/artemis/core/diagnostics/probes/adb_probe.py +887 -0
  217. package/artemis-main/artemis/core/diagnostics/probes/base.py +45 -0
  218. package/artemis-main/artemis/core/diagnostics/probes/credentials_probe.py +296 -0
  219. package/artemis-main/artemis/core/diagnostics/probes/host_probe.py +427 -0
  220. package/artemis-main/artemis/core/diagnostics/probes/runtime_probe.py +169 -0
  221. package/artemis-main/artemis/core/diagnostics/probes/toolchain_probe.py +139 -0
  222. package/artemis-main/artemis/core/diagnostics/readiness.py +83 -0
  223. package/artemis-main/artemis/core/diagnostics/schema.py +118 -0
  224. package/artemis-main/artemis/core/tool_declaration.py +43 -0
  225. package/artemis-main/artemis/core/tool_failure.py +56 -0
  226. package/artemis-main/artemis/data_engine/context_vars.py +24 -0
  227. package/artemis-main/artemis/data_engine/engine.py +1615 -0
  228. package/artemis-main/artemis/data_engine/history_reader.py +142 -0
  229. package/artemis-main/artemis/data_engine/models.py +131 -0
  230. package/artemis-main/artemis/data_engine/storage.py +1245 -0
  231. package/artemis-main/artemis/data_engine/trace.py +727 -0
  232. package/artemis-main/artemis/drivers/__init__.py +48 -0
  233. package/artemis-main/artemis/drivers/android/adb_driver.py +507 -0
  234. package/artemis-main/artemis/drivers/android/hierarchy.py +64 -0
  235. package/artemis-main/artemis/drivers/android/input_ime.py +120 -0
  236. package/artemis-main/artemis/drivers/android/recorder.py +57 -0
  237. package/artemis-main/artemis/drivers/base.py +290 -0
  238. package/artemis-main/artemis/drivers/cloud/cloud_driver.py +117 -0
  239. package/artemis-main/artemis/drivers/factory.py +80 -0
  240. package/artemis-main/artemis/drivers/mock/mock_driver.py +198 -0
  241. package/artemis-main/artemis/drivers/types.py +67 -0
  242. package/artemis-main/artemis/graph/checkpoints.py +1243 -0
  243. package/artemis-main/artemis/graph/graph.py +1055 -0
  244. package/artemis-main/artemis/graph/perception.py +234 -0
  245. package/artemis-main/artemis/graph/state.py +151 -0
  246. package/artemis-main/artemis/graph/visibility.py +238 -0
  247. package/artemis-main/artemis/interfaces/cli/__init__.py +19 -0
  248. package/artemis-main/artemis/interfaces/cli/commands/__init__.py +15 -0
  249. package/artemis-main/artemis/interfaces/cli/commands/batch.py +278 -0
  250. package/artemis-main/artemis/interfaces/cli/commands/doctor.py +468 -0
  251. package/artemis-main/artemis/interfaces/cli/commands/helper.py +226 -0
  252. package/artemis-main/artemis/interfaces/cli/commands/init.py +193 -0
  253. package/artemis-main/artemis/interfaces/cli/commands/mcp.py +862 -0
  254. package/artemis-main/artemis/interfaces/cli/commands/run.py +463 -0
  255. package/artemis-main/artemis/interfaces/cli/commands/server.py +151 -0
  256. package/artemis-main/artemis/interfaces/cli/commands/server_lifecycle.py +328 -0
  257. package/artemis-main/artemis/interfaces/cli/commands/trace.py +111 -0
  258. package/artemis-main/artemis/interfaces/cli/commands/ui.py +404 -0
  259. package/artemis-main/artemis/interfaces/cli/main.py +109 -0
  260. package/artemis-main/artemis/interfaces/sdk/__init__.py +31 -0
  261. package/artemis-main/artemis/interfaces/sdk/client.py +19 -0
  262. package/artemis-main/artemis/interfaces/sdk/task.py +72 -0
  263. package/artemis-main/artemis/llm/__init__.py +25 -0
  264. package/artemis-main/artemis/llm/fake_model.py +179 -0
  265. package/artemis-main/artemis/llm/google/__init__.py +57 -0
  266. package/artemis-main/artemis/llm/google/provider.py +185 -0
  267. package/artemis-main/artemis/llm/google/usage.py +148 -0
  268. package/artemis-main/artemis/llm/parser.py +32 -0
  269. package/artemis-main/artemis/llm/reliability.py +267 -0
  270. package/artemis-main/artemis/llm/router.py +389 -0
  271. package/artemis-main/artemis/llm/schemas.py +36 -0
  272. package/artemis-main/artemis/llm/structured.py +196 -0
  273. package/artemis-main/artemis/main.py +58 -0
  274. package/artemis-main/artemis/mcp/__init__.py +20 -0
  275. package/artemis-main/artemis/mcp/action_executor.py +760 -0
  276. package/artemis-main/artemis/mcp/action_manifest.py +320 -0
  277. package/artemis-main/artemis/mcp/action_names.py +172 -0
  278. package/artemis-main/artemis/mcp/action_server.py +202 -0
  279. package/artemis-main/artemis/mcp/action_session.py +245 -0
  280. package/artemis-main/artemis/mcp/action_specs.py +868 -0
  281. package/artemis-main/artemis/mcp/action_types.py +86 -0
  282. package/artemis-main/artemis/mcp/actuators/__init__.py +21 -0
  283. package/artemis-main/artemis/mcp/actuators/adb.py +414 -0
  284. package/artemis-main/artemis/mcp/actuators/base.py +113 -0
  285. package/artemis-main/artemis/mcp/actuators/mock.py +70 -0
  286. package/artemis-main/artemis/mcp/adb_server.py +490 -0
  287. package/artemis-main/artemis/mcp/observation.py +133 -0
  288. package/artemis-main/artemis/mcp/xml_search_server.py +297 -0
  289. package/artemis-main/artemis/memory/__init__.py +90 -0
  290. package/artemis-main/artemis/memory/chunking.py +1843 -0
  291. package/artemis-main/artemis/memory/context_policy.py +226 -0
  292. package/artemis-main/artemis/memory/step_capsule.md +73 -0
  293. package/artemis-main/artemis/memory/step_memory.py +281 -0
  294. package/artemis-main/artemis/memory/transcript.py +798 -0
  295. package/artemis-main/artemis/platform/__init__.py +61 -0
  296. package/artemis-main/artemis/platform/base.py +104 -0
  297. package/artemis-main/artemis/platform/darwin.py +184 -0
  298. package/artemis-main/artemis/platform/linux.py +207 -0
  299. package/artemis-main/artemis/platform/windows.py +193 -0
  300. package/artemis-main/artemis/resources/__init__.py +46 -0
  301. package/artemis-main/artemis/runtime/__init__.py +108 -0
  302. package/artemis-main/artemis/runtime/adb_endpoint.py +173 -0
  303. package/artemis-main/artemis/runtime/awake_lease.py +283 -0
  304. package/artemis-main/artemis/runtime/awake_service.py +343 -0
  305. package/artemis-main/artemis/runtime/cancel_requests.py +254 -0
  306. package/artemis-main/artemis/runtime/daemon_client.py +422 -0
  307. package/artemis-main/artemis/runtime/device_lock.py +1013 -0
  308. package/artemis-main/artemis/runtime/device_pool.py +478 -0
  309. package/artemis-main/artemis/runtime/helper_manager.py +1030 -0
  310. package/artemis-main/artemis/runtime/process_probe.py +98 -0
  311. package/artemis-main/artemis/runtime/server_lifecycle.py +435 -0
  312. package/artemis-main/artemis/runtime/supervisor.py +121 -0
  313. package/artemis-main/artemis/runtime/trace_store.py +375 -0
  314. package/artemis-main/artemis/sdk/__init__.py +26 -0
  315. package/artemis-main/artemis/sdk/agent.py +485 -0
  316. package/artemis-main/artemis/sdk/builders/__init__.py +29 -0
  317. package/artemis-main/artemis/sdk/builders/agent_config_builder.py +474 -0
  318. package/artemis-main/artemis/sdk/constants.py +15 -0
  319. package/artemis-main/artemis/sdk/run_outcome.py +58 -0
  320. package/artemis-main/artemis/sdk/types/__init__.py +61 -0
  321. package/artemis-main/artemis/sdk/types/agent.py +89 -0
  322. package/artemis-main/artemis/sdk/utils.py +28 -0
  323. package/artemis-main/artemis/services/llm.py +1040 -0
  324. package/artemis-main/artemis/services/token_meter.py +267 -0
  325. package/artemis-main/artemis/telemetry/__init__.py +35 -0
  326. package/artemis-main/artemis/telemetry/models.py +64 -0
  327. package/artemis-main/artemis/telemetry/replay.py +34 -0
  328. package/artemis-main/artemis/telemetry/storage/json_storage.py +47 -0
  329. package/artemis-main/artemis/telemetry/tracer.py +85 -0
  330. package/artemis-main/artemis/toolchain/__init__.py +52 -0
  331. package/artemis-main/artemis/toolchain/descriptors.py +92 -0
  332. package/artemis-main/artemis/toolchain/resolver.py +185 -0
  333. package/artemis-main/artemis/tools/base.py +232 -0
  334. package/artemis-main/artemis/tools/command_tool.py +1240 -0
  335. package/artemis-main/artemis/tools/committee_prompts.json +5 -0
  336. package/artemis-main/artemis/tools/committee_tool.py +422 -0
  337. package/artemis-main/artemis/tools/diagnoser_submit_answer_tool.py +87 -0
  338. package/artemis-main/artemis/tools/diagnostic_tool.py +120 -0
  339. package/artemis-main/artemis/tools/explorer_tool.py +581 -0
  340. package/artemis-main/artemis/tools/history/__init__.py +398 -0
  341. package/artemis-main/artemis/tools/history/replay.py +120 -0
  342. package/artemis-main/artemis/tools/history/screenshot.py +139 -0
  343. package/artemis-main/artemis/tools/history/search.py +563 -0
  344. package/artemis-main/artemis/tools/image_processor_tool.py +124 -0
  345. package/artemis-main/artemis/tools/index.py +47 -0
  346. package/artemis-main/artemis/tools/log_tool.py +108 -0
  347. package/artemis-main/artemis/tools/mobile/launch_app.py +132 -0
  348. package/artemis-main/artemis/tools/mobile/log_utils.py +138 -0
  349. package/artemis-main/artemis/tools/mobile/ocr.py +202 -0
  350. package/artemis-main/artemis/tools/mobile/read_hierarchy.py +108 -0
  351. package/artemis-main/artemis/tools/mobile/read_logs.py +116 -0
  352. package/artemis-main/artemis/tools/mobile/search_logs.py +260 -0
  353. package/artemis-main/artemis/tools/object_detection_tool.py +244 -0
  354. package/artemis-main/artemis/tools/scratchpad.py +819 -0
  355. package/artemis-main/artemis/tools/system/adb_shell.py +31 -0
  356. package/artemis-main/artemis/tools/tool_wrapper.py +270 -0
  357. package/artemis-main/artemis/tools/video_tool.py +261 -0
  358. package/artemis-main/artemis/tools/wait_tool.py +97 -0
  359. package/artemis-main/artemis/utils/app_launch_utils.py +207 -0
  360. package/artemis-main/artemis/utils/coordinates.py +606 -0
  361. package/artemis-main/artemis/utils/credentials_validator.py +201 -0
  362. package/artemis-main/artemis/utils/cv_canvas.py +189 -0
  363. package/artemis-main/artemis/utils/cython_compat.py +45 -0
  364. package/artemis-main/artemis/utils/element_hit_test.py +80 -0
  365. package/artemis-main/artemis/utils/image_diff.py +335 -0
  366. package/artemis-main/artemis/utils/image_hash.py +80 -0
  367. package/artemis-main/artemis/utils/notes.py +470 -0
  368. package/artemis-main/artemis/utils/ocr_api.py +105 -0
  369. package/artemis-main/artemis/utils/ocr_xml_fusion.py +318 -0
  370. package/artemis-main/artemis/utils/plan_grammar.py +513 -0
  371. package/artemis-main/artemis/utils/python_executor.py +213 -0
  372. package/artemis-main/artemis/utils/startup_progress.py +60 -0
  373. package/artemis-main/artemis/utils/task_tree.py +1174 -0
  374. package/artemis-main/artemis/utils/text.py +37 -0
  375. package/artemis-main/artemis/utils/ui_filter.py +651 -0
  376. package/artemis-main/artemis/utils/video.py +685 -0
  377. package/artemis-main/artemis/utils/visualization.py +802 -0
  378. package/artemis-main/config/artemis.example.jsonc +12 -0
  379. package/artemis-main/mcp_server/README.md +111 -0
  380. package/artemis-main/mcp_server/__init__.py +21 -0
  381. package/artemis-main/mcp_server/__main__.py +20 -0
  382. package/artemis-main/mcp_server/background/__init__.py +15 -0
  383. package/artemis-main/mcp_server/background/task_runner.py +453 -0
  384. package/artemis-main/mcp_server/base.py +49 -0
  385. package/artemis-main/mcp_server/notifiers/__init__.py +74 -0
  386. package/artemis-main/mcp_server/notifiers/agentapi.py +289 -0
  387. package/artemis-main/mcp_server/notifiers/base.py +45 -0
  388. package/artemis-main/mcp_server/notifiers/composite.py +87 -0
  389. package/artemis-main/mcp_server/notifiers/desktop.py +108 -0
  390. package/artemis-main/mcp_server/notifiers/file.py +69 -0
  391. package/artemis-main/mcp_server/notifiers/script.py +90 -0
  392. package/artemis-main/mcp_server/notifiers/webhook.py +87 -0
  393. package/artemis-main/mcp_server/rules.md +68 -0
  394. package/artemis-main/mcp_server/server.py +106 -0
  395. package/artemis-main/mcp_server/tools/__init__.py +29 -0
  396. package/artemis-main/mcp_server/tools/device_state.py +107 -0
  397. package/artemis-main/mcp_server/tools/diagnose.py +1249 -0
  398. package/artemis-main/mcp_server/tools/inspect_trace.py +394 -0
  399. package/artemis-main/mcp_server/tools/task_manager.py +627 -0
  400. package/artemis-main/mcp_server/tools/task_runner.py +548 -0
  401. package/artemis-main/mcp_server/utils/__init__.py +15 -0
  402. package/artemis-main/mcp_server/utils/device_utils.py +180 -0
  403. package/artemis-main/mcp_server/utils/env_utils.py +65 -0
  404. package/artemis-main/packages/artemis-accessibility-helper/ArtemisAccessibilityHelper.apk +0 -0
  405. package/artemis-main/packages/artemis-accessibility-helper/app/build.gradle.kts +35 -0
  406. package/artemis-main/packages/artemis-accessibility-helper/app/src/main/AndroidManifest.xml +49 -0
  407. package/artemis-main/packages/artemis-accessibility-helper/app/src/main/java/com/artemis/helper/A11yNode.java +274 -0
  408. package/artemis-main/packages/artemis-accessibility-helper/app/src/main/java/com/artemis/helper/ArtemisAccessibilityService.java +227 -0
  409. package/artemis-main/packages/artemis-accessibility-helper/app/src/main/java/com/artemis/helper/CommandServer.java +463 -0
  410. package/artemis-main/packages/artemis-accessibility-helper/app/src/main/java/com/artemis/helper/DisplayUtils.java +111 -0
  411. package/artemis-main/packages/artemis-accessibility-helper/app/src/main/java/com/artemis/helper/GestureController.java +232 -0
  412. package/artemis-main/packages/artemis-accessibility-helper/app/src/main/java/com/artemis/helper/HierarchyDumper.java +832 -0
  413. package/artemis-main/packages/artemis-accessibility-helper/app/src/main/java/com/artemis/helper/TokenReceiver.java +36 -0
  414. package/artemis-main/packages/artemis-accessibility-helper/app/src/main/java/com/artemis/helper/TokenStore.java +38 -0
  415. package/artemis-main/packages/artemis-accessibility-helper/app/src/main/java/com/artemis/helper/XmlUtils.java +102 -0
  416. package/artemis-main/packages/artemis-accessibility-helper/app/src/main/res/values/strings.xml +4 -0
  417. package/artemis-main/packages/artemis-accessibility-helper/app/src/main/res/xml/accessibility_service_config.xml +10 -0
  418. package/artemis-main/packages/artemis-accessibility-helper/build.gradle.kts +3 -0
  419. package/artemis-main/packages/artemis-accessibility-helper/build_apk.sh +215 -0
  420. package/artemis-main/packages/artemis-accessibility-helper/debug.keystore +0 -0
  421. package/artemis-main/packages/artemis-accessibility-helper/gradle.properties +2 -0
  422. package/artemis-main/packages/artemis-accessibility-helper/helper_manifest.json +7 -0
  423. package/artemis-main/packages/artemis-accessibility-helper/settings.gradle.kts +17 -0
  424. package/artemis-main/packages/artemis-client/DESIGN.md +89 -0
  425. package/artemis-main/packages/artemis-client/README.md +112 -0
  426. package/artemis-main/packages/artemis-client/examples/basic.py +30 -0
  427. package/artemis-main/packages/artemis-client/pyproject.toml +36 -0
  428. package/artemis-main/packages/artemis-client/src/artemis_client/__init__.py +45 -0
  429. package/artemis-main/packages/artemis-client/src/artemis_client/client.py +401 -0
  430. package/artemis-main/packages/artemis-client/src/artemis_client/errors.py +66 -0
  431. package/artemis-main/packages/artemis-client/src/artemis_client/models.py +212 -0
  432. package/artemis-main/packages/artemis-client/src/artemis_client/py.typed +1 -0
  433. package/artemis-main/packages/artemis-client/src/artemis_client/transport.py +134 -0
  434. package/artemis-main/packages/artemis-client/tests/test_client.py +289 -0
  435. package/artemis-main/packages/artemis-client/tests/test_transport.py +85 -0
  436. package/artemis-main/pyproject.toml +259 -0
  437. package/artemis-main/pyright-core.json +17 -0
  438. package/artemis-main/scripts/check_dependency_sources.py +68 -0
  439. package/artemis-main/scripts/install_deps.ps1 +520 -0
  440. package/artemis-main/scripts/install_deps.sh +533 -0
  441. package/artemis-main/scripts/quality_ratchet.py +97 -0
  442. package/artemis-main/scripts/real_device_video_acceptance.py +483 -0
  443. package/artemis-main/scripts/run_edge_case_part.py +327 -0
  444. package/artemis-main/scripts/run_ui.py +242 -0
  445. package/artemis-main/scripts/start.ps1 +500 -0
  446. package/artemis-main/scripts/test_all_edge_cases.py +269 -0
  447. package/artemis-main/scripts/verify_concurrency_and_devices.py +385 -0
  448. package/artemis-main/setup.py +164 -0
  449. package/artemis-main/start.bat +35 -0
  450. package/artemis-main/start.sh +423 -0
  451. package/artemis-main/third_party/mobile_use/LICENSE +190 -0
  452. package/artemis-main/third_party/mobile_use/METADATA +27 -0
  453. package/artemis-main/third_party/mobile_use/NOTICE +12 -0
  454. package/artemis-main/third_party/mobile_use/__init__.py +19 -0
  455. package/artemis-main/third_party/mobile_use/agents/__init__.py +13 -0
  456. package/artemis-main/third_party/mobile_use/agents/hopper/__init__.py +13 -0
  457. package/artemis-main/third_party/mobile_use/agents/hopper/hopper.md +22 -0
  458. package/artemis-main/third_party/mobile_use/agents/hopper/hopper.py +80 -0
  459. package/artemis-main/third_party/mobile_use/clients/__init__.py +13 -0
  460. package/artemis-main/third_party/mobile_use/clients/ui_automator_client.py +482 -0
  461. package/artemis-main/third_party/mobile_use/config/__init__.py +13 -0
  462. package/artemis-main/third_party/mobile_use/config/llm.py +183 -0
  463. package/artemis-main/third_party/mobile_use/config/output.py +184 -0
  464. package/artemis-main/third_party/mobile_use/context.py +119 -0
  465. package/artemis-main/third_party/mobile_use/controllers/__init__.py +13 -0
  466. package/artemis-main/third_party/mobile_use/controllers/platform_specific_commands_controller.py +151 -0
  467. package/artemis-main/third_party/mobile_use/controllers/types.py +92 -0
  468. package/artemis-main/third_party/mobile_use/controllers/unified_controller.py +183 -0
  469. package/artemis-main/third_party/mobile_use/graph.py +54 -0
  470. package/artemis-main/third_party/mobile_use/llm_service.py +193 -0
  471. package/artemis-main/third_party/mobile_use/main.py +130 -0
  472. package/artemis-main/third_party/mobile_use/sdk/__init__.py +13 -0
  473. package/artemis-main/third_party/mobile_use/sdk/agent.py +1060 -0
  474. package/artemis-main/third_party/mobile_use/sdk/builders/__init__.py +13 -0
  475. package/artemis-main/third_party/mobile_use/sdk/builders/agent_config_builder.py +225 -0
  476. package/artemis-main/third_party/mobile_use/sdk/builders/index.py +33 -0
  477. package/artemis-main/third_party/mobile_use/sdk/builders/task_request_builder.py +243 -0
  478. package/artemis-main/third_party/mobile_use/sdk/types/__init__.py +13 -0
  479. package/artemis-main/third_party/mobile_use/sdk/types/agent.py +98 -0
  480. package/artemis-main/third_party/mobile_use/sdk/types/exceptions.py +109 -0
  481. package/artemis-main/third_party/mobile_use/sdk/types/task.py +234 -0
  482. package/artemis-main/third_party/mobile_use/testing.py +59 -0
  483. package/artemis-main/third_party/mobile_use/tools/__init__.py +13 -0
  484. package/artemis-main/third_party/mobile_use/tools/index.py +44 -0
  485. package/artemis-main/third_party/mobile_use/tools/mobile/__init__.py +13 -0
  486. package/artemis-main/third_party/mobile_use/tools/mobile/launch_app.py +89 -0
  487. package/artemis-main/third_party/mobile_use/tools/tool_wrapper.py +52 -0
  488. package/artemis-main/third_party/mobile_use/utils/__init__.py +13 -0
  489. package/artemis-main/third_party/mobile_use/utils/app_launch_utils.py +217 -0
  490. package/artemis-main/third_party/mobile_use/utils/cli_helpers.py +60 -0
  491. package/artemis-main/third_party/mobile_use/utils/decorators.py +158 -0
  492. package/artemis-main/third_party/mobile_use/utils/file.py +31 -0
  493. package/artemis-main/third_party/mobile_use/utils/logger.py +216 -0
  494. package/artemis-main/third_party/mobile_use/utils/media.py +213 -0
  495. package/artemis-main/third_party/mobile_use/utils/shell_utils.py +39 -0
  496. package/artemis-main/third_party/mobile_use/utils/video.py +398 -0
  497. package/artemis-main/uv.lock +4908 -0
  498. package/bin/android-midscene-automation.js +1 -5
  499. package/bin/artemis-config.mjs +15 -0
  500. package/bin/artemis-embedded.py +56 -0
  501. package/bin/start-artemis.mjs +30 -0
  502. package/bin/start-dev.mjs +112 -0
  503. package/bin/test_artemis_embedded.py +44 -0
  504. package/package.json +4 -2
  505. package/server/artemis.ts +34 -0
  506. package/server/http-api.ts +12 -0
  507. package/src/App.vue +10 -5
  508. package/src/api.ts +5 -0
  509. package/src/appium-recorder/AppiumPage.vue +1 -1
  510. package/src/appium-recorder/components/ComponentInspector.vue +1 -1
  511. package/src/appium-recorder/components/FlowNodeSearch.vue +3 -3
  512. package/src/appium-recorder/components/ImageCheckDialog.vue +3 -3
  513. package/src/appium-recorder/components/RecorderWorkspace.vue +4 -4
  514. package/src/appium-recorder/flow-appearance.ts +4 -4
  515. package/src/components/AppSidebar.vue +8 -7
  516. package/src/components/MidsceneMaintenanceNotice.vue +17 -0
  517. package/src/components/analytics/AnalysisCharts.vue +4 -4
  518. package/src/components/analytics/ReportAnalysis.vue +1 -1
  519. package/src/components/generator/TestCaseFileUpload.vue +1 -1
  520. package/src/components/help/HelpCenter.vue +56 -37
  521. package/src/pages/AiGeneratorPage.vue +1 -1
  522. package/src/pages/ArtemisPage.vue +72 -0
  523. package/src/pages/ConfigPage.vue +18 -19
  524. package/src/style.css +340 -121
  525. package/src/types.ts +1 -1
@@ -0,0 +1,427 @@
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
+ """Integration-host probe: the process environment an IDE / MCP client launched us in.
16
+
17
+ The standard readiness probes answer "can Artemis drive a phone on this
18
+ machine?". This probe answers the question that precedes it when Artemis is
19
+ embedded in an AI coding assistant (Claude Code, Cursor, Codex, Antigravity,
20
+ ...): "was the server launched the way the installer expects, and can it
21
+ spawn a background runner?". The failure modes it covers are the ones users
22
+ hit mostly through MCP, rarely from the CLI or the web console:
23
+
24
+ * the MCP client started the server with a different interpreter than the
25
+ project virtualenv, so the spawned runner imports a different package set;
26
+ * the ``.env`` file the settings loader reads is not where the user put the key;
27
+ * the traces directory is not writable from the client's sandbox;
28
+ * the daemon port is held by another process (a very common collision with
29
+ dev servers that also default to port 8000).
30
+
31
+ The probe is not registered in the default :class:`ReadinessEngine` probe set:
32
+ the web console has its own device wizard and would only render noise. The
33
+ ``mobile_diagnose`` MCP tool and ``artemis doctor`` run it explicitly.
34
+
35
+ Only the unwritable-traces case blocks task execution; the result carries
36
+ ``is_blocker`` accordingly, so the WARN findings degrade the verdict without
37
+ blocking it.
38
+ """
39
+
40
+ from __future__ import annotations
41
+
42
+ import os
43
+ from pathlib import Path
44
+ import sys
45
+ import tempfile
46
+ from typing import Any
47
+
48
+ from artemis.core.diagnostics.probes.base import BaseProbe
49
+ from artemis.core.diagnostics.schema import (
50
+ ProbeAction,
51
+ ProbeCategory,
52
+ ProbeResult,
53
+ ProbeStatus,
54
+ )
55
+
56
+
57
+ def _same_file(a: str | os.PathLike[str], b: str | os.PathLike[str]) -> bool:
58
+ """Compare two interpreter paths tolerantly (symlinks, case on Windows)."""
59
+ try:
60
+ return os.path.samefile(a, b)
61
+ except OSError:
62
+ return os.path.normcase(os.path.abspath(a)) == os.path.normcase(os.path.abspath(b))
63
+
64
+
65
+ #: Client names accepted by ``artemis mcp --install`` (see interfaces/cli/commands/mcp.py).
66
+ MCP_CLIENT_NAMES: tuple[str, ...] = (
67
+ "antigravity",
68
+ "claude",
69
+ "cursor",
70
+ "windsurf",
71
+ "vscode",
72
+ "cline",
73
+ "roo",
74
+ "openclaw",
75
+ "codex",
76
+ )
77
+
78
+
79
+ def _env_has_prefix(prefix: str, *, exclude: tuple[str, ...] = ()) -> bool:
80
+ return any(key.startswith(prefix) and key not in exclude for key in os.environ)
81
+
82
+
83
+ def detect_mcp_client() -> str | None:
84
+ """Best-effort guess of which MCP client launched this process, from ``os.environ``.
85
+
86
+ Returns one of :data:`MCP_CLIENT_NAMES` or ``None`` when unsure. MCP hosts
87
+ spawn the server with their own process environment, so the variables the
88
+ IDE exports to its child processes leak through. Specific hosts are checked
89
+ before the generic VS Code markers because Cursor, Windsurf, Cline and Roo
90
+ are (or run inside) VS Code and inherit ``VSCODE_*`` / ``TERM_PROGRAM``.
91
+ """
92
+ # Antigravity injects its language-server endpoint and CSRF token
93
+ # (ANTIGRAVITY_LS_ADDRESS / ANTIGRAVITY_CSRF_TOKEN) so child processes can
94
+ # call `agentapi`; mcp_server/notifiers/agentapi.py relies on the same pair.
95
+ if _env_has_prefix("ANTIGRAVITY_"):
96
+ return "antigravity"
97
+ # Claude Code marks every child with CLAUDECODE=1 plus CLAUDE_CODE_* details
98
+ # (entrypoint, session id, ...). Claude Desktop shares the same installer
99
+ # target, so both map to "claude".
100
+ if os.environ.get("CLAUDECODE") == "1" or _env_has_prefix("CLAUDE_CODE_"):
101
+ return "claude"
102
+ # Cursor tags its integrated terminal and spawned processes with
103
+ # CURSOR_TRACE_ID (and CURSOR_AGENT in agent mode).
104
+ if "CURSOR_TRACE_ID" in os.environ or _env_has_prefix("CURSOR_"):
105
+ return "cursor"
106
+ # Windsurf (Codeium) exports WINDSURF_* to its child processes.
107
+ if _env_has_prefix("WINDSURF_"):
108
+ return "windsurf"
109
+ # Codex CLI sets CODEX_SANDBOX* / CODEX_* for the processes it runs.
110
+ # CODEX_HOME alone is excluded: users set it by hand to relocate the Codex
111
+ # config directory, so on its own it does not prove Codex launched us.
112
+ if _env_has_prefix("CODEX_", exclude=("CODEX_HOME",)):
113
+ return "codex"
114
+ # OPENCLAW_WEBHOOK_URL is a notifier setting users put in any host's env
115
+ # block (see mcp_server/notifiers/webhook.py); only other OPENCLAW_* vars
116
+ # indicate the OpenClaw runtime itself.
117
+ if _env_has_prefix("OPENCLAW_", exclude=("OPENCLAW_WEBHOOK_URL",)):
118
+ return "openclaw"
119
+ # Cline / Roo Code run as VS Code extensions; their own prefixes are rare
120
+ # but unambiguous when present, and must win over the generic VS Code check.
121
+ if _env_has_prefix("CLINE_"):
122
+ return "cline"
123
+ if _env_has_prefix("ROO_"):
124
+ return "roo"
125
+ # Plain VS Code (Copilot agent mode) exports VSCODE_PID / VSCODE_IPC_HOOK
126
+ # and sets TERM_PROGRAM=vscode. Weakest signal, so it is checked last.
127
+ if (
128
+ "VSCODE_PID" in os.environ
129
+ or "VSCODE_IPC_HOOK" in os.environ
130
+ or os.environ.get("TERM_PROGRAM") == "vscode"
131
+ ):
132
+ return "vscode"
133
+ return None
134
+
135
+
136
+ def mcp_install_actions(client: str | None) -> list[ProbeAction]:
137
+ """Actions that regenerate the MCP client config for ``client`` (or ask which one)."""
138
+ if client:
139
+ return [
140
+ ProbeAction(
141
+ action_type="command",
142
+ label="Regenerate MCP Config",
143
+ payload=f"uv run artemis mcp --install {client}",
144
+ )
145
+ ]
146
+ return [
147
+ ProbeAction(
148
+ action_type="hint",
149
+ label="Regenerate MCP Config",
150
+ payload=(
151
+ "Could not tell which MCP client launched this server. Ask the user which IDE"
152
+ " they are using, then run `uv run artemis mcp --install <client>` with"
153
+ " <client> being one of: " + ", ".join(MCP_CLIENT_NAMES) + "."
154
+ ),
155
+ )
156
+ ]
157
+
158
+
159
+ def project_venv_python(project_root: Path) -> Path:
160
+ """Return the interpreter path the installer writes into MCP client configs."""
161
+ if sys.platform == "win32":
162
+ return project_root / ".venv" / "Scripts" / "python.exe"
163
+ return project_root / ".venv" / "bin" / "python"
164
+
165
+
166
+ def directory_is_writable(path: Path) -> tuple[bool, str | None]:
167
+ """Try to create and delete a scratch file; return (ok, error)."""
168
+ try:
169
+ path.mkdir(parents=True, exist_ok=True)
170
+ with tempfile.NamedTemporaryFile(dir=path, prefix=".artemis-diag-", delete=True):
171
+ pass
172
+ return True, None
173
+ except OSError as exc:
174
+ return False, str(exc)
175
+
176
+
177
+ def intended_traces_path() -> Path:
178
+ """Return the traces directory the settings loader *would* pick, without creating it.
179
+
180
+ Mirrors the selection order of :func:`artemis.config.paths.get_default_traces_path`
181
+ (``ARTEMIS_TRACES_DIR`` env, else the user app dir when the install mode
182
+ keeps mutable state there, else ``ROOT_DIR/traces``) minus its ``mkdir``.
183
+ Used only when that function raised, so the probe can still name the
184
+ offending directory in its FAIL result instead of crashing.
185
+ """
186
+ from artemis.config import paths as artemis_paths
187
+
188
+ env_traces = os.getenv(artemis_paths.ENV_ARTEMIS_TRACES_DIR)
189
+ if env_traces:
190
+ return Path(env_traces)
191
+ if artemis_paths._use_user_app_dir():
192
+ return artemis_paths.get_app_dir() / "traces"
193
+ return Path(artemis_paths.ROOT_DIR) / "traces"
194
+
195
+
196
+ def resolve_traces_dir() -> tuple[Path, str | None]:
197
+ """Return ``(traces_dir, error)``; ``error`` is set when the directory could not be created.
198
+
199
+ ``get_default_traces_path`` creates the directory as a side effect, so on a
200
+ read-only parent it raises before :func:`directory_is_writable` ever runs.
201
+ Surface that as data rather than letting the probe itself crash.
202
+ """
203
+ from artemis.config.paths import get_default_traces_path
204
+
205
+ try:
206
+ return Path(get_default_traces_path()), None
207
+ except OSError as exc:
208
+ return intended_traces_path(), str(exc)
209
+
210
+
211
+ class IntegrationHostProbe(BaseProbe):
212
+ """Probe verifying the MCP / IDE host process can actually run Artemis tasks."""
213
+
214
+ @property
215
+ def probe_id(self) -> str:
216
+ return "integration_host"
217
+
218
+ @property
219
+ def category(self) -> ProbeCategory:
220
+ return ProbeCategory.RUNTIME
221
+
222
+ @property
223
+ def is_blocker(self) -> bool:
224
+ return True
225
+
226
+ async def probe(self) -> ProbeResult:
227
+ # Lazy imports keep this module importable in stripped test environments
228
+ # and avoid a core -> runtime import at package import time.
229
+ from artemis.config.paths import ROOT_DIR, get_env_file, is_source_checkout
230
+ from artemis.runtime.daemon_client import (
231
+ DEFAULT_DAEMON_HOST,
232
+ DEFAULT_DAEMON_PORT,
233
+ daemon_log_path,
234
+ is_artemis_daemon,
235
+ is_standalone_forced,
236
+ )
237
+ from artemis.runtime.server_lifecycle import is_port_in_use
238
+
239
+ project_root = Path(ROOT_DIR)
240
+ source_checkout = is_source_checkout()
241
+ server_python = Path(sys.executable)
242
+ # A project virtualenv is only meaningful for a source checkout. In a
243
+ # wheel install ROOT_DIR is site-packages, which never carries a .venv;
244
+ # the current interpreter also runs background tasks.
245
+ venv_python: Path | None = project_venv_python(project_root) if source_checkout else None
246
+ venv_exists = venv_python is not None and venv_python.is_file()
247
+ runner_python = venv_python if venv_exists else server_python
248
+ interpreter_matches_venv = venv_exists and _same_file(server_python, venv_python)
249
+ mcp_client = detect_mcp_client()
250
+
251
+ env_file = Path(get_env_file())
252
+ traces_dir, traces_error = resolve_traces_dir()
253
+ if traces_error is None:
254
+ traces_writable, traces_error = directory_is_writable(traces_dir)
255
+ else:
256
+ traces_writable = False
257
+
258
+ standalone = is_standalone_forced()
259
+ # Identity, not liveness: a dev server squatting on the port answers
260
+ # HTTP too, and must show up as a hijacked port, not as the daemon.
261
+ daemon_reachable = False if standalone else is_artemis_daemon()
262
+ port_in_use = is_port_in_use(DEFAULT_DAEMON_PORT, DEFAULT_DAEMON_HOST)
263
+ port_hijacked = port_in_use and not daemon_reachable and not standalone
264
+
265
+ metadata: dict[str, Any] = {
266
+ "os": sys.platform,
267
+ "mcp_client": mcp_client,
268
+ "project_root": str(project_root),
269
+ "is_source_checkout": source_checkout,
270
+ "server_python": str(server_python),
271
+ "venv_python": str(venv_python) if venv_python is not None else None,
272
+ "venv_exists": venv_exists,
273
+ "interpreter_matches_venv": interpreter_matches_venv,
274
+ "runner_python": str(runner_python),
275
+ "env_file": str(env_file),
276
+ "env_file_exists": env_file.is_file(),
277
+ "traces_dir": str(traces_dir),
278
+ "traces_dir_writable": traces_writable,
279
+ "traces_dir_error": traces_error,
280
+ "daemon": {
281
+ "host": DEFAULT_DAEMON_HOST,
282
+ "port": DEFAULT_DAEMON_PORT,
283
+ "standalone_forced": standalone,
284
+ "reachable": daemon_reachable,
285
+ "port_in_use": port_in_use,
286
+ "port_held_by_other_process": port_hijacked,
287
+ "log_path": str(daemon_log_path()),
288
+ },
289
+ }
290
+
291
+ problems: list[str] = []
292
+ actions: list[ProbeAction] = []
293
+ status = ProbeStatus.PASS
294
+
295
+ if not traces_writable:
296
+ status = ProbeStatus.FAIL
297
+ problems.append(f"traces directory {traces_dir} is not writable ({traces_error})")
298
+ actions.append(
299
+ ProbeAction(
300
+ action_type="hint",
301
+ label="Fix Traces Directory",
302
+ payload=(
303
+ f"Make {traces_dir} writable for the user running the MCP server, or point "
304
+ "ARTEMIS_TRACES_DIR at a writable directory in the MCP server's env block, "
305
+ "then restart the MCP server."
306
+ ),
307
+ )
308
+ )
309
+
310
+ if port_hijacked:
311
+ status = ProbeStatus.WARN if status is ProbeStatus.PASS else status
312
+ problems.append(
313
+ f"port {DEFAULT_DAEMON_PORT} is held by a process that is not the Artemis daemon"
314
+ )
315
+ actions.append(
316
+ ProbeAction(
317
+ action_type="hint",
318
+ label="Free the Daemon Port",
319
+ payload=(
320
+ f"Another program is listening on {DEFAULT_DAEMON_HOST}:{DEFAULT_DAEMON_PORT}"
321
+ " (often a dev server). Stop it, or set ARTEMIS_DAEMON_PORT to a free port in"
322
+ " the MCP server's env block and restart the MCP server. Tasks still run in"
323
+ " standalone mode meanwhile, without the shared scheduler."
324
+ ),
325
+ )
326
+ )
327
+
328
+ if not source_checkout:
329
+ # Wheel / frozen install: no virtualenv expectation, nothing to warn about.
330
+ pass
331
+ elif venv_exists and not interpreter_matches_venv:
332
+ status = ProbeStatus.WARN if status is ProbeStatus.PASS else status
333
+ problems.append(
334
+ f"MCP server runs on {server_python}, not the project virtualenv {venv_python}"
335
+ )
336
+ actions.extend(mcp_install_actions(mcp_client))
337
+ actions.append(
338
+ ProbeAction(
339
+ action_type="hint",
340
+ label="Interpreter Mismatch",
341
+ payload=(
342
+ "The MCP client config should point `command` at the .venv interpreter so the"
343
+ " server and the background runner share one package set. Restart the MCP"
344
+ " server after changing the config."
345
+ ),
346
+ )
347
+ )
348
+ elif not venv_exists:
349
+ status = ProbeStatus.WARN if status is ProbeStatus.PASS else status
350
+ problems.append(
351
+ "no project virtualenv (.venv) found; runner falls back to the server interpreter"
352
+ )
353
+ actions.append(
354
+ ProbeAction(
355
+ action_type="command",
356
+ label="Create Virtualenv",
357
+ payload="uv sync",
358
+ )
359
+ )
360
+
361
+ if not env_file.is_file():
362
+ actions.append(
363
+ ProbeAction(
364
+ action_type="hint",
365
+ label="Env File Location",
366
+ payload=(
367
+ f"No .env file at {env_file}. API keys are read from that file or from the MCP"
368
+ " server's env block; the file is created by `artemis init`."
369
+ ),
370
+ )
371
+ )
372
+
373
+ if not daemon_reachable and not port_hijacked:
374
+ actions.append(
375
+ ProbeAction(
376
+ action_type="hint",
377
+ label="Daemon Auto-Start",
378
+ payload=(
379
+ "Standalone mode is forced (ARTEMIS_STANDALONE=1); tasks run as detached"
380
+ " runner processes."
381
+ if standalone
382
+ else "The Artemis daemon is not running; mobile_run_task starts it on the"
383
+ f" first task. Its log is {daemon_log_path()}."
384
+ ),
385
+ )
386
+ )
387
+
388
+ if status is ProbeStatus.PASS:
389
+ summary = "Host Ready"
390
+ description = (
391
+ f"MCP server runs on {server_python}; runner uses {runner_python}. "
392
+ f"Traces at {traces_dir}. "
393
+ + (
394
+ "Daemon reachable."
395
+ if daemon_reachable
396
+ else "Daemon will auto-start on the first task."
397
+ )
398
+ )
399
+ actions.insert(
400
+ 0,
401
+ ProbeAction(
402
+ action_type="hint",
403
+ label="Host OK",
404
+ payload="Interpreter, traces directory and daemon port look consistent.",
405
+ ),
406
+ )
407
+ else:
408
+ summary = "Host Misconfigured" if status is ProbeStatus.FAIL else "Host Warning"
409
+ joined = "; ".join(problems)
410
+ description = joined[:1].upper() + joined[1:] + "."
411
+
412
+ # Only an unwritable traces directory stops tasks from running. The
413
+ # WARN findings (interpreter mismatch, missing venv, squatted port) are
414
+ # degradations the runner works around, so they must not turn the
415
+ # verdict into "blocked" — a pip/conda checkout without .venv would
416
+ # otherwise never leave that state.
417
+ return ProbeResult(
418
+ id=self.probe_id,
419
+ category=self.category,
420
+ title="MCP / IDE Integration Host",
421
+ status=status,
422
+ is_blocker=status is not ProbeStatus.WARN,
423
+ summary=summary,
424
+ description=description,
425
+ metadata=metadata,
426
+ actions=actions,
427
+ )
@@ -0,0 +1,169 @@
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
+ """Python Runtime and System Configuration Diagnostic Probes."""
16
+
17
+ import platform
18
+ import sys
19
+
20
+ from artemis.config import parse_llm_config
21
+ from artemis.core.diagnostics.probes.base import BaseProbe
22
+ from artemis.core.diagnostics.schema import (
23
+ ProbeAction,
24
+ ProbeCategory,
25
+ ProbeResult,
26
+ ProbeStatus,
27
+ )
28
+
29
+
30
+ class PythonRuntimeProbe(BaseProbe):
31
+ """Probe inspecting Python runtime environment, version, and architecture."""
32
+
33
+ @property
34
+ def probe_id(self) -> str:
35
+ return "python_runtime"
36
+
37
+ @property
38
+ def category(self) -> ProbeCategory:
39
+ return ProbeCategory.RUNTIME
40
+
41
+ @property
42
+ def is_blocker(self) -> bool:
43
+ return True
44
+
45
+ async def probe(self) -> ProbeResult:
46
+ py_ver_tuple = sys.version_info
47
+ py_ver_str = f"{py_ver_tuple.major}.{py_ver_tuple.minor}.{py_ver_tuple.micro}"
48
+ is_supported = py_ver_tuple >= (3, 12)
49
+ arch = platform.machine()
50
+ executable = sys.executable
51
+
52
+ metadata = {
53
+ "version": py_ver_str,
54
+ "major": py_ver_tuple.major,
55
+ "minor": py_ver_tuple.minor,
56
+ "micro": py_ver_tuple.micro,
57
+ "executable": executable,
58
+ "architecture": arch,
59
+ "platform": sys.platform,
60
+ "is_supported": is_supported,
61
+ }
62
+
63
+ if is_supported:
64
+ return ProbeResult(
65
+ id=self.probe_id,
66
+ category=self.category,
67
+ title="Python Runtime Environment",
68
+ status=ProbeStatus.PASS,
69
+ is_blocker=self.is_blocker,
70
+ summary=f"Python {py_ver_str} Ready",
71
+ description=f"Python {py_ver_str} ({arch}, 64-bit) detected at {executable}. Satisfies requirement (>= 3.12).",
72
+ metadata=metadata,
73
+ actions=[
74
+ ProbeAction(
75
+ action_type="hint",
76
+ label="Python Runtime OK",
77
+ payload=f"Python {py_ver_str} ({arch})",
78
+ )
79
+ ],
80
+ )
81
+ else:
82
+ return ProbeResult(
83
+ id=self.probe_id,
84
+ category=self.category,
85
+ title="Python Runtime Environment",
86
+ status=ProbeStatus.FAIL,
87
+ is_blocker=self.is_blocker,
88
+ summary=f"Python {py_ver_str} Unsupported",
89
+ description=f"Python {py_ver_str} detected. Artemis requires Python >= 3.12.",
90
+ metadata=metadata,
91
+ actions=[
92
+ ProbeAction(
93
+ action_type="link",
94
+ label="Download Python 3.12+",
95
+ payload="https://www.python.org/downloads/",
96
+ ),
97
+ ProbeAction(
98
+ action_type="command",
99
+ label="Install with uv",
100
+ payload="uv python install 3.12",
101
+ ),
102
+ ],
103
+ )
104
+
105
+
106
+ class SystemConfigProbe(BaseProbe):
107
+ """Probe verifying config/artemis.jsonc and settings integrity."""
108
+
109
+ @property
110
+ def probe_id(self) -> str:
111
+ return "system_config"
112
+
113
+ @property
114
+ def category(self) -> ProbeCategory:
115
+ return ProbeCategory.RUNTIME
116
+
117
+ @property
118
+ def is_blocker(self) -> bool:
119
+ return True
120
+
121
+ async def probe(self) -> ProbeResult:
122
+ try:
123
+ cfg = parse_llm_config()
124
+ planner_model = getattr(getattr(cfg, "planner", None), "model", "Gemini 2.5 Flash")
125
+ metadata = {
126
+ "valid": True,
127
+ "config_file": "config/artemis.jsonc",
128
+ "planner_model": planner_model,
129
+ }
130
+ return ProbeResult(
131
+ id=self.probe_id,
132
+ category=self.category,
133
+ title="Artemis System Configuration",
134
+ status=ProbeStatus.PASS,
135
+ is_blocker=self.is_blocker,
136
+ summary="Config Valid",
137
+ description=f"System configuration loaded and validated (Planner model: {planner_model}).",
138
+ metadata=metadata,
139
+ actions=[
140
+ ProbeAction(
141
+ action_type="hint",
142
+ label="Config Healthy",
143
+ payload="config/artemis.jsonc is loaded and valid.",
144
+ )
145
+ ],
146
+ )
147
+ except Exception as e:
148
+ metadata = {
149
+ "valid": False,
150
+ "config_file": "config/artemis.jsonc",
151
+ "error": str(e),
152
+ }
153
+ return ProbeResult(
154
+ id=self.probe_id,
155
+ category=self.category,
156
+ title="Artemis System Configuration",
157
+ status=ProbeStatus.FAIL,
158
+ is_blocker=self.is_blocker,
159
+ summary="Config Error",
160
+ description=f"Failed to parse config/artemis.jsonc: {e}",
161
+ metadata=metadata,
162
+ actions=[
163
+ ProbeAction(
164
+ action_type="command",
165
+ label="Run Artemis Init",
166
+ payload="artemis init",
167
+ )
168
+ ],
169
+ )