android-midscene-automation 0.2.3 → 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 +11 -1
  2. package/README.md +63 -1
  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 +10 -8
  505. package/server/artemis.ts +34 -0
  506. package/server/http-api.ts +13 -1
  507. package/src/App.vue +72 -23
  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 +369 -186
  525. package/src/types.ts +1 -1
@@ -0,0 +1,226 @@
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
+ """Per-agent policies for rendering compiled history.
16
+
17
+ Each policy sets the visibility options for
18
+ :func:`artemis.utils.task_tree.build_plan_and_history`. When transcript mode
19
+ is enabled, compressed steps are rendered as chunk blocks.
20
+
21
+ Chunk views:
22
+
23
+ - ``"full"`` (outputter / history_analyzer): the chunk's stored three-band
24
+ rendering including the ③ full-width per-step ledger;
25
+ - ``"digest"`` (everything else): bands ① + ② with the ③ ledger replaced by
26
+ a ``ledger via search_history`` marker line.
27
+
28
+ Because chunks only exist when ``agent.memory.transcript.enabled`` is on, the
29
+ flag-off output of every agent stays byte-for-byte unchanged.
30
+
31
+ ``agent.memory.policies`` allows per-agent field overrides, e.g.::
32
+
33
+ "memory": {"policies": {"planner": {"last_n_detailed": 3}}}
34
+ """
35
+
36
+ from dataclasses import dataclass, fields, replace
37
+ from typing import Any
38
+
39
+ from third_party.mobile_use.utils.logger import get_logger
40
+
41
+ logger = get_logger(__name__)
42
+
43
+ #: Marker appended to a digest-view chunk block in place of the ③ ledger.
44
+ DIGEST_LEDGER_MARKER_TEMPLATE = (
45
+ "③ Step action ledger: available via search_history (Steps {start}–{end})"
46
+ )
47
+
48
+ #: The band-③ heading inside a chunk's stored ``rendered_text``.
49
+ _BAND3_HEADING = "③ Step action ledger"
50
+
51
+
52
+ @dataclass(frozen=True)
53
+ class ContextPolicy:
54
+ """One agent's compiled-history rendering declaration.
55
+
56
+ Field semantics map 1:1 onto ``build_plan_and_history`` kwargs, with two
57
+ conveniences: ``uncompressed=True`` renders every step (the historical
58
+ ``min_summaries=len(steps)`` idiom) and ``whitelist=True`` marks policies
59
+ whose caller supplies ``keep_subgoal_hashes`` at runtime.
60
+ """
61
+
62
+ strategy: str = "sliding_window" # documentation only
63
+ strict_milestone_pruning: bool = False
64
+ recent_window_size: int = 3
65
+ min_summaries: int = 5
66
+ uncompressed: bool = False
67
+ last_n_detailed: int = 1
68
+ all_detailed: bool = False
69
+ whitelist: bool = False
70
+ chunk_view: str | None = "digest" # "full" | "digest" | None (never chunks)
71
+
72
+
73
+ #: Default compiled-history settings for each agent.
74
+ CONTEXT_POLICIES: dict[str, ContextPolicy] = {
75
+ # operator.py:996 — last_n_detailed comes from the node constructor.
76
+ "operator": ContextPolicy(
77
+ strategy="strict_milestone",
78
+ strict_milestone_pruning=True,
79
+ recent_window_size=3,
80
+ ),
81
+ # operator.py:341 — transcript F-region cold-start restore.
82
+ "operator_cold_start": ContextPolicy(
83
+ strategy="full",
84
+ uncompressed=True,
85
+ last_n_detailed=1,
86
+ chunk_view=None,
87
+ ),
88
+ # planner.py:133 — caller passes an empty plan and hash "default".
89
+ "planner": ContextPolicy(
90
+ strategy="strict_milestone",
91
+ strict_milestone_pruning=True,
92
+ recent_window_size=5,
93
+ last_n_detailed=2,
94
+ ),
95
+ # outputter.py:81
96
+ "outputter": ContextPolicy(
97
+ strategy="full",
98
+ uncompressed=True,
99
+ last_n_detailed=0,
100
+ chunk_view="full",
101
+ ),
102
+ # history_analyzer.py:113
103
+ "history_analyzer": ContextPolicy(
104
+ strategy="full",
105
+ uncompressed=True,
106
+ last_n_detailed=1,
107
+ chunk_view="full",
108
+ ),
109
+ # diagnoser.py:166 — every non-whitelist kwarg is the historical default.
110
+ "diagnoser": ContextPolicy(strategy="milestone_whitelist", whitelist=True),
111
+ # committee_tool.py:189
112
+ "committee": ContextPolicy(
113
+ strategy="milestone_whitelist",
114
+ whitelist=True,
115
+ last_n_detailed=3,
116
+ ),
117
+ }
118
+
119
+ _POLICY_FIELDS = {f.name for f in fields(ContextPolicy)}
120
+
121
+
122
+ def resolve_policy(agent: str) -> ContextPolicy:
123
+ """The agent's policy with any ``agent.memory.policies`` overrides applied."""
124
+ try:
125
+ policy = CONTEXT_POLICIES[agent]
126
+ except KeyError:
127
+ raise KeyError(
128
+ f"No ContextPolicy declared for agent '{agent}';"
129
+ f" known agents: {sorted(CONTEXT_POLICIES)}"
130
+ ) from None
131
+
132
+ overrides: dict[str, Any] = {}
133
+ try:
134
+ from artemis.config import load_agent_config
135
+
136
+ configured = (load_agent_config().memory.policies or {}).get(agent) or {}
137
+ overrides = {k: v for k, v in configured.items() if k in _POLICY_FIELDS}
138
+ unknown = set(configured) - _POLICY_FIELDS
139
+ if unknown:
140
+ logger.warning(
141
+ f"agent.memory.policies['{agent}'] ignores unknown fields: {sorted(unknown)}"
142
+ )
143
+ except Exception:
144
+ overrides = {}
145
+ return replace(policy, **overrides) if overrides else policy
146
+
147
+
148
+ def _chunk_digest_text(rendered_text: str, start: int, end: int) -> str:
149
+ """Bands ① + ② of a stored chunk rendering, ③ replaced by the recall marker."""
150
+ marker = DIGEST_LEDGER_MARKER_TEMPLATE.format(start=start, end=end)
151
+ head, sep, _ = rendered_text.partition(f"{_BAND3_HEADING}\n")
152
+ if not sep:
153
+ return f"{rendered_text.rstrip()}\n{marker}"
154
+ return f"{head.rstrip()}\n\n{marker}"
155
+
156
+
157
+ def load_chunk_blocks(engine: Any, view: str | None) -> list[dict] | None:
158
+ """The persisted history chunks rendered for the compiled view, or None.
159
+
160
+ Returns None (→ byte-identical legacy rendering) unless the transcript
161
+ flag is on, the engine exposes chunks, and at least one chunk exists.
162
+ """
163
+ if engine is None or view not in ("full", "digest"):
164
+ return None
165
+ try:
166
+ from artemis.config import load_agent_config
167
+
168
+ if not load_agent_config().memory.transcript.enabled:
169
+ return None
170
+ except Exception:
171
+ return None
172
+ try:
173
+ rows = engine.get_history_chunks() or []
174
+ except Exception as e:
175
+ logger.debug(f"load_chunk_blocks: no chunks available: {e}")
176
+ return None
177
+
178
+ blocks: list[dict] = []
179
+ for row in rows:
180
+ start = getattr(row, "start_step_number", None)
181
+ end = getattr(row, "end_step_number", None)
182
+ rendered = getattr(row, "rendered_text", None)
183
+ if not isinstance(start, int) or not isinstance(end, int) or not rendered:
184
+ continue
185
+ text = rendered if view == "full" else _chunk_digest_text(rendered, start, end)
186
+ blocks.append({"start_step_number": start, "end_step_number": end, "text": text})
187
+ return blocks or None
188
+
189
+
190
+ def build_history_for(
191
+ agent: str,
192
+ task_plan: str,
193
+ steps: list,
194
+ current_subgoal_hash: str,
195
+ *,
196
+ keep_subgoal_hashes: set | None = None,
197
+ last_n_detailed: int | None = None,
198
+ engine: Any = None,
199
+ ) -> str:
200
+ """Render the agent's compiled plan-and-history view per its policy.
201
+
202
+ Args:
203
+ agent: Key into :data:`CONTEXT_POLICIES`.
204
+ keep_subgoal_hashes: Runtime whitelist (whitelist policies only).
205
+ last_n_detailed: Runtime override (the operator's constructor knob).
206
+ engine: DataEngine handing chunks to the compiled view; omit at call
207
+ sites that must never receive chunk blocks.
208
+ """
209
+ from artemis.utils.task_tree import build_plan_and_history
210
+
211
+ policy = resolve_policy(agent)
212
+ kwargs: dict[str, Any] = {
213
+ "min_summaries": len(steps) if policy.uncompressed else policy.min_summaries,
214
+ "last_n_detailed": (policy.last_n_detailed if last_n_detailed is None else last_n_detailed),
215
+ "all_detailed": policy.all_detailed,
216
+ "strict_milestone_pruning": policy.strict_milestone_pruning,
217
+ "recent_window_size": policy.recent_window_size,
218
+ }
219
+ if policy.whitelist:
220
+ kwargs["keep_subgoal_hashes"] = keep_subgoal_hashes
221
+
222
+ chunks = load_chunk_blocks(engine, policy.chunk_view)
223
+ if chunks:
224
+ kwargs["chunks"] = chunks
225
+
226
+ return build_plan_and_history(task_plan, steps, current_subgoal_hash, **kwargs)
@@ -0,0 +1,73 @@
1
+ # ROLE
2
+ You are the Segment Capsule writer for a mobile automation agent's history compression. You receive one contiguous segment of executed steps and produce the two LLM-authored bands of the segment's history chunk:
3
+
4
+ - Band ① — Synopsis & effects: what this segment was doing, what was actually done, and what effects/artifacts it left behind, plus structured fields.
5
+ - Band ② — Compressed step summary: an interval narrative ("Steps xx–xx did ..., Step xx did ...") covering every step of the segment.
6
+
7
+ The mechanical per-step action ledger (band ③) is assembled outside of you; do NOT reproduce it.
8
+
9
+ # INPUT SHAPE
10
+ The segment arrives turn by turn, in order. Each turn block has:
11
+ - `Recorded steps:` — the mechanical facts of the step(s) executed in that turn: step number, session offset, the exact action, the controller/validator result, the visual transition summary, the notes written (tool → note key: gist), and any user-injected instruction.
12
+ - `Transcript as seen by the operator during this turn:` — the turn exactly as it stood in the operator's own context: the observation text, the operator's reasoning (including `(thinking)` blocks), every `[tool call]` with its arguments, every `[tool result]` returned to the operator (explorer answers, recalled history, note reads, ADB output, screenshot placeholders), and the `--- Action Execution Result ---` message that closes the turn (every turn on the Pro path; only when an action failed on the Flash path). Your capsule REPLACES this transcript in the operator's context — everything the operator learned from it that still matters must survive in your output.
13
+
14
+ A turn block without a transcript falls back to a reasoning excerpt; treat it the same way with less evidence.
15
+
16
+ # NEUTRAL-WORDING CONTRACT (STRICT)
17
+ 1. **Zero subjective validation**: never declare semantic success, completion, or final failure. Subjective verdicts in compressed history cause future turns to falsely believe a goal was definitively met or permanently broken.
18
+ - **BANNED WORDS**: "successfully", "completed", "achieved", "entered", "navigated to", "arrived at", "failed", "unsuccessful", "could not", "impossible".
19
+ - Describe observable transitions and recorded results instead (e.g. "the settings page was displayed", "the controller reported an execution error").
20
+ - Tool results in the transcript may themselves use verdict words; restate them as recorded results ("the explorer reported ...", "the tool returned ...").
21
+ 2. **First-person perspective**: write from the agent's perspective using "I". Never "the agent" / "the operator".
22
+ 3. **Planned iterations are not anomalies**: systematic processing of different candidate items, or periodic polling cycles under a `[Loop]` / `[Loop:continuous]` milestone, is planned progress — record it objectively (e.g. "polling round #2"). Only flag a loop when the exact same target is repeated without progress or states oscillate blindly; then record the pattern and count factually.
23
+ 4. **Preserve, don't abstract**: be deliberately detailed and stay close to the source wording. Prefer longer output over abstracting facts beyond recognition. Faithfully carry visible text, codes, prices, account names, file names, error banners — and the concrete content of tool results the operator relied on (an explorer's answer, a recalled step, a note's contents, a command's output).
24
+ 5. **Fact priority**: user instructions > validator/controller recorded results > tool results returned to the operator (explorer / recall / notes / ADB) > UI/OCR deltas > visual transition summaries > the operator's reasoning.
25
+
26
+ # BAND ① REQUIREMENTS
27
+ Band ① is the segment-level account; band ② below it is the interval-level account. The two bands must not say the same thing twice: ① never re-lists what the intervals of ② already narrate, and it should come out shorter than ② (the `doing` + `did` + `effect` prose together — not counting the structured fields — shorter than the ② intervals together).
28
+
29
+ Answer three questions in prose (field `doing` / `did` / `effect`):
30
+ - `doing`: what this segment was trying to do and why (strategic context within the plan). One or two sentences.
31
+ - `did`: the route the segment actually took, at segment level: the plan-relevant outcome, plus every detour, retry, recovery, pivot, and tool consultation (what I asked, what it returned) when that shaped the next action. Do NOT walk through the steps one by one — ② does that. A segment that went straight through gets one sentence.
32
+ - `effect`: what the segment left behind beyond the screen state already in `exit_state`: persistent state changes, artifacts, and notes. **This MUST include every note left during the segment**: for each notes write in the input, name the target note file and the gist of what was recorded. Every note's target key is machine-checked against your effect field — an omitted note key forces regeneration. If nothing was left behind besides the screen state, say so in a few words.
33
+
34
+ Structured fields (arrays of short strings, may be empty; each entry one short clause, not a sentence):
35
+ - `verified_facts`: states/values actually observed on screen or returned by a tool that later steps may need (name the source: "on screen", "explorer", "recall", "adb"). Values, names, versions, counts, codes, prices — not a description of every screen visited.
36
+ - `unresolved`: open questions, unverified assumptions, missing prerequisites.
37
+ - `failed_paths`: approaches attempted in this segment whose recorded results were errors (state the recorded error factually).
38
+ - `important_entities`: accounts, files, order/tracking numbers, package names, key UI entry points.
39
+ - `entry_state` / `exit_state` (single strings): the observable screen/app state at segment start and end, a short clause each.
40
+
41
+ # BAND ② REQUIREMENTS
42
+ Field `intervals`: an ordered array of `{"start_step": N, "end_step": M, "text": "..."}` objects.
43
+ - Band ② answers only "what were these steps doing, and what did I see as a result". The action itself (tap / swipe / input, its target and its coordinates) is ALREADY carried step by step by the mechanical ledger (band ③) that sits right below your output — do not write it twice.
44
+ - **NEVER include coordinates** — no `[x, y]` pairs, no pixel or normalized positions, no bounds. This is machine-checked; any `[number, number]` in an interval text forces regeneration.
45
+ - **NEVER narrate step by step** ("Step 5: I tapped X. Step 6: I tapped Y."). One interval is a behavior plus the observed outcome, e.g. "Opened the 'Network & internet' section and returned to the main list; the section showed Wi‑Fi, mobile network and VPN rows."
46
+ - **Merge homogeneous consecutive actions into one interval** (repeated scrolls, a sweep through several sections, a polling loop, retries of the same target) — describe the run and its count/result once. A step that does something of a different nature gets its own single-step interval (`start_step == end_step`).
47
+ - Every line's text must reference concrete behavior for those steps (the step numbers come from the `Recorded steps:` lines): the target's visible text, the screen or state observed afterwards, values read, errors shown.
48
+ - **The union of intervals MUST cover the segment's full step range** — no gaps, no overlaps, in ascending order. This is machine-checked; a gap forces regeneration.
49
+ - Keep the neutral-wording contract in every line.
50
+
51
+ # LENGTH TARGET (SOFT)
52
+ Bands ① and ② together should stay within roughly one third of the source text you were given. Reach that by not repeating what band ③ already records (actions, targets, coordinates) and by merging homogeneous runs — NEVER by abstracting away concrete facts (visible text, values read, tool answers, error banners, note contents must all survive verbatim).
53
+
54
+ # OUTPUT FORMAT
55
+ Return ONLY one JSON object (no markdown fences, no commentary):
56
+
57
+ ```json
58
+ {
59
+ "doing": "...",
60
+ "did": "...",
61
+ "effect": "...",
62
+ "entry_state": "...",
63
+ "exit_state": "...",
64
+ "verified_facts": ["..."],
65
+ "unresolved": ["..."],
66
+ "failed_paths": ["..."],
67
+ "important_entities": ["..."],
68
+ "intervals": [
69
+ {"start_step": 18, "end_step": 20, "text": "..."},
70
+ {"start_step": 21, "end_step": 21, "text": "..."}
71
+ ]
72
+ }
73
+ ```
@@ -0,0 +1,281 @@
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 background step-summary runtime (StepMemoryService).
16
+
17
+ Hosts the scheduling skeleton lifted from the Flash VisualStepSummarizer:
18
+ zero-blocking dispatch, bounded retry, bounded flush, canonical step_id keying
19
+ with alias resolution (tool_call_id / legacy ordinals), and status queries.
20
+
21
+ What a summary *contains* (the lens semantics) lives in subclasses — the
22
+ service only guarantees how jobs are scheduled, retried, keyed, and drained.
23
+ Flash and Pro profiles share this runtime.
24
+ """
25
+
26
+ import asyncio
27
+ from typing import Any
28
+
29
+ from artemis.data_engine.trace import detached_trace
30
+ from third_party.mobile_use.utils.logger import get_logger
31
+
32
+ logger = get_logger(__name__)
33
+
34
+ JobKey = int | str
35
+
36
+
37
+ class StepLens:
38
+ """Formal lens interface: what one summary attempt *produces*.
39
+
40
+ A lens turns one job payload into summary text; the surrounding service
41
+ owns scheduling, retries, keying, and draining. Implementations must be
42
+ side-effect-tolerant under retries (an attempt may run again after a
43
+ failure) and return ``None``/raise to signal a failed attempt.
44
+
45
+ Two lens altitudes exist in the redesign (§5): the step-level visual
46
+ transition lens (:class:`~artemis.agents.flash.summarizer.VisualStepSummarizer`
47
+ keeps it inlined as its ``_attempt`` for compatibility) and the chunk-level
48
+ :class:`~artemis.memory.chunking.StepCapsuleLens`.
49
+ """
50
+
51
+ name = "lens"
52
+
53
+ async def render(self, key: JobKey, payload: dict[str, Any]) -> str | None:
54
+ """Produce the summary text for one payload, or None on failure."""
55
+ raise NotImplementedError
56
+
57
+
58
+ class StepMemoryService:
59
+ """Non-blocking background runtime for per-step summary jobs.
60
+
61
+ Key design properties:
62
+ 1. Zero-blocking dispatch: callers submit and proceed immediately.
63
+ 2. Bounded retry: each job attempts at most ``1 + retry_limit`` times and
64
+ then enters an explicit failed state — never an unbounded loop.
65
+ 3. Bounded flush: ``flush()`` waits up to the configured timeout, then
66
+ cancels whatever is still in flight.
67
+ 4. Canonical keying with aliases: jobs are keyed by the DataEngine step id
68
+ where available; tool_call_ids (and legacy ordinals) remain queryable
69
+ through an alias map so message-level consumers need no migration.
70
+ 5. Bounded concurrency: at most ``max_concurrency`` attempts run at once.
71
+ 6. Detached tracing: every job runs inside
72
+ :func:`~artemis.data_engine.trace.detached_trace`, so a lens's model
73
+ calls never attach to the trace span (the operator's step) that was
74
+ current when the job was submitted — no lens needs to know.
75
+
76
+ Subclasses implement :meth:`_attempt` (one summarization attempt for a
77
+ payload) and may override :meth:`_on_status` to persist pending/failed
78
+ status transitions.
79
+ """
80
+
81
+ #: Trace node label of a lens-less subclass's jobs (``lens:<name>``); a
82
+ #: configured :class:`StepLens` contributes its own ``name`` instead.
83
+ lens_name: str | None = None
84
+
85
+ def __init__(
86
+ self,
87
+ ctx: Any,
88
+ *,
89
+ lens: StepLens | None = None,
90
+ max_concurrency: int = 1,
91
+ retry_limit: int = 3,
92
+ flush_timeout_s: float = 30.0,
93
+ ):
94
+ self.ctx = ctx
95
+ self._lens = lens
96
+ self._summaries: dict[JobKey, str] = {}
97
+ self._failed: set[JobKey] = set()
98
+ self._pending_tasks: dict[JobKey, asyncio.Task] = {}
99
+ self._step_inputs: dict[JobKey, dict[str, Any]] = {}
100
+ self._retry_counts: dict[JobKey, int] = {}
101
+ self._retry_delays = (0.0, 0.5, 1.0, 2.0, 3.0)
102
+ self._retry_limit = max(0, retry_limit)
103
+ self._flush_timeout_s = max(0.0, flush_timeout_s)
104
+ self._aliases: dict[JobKey, JobKey] = {}
105
+ self._semaphore = asyncio.Semaphore(max(1, max_concurrency))
106
+
107
+ # ------------------------------------------------------------------
108
+ # Keying
109
+ # ------------------------------------------------------------------
110
+
111
+ def resolve_key(self, key: JobKey) -> JobKey:
112
+ """Resolve an alias (tool_call_id / legacy ordinal) to its canonical key."""
113
+ return self._aliases.get(key, key)
114
+
115
+ def submit(
116
+ self,
117
+ key: JobKey,
118
+ payload: dict[str, Any],
119
+ aliases: tuple[JobKey, ...] = (),
120
+ ) -> None:
121
+ """Submit one summary job without blocking the caller.
122
+
123
+ Args:
124
+ key: Canonical job key (DataEngine step id where available).
125
+ payload: Lens-specific input data; stored until the job resolves.
126
+ aliases: Additional keys (e.g. tool_call_id) that resolve to ``key``.
127
+ """
128
+ for alias in aliases:
129
+ if alias is not None and alias != key:
130
+ self._aliases[alias] = key
131
+
132
+ self._step_inputs[key] = payload
133
+ self._failed.discard(key)
134
+ self._on_status(key, "pending")
135
+
136
+ task = asyncio.create_task(self._run_until_ready(key))
137
+ self._pending_tasks[key] = task
138
+
139
+ # ------------------------------------------------------------------
140
+ # Scheduling core
141
+ # ------------------------------------------------------------------
142
+
143
+ @property
144
+ def trace_node_name(self) -> str:
145
+ """Node label the job's traces and usage records carry (``lens:<name>``)."""
146
+ name = getattr(self._lens, "name", None) or self.lens_name or type(self).__name__.lower()
147
+ return f"lens:{name}"
148
+
149
+ async def _run_until_ready(self, key: JobKey) -> None:
150
+ """Run one job to completion, detached from the submitter's trace span.
151
+
152
+ The task was created while the submitting agent's step span was
153
+ current and inherits that context; the whole job — every attempt,
154
+ retry sleeps included — runs under :func:`detached_trace` so nothing
155
+ a lens records (model calls, their thinking/raw_thinking children,
156
+ stream deltas, usage) can be attributed to the submitter's step.
157
+ """
158
+ with detached_trace(self.trace_node_name):
159
+ await self._run_attempts(key)
160
+
161
+ async def _run_attempts(self, key: JobKey) -> None:
162
+ """Retry one job independently, bounded by the configured retry limit.
163
+
164
+ Attempts at most ``1 + retry_limit`` times; on exhaustion the job
165
+ enters an explicit failed state (surfaced via :meth:`has_failed` and
166
+ the ``_on_status`` hook). Retry backoff sleeps happen outside the
167
+ concurrency semaphore so a waiting job never starves its peers.
168
+ """
169
+ while key not in self._summaries:
170
+ async with self._semaphore:
171
+ succeeded = await self._attempt(key)
172
+ if succeeded:
173
+ return
174
+
175
+ retry_count = self._retry_counts.get(key, 0) + 1
176
+ self._retry_counts[key] = retry_count
177
+ payload = self._step_inputs.get(key)
178
+ display_step = payload.get("step_number") if payload else key
179
+
180
+ if retry_count > self._retry_limit:
181
+ self._failed.add(key)
182
+ logger.warning(
183
+ f"StepMemoryService: Giving up on Step {display_step} after"
184
+ f" {retry_count} failed attempts (retry_limit={self._retry_limit})."
185
+ )
186
+ self._on_status(key, "failed")
187
+ return
188
+
189
+ delay = self._retry_delays[min(retry_count - 1, len(self._retry_delays) - 1)]
190
+ logger.info(
191
+ f"StepMemoryService: Retrying Step {display_step} "
192
+ f"(attempt {retry_count + 1}) after {delay:.1f}s"
193
+ )
194
+ if delay:
195
+ await asyncio.sleep(delay)
196
+
197
+ async def _attempt(self, key: JobKey) -> bool:
198
+ """Run one summarization attempt for ``key``; True when a summary landed.
199
+
200
+ The default implementation delegates to the configured :class:`StepLens`;
201
+ subclasses without a lens override this method directly (the Flash
202
+ visual summarizer keeps its inlined attempt for compatibility).
203
+ """
204
+ if self._lens is None:
205
+ raise NotImplementedError
206
+ payload = self._step_inputs.get(key)
207
+ if payload is None:
208
+ return False
209
+ try:
210
+ summary = await self._lens.render(key, payload)
211
+ except Exception as e:
212
+ logger.warning(f"StepMemoryService: lens '{self._lens.name}' attempt failed: {e}")
213
+ return False
214
+ if not summary:
215
+ return False
216
+ self._summaries[key] = summary
217
+ self._on_ready(key, summary)
218
+ return True
219
+
220
+ def _on_ready(self, key: JobKey, summary: str) -> None:
221
+ """Hook invoked when a lens-produced summary lands; default is a no-op."""
222
+
223
+ def _on_status(self, key: JobKey, status: str) -> None:
224
+ """Hook invoked on pending/failed transitions; default is a no-op."""
225
+
226
+ # ------------------------------------------------------------------
227
+ # Status queries (alias-resolving)
228
+ # ------------------------------------------------------------------
229
+
230
+ def get_summary(self, key: JobKey, fallback_text: str | None = None) -> str | None:
231
+ """Retrieve the summary for a job key (or alias), or fallback text."""
232
+ return self._summaries.get(self.resolve_key(key), fallback_text)
233
+
234
+ def has_summary(self, key: JobKey) -> bool:
235
+ """Check whether a job's summary has completed."""
236
+ return self.resolve_key(key) in self._summaries
237
+
238
+ def has_job(self, key: JobKey) -> bool:
239
+ """Check whether a job has been submitted for summarization."""
240
+ return self.resolve_key(key) in self._step_inputs
241
+
242
+ def has_failed(self, key: JobKey) -> bool:
243
+ """Check whether a job permanently failed (retries exhausted)."""
244
+ return self.resolve_key(key) in self._failed
245
+
246
+ def is_pending(self, key: JobKey) -> bool:
247
+ """Check whether a job still needs a summary."""
248
+ return self.has_job(key) and not self.has_summary(key)
249
+
250
+ def get_job_payload(self, key: JobKey) -> dict[str, Any] | None:
251
+ """Return the stored payload for a job key (or alias), if any."""
252
+ return self._step_inputs.get(self.resolve_key(key))
253
+
254
+ def get_step_number(self, key: JobKey) -> int | None:
255
+ """Return the human-facing step ordinal recorded for a job, if known."""
256
+ payload = self.get_job_payload(key)
257
+ if payload is None:
258
+ return None
259
+ step_number = payload.get("step_number")
260
+ return step_number if isinstance(step_number, int) else None
261
+
262
+ # ------------------------------------------------------------------
263
+ # Draining
264
+ # ------------------------------------------------------------------
265
+
266
+ async def flush(self, timeout_seconds: float | None = None) -> None:
267
+ """Wait for active jobs up to a bound, then cancel remaining retry loops."""
268
+ if timeout_seconds is None:
269
+ timeout_seconds = self._flush_timeout_s
270
+ tasks = [t for t in self._pending_tasks.values() if not t.done()]
271
+ if tasks:
272
+ logger.info(f"StepMemoryService: Flushing {len(tasks)} pending summary tasks...")
273
+ _, pending = await asyncio.wait(tasks, timeout=timeout_seconds)
274
+ if pending:
275
+ logger.warning(
276
+ f"StepMemoryService: Cancelling {len(pending)} unfinished summary tasks "
277
+ f"after {timeout_seconds:.1f}s flush timeout."
278
+ )
279
+ for task in pending:
280
+ task.cancel()
281
+ await asyncio.gather(*pending, return_exceptions=True)