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.
- package/CHANGELOG.md +10 -0
- package/README.md +159 -29
- package/artemis-main/.dockerignore +15 -0
- package/artemis-main/.env.example +34 -0
- package/artemis-main/CONTRIBUTING.md +35 -0
- package/artemis-main/Dockerfile +68 -0
- package/artemis-main/LICENSE +202 -0
- package/artemis-main/Makefile +115 -0
- package/artemis-main/README.md +322 -0
- package/artemis-main/README_CN.md +320 -0
- package/artemis-main/apps/__init__.py +15 -0
- package/artemis-main/apps/admin_console/__init__.py +19 -0
- package/artemis-main/apps/admin_console/core/__init__.py +13 -0
- package/artemis-main/apps/admin_console/core/config.py +43 -0
- package/artemis-main/apps/admin_console/core/security.py +178 -0
- package/artemis-main/apps/admin_console/core/state.py +243 -0
- package/artemis-main/apps/admin_console/database/__init__.py +13 -0
- package/artemis-main/apps/admin_console/database/connection.py +60 -0
- package/artemis-main/apps/admin_console/database/repositories/__init__.py +13 -0
- package/artemis-main/apps/admin_console/database/repositories/session_repository.py +564 -0
- package/artemis-main/apps/admin_console/database/repositories/step_repository.py +512 -0
- package/artemis-main/apps/admin_console/database/repositories/trace_repository.py +133 -0
- package/artemis-main/apps/admin_console/index.html +3787 -0
- package/artemis-main/apps/admin_console/replay_manager.py +2585 -0
- package/artemis-main/apps/admin_console/routers/__init__.py +13 -0
- package/artemis-main/apps/admin_console/routers/media.py +198 -0
- package/artemis-main/apps/admin_console/routers/replay.py +91 -0
- package/artemis-main/apps/admin_console/routers/sessions.py +239 -0
- package/artemis-main/apps/admin_console/routers/steps.py +109 -0
- package/artemis-main/apps/admin_console/routers/stream.py +56 -0
- package/artemis-main/apps/admin_console/routers/system.py +761 -0
- package/artemis-main/apps/admin_console/routers/tasks.py +517 -0
- package/artemis-main/apps/admin_console/schemas/__init__.py +13 -0
- package/artemis-main/apps/admin_console/schemas/task_schema.py +47 -0
- package/artemis-main/apps/admin_console/server.py +508 -0
- package/artemis-main/apps/admin_console/services/__init__.py +13 -0
- package/artemis-main/apps/admin_console/services/device_stream_service.py +137 -0
- package/artemis-main/apps/admin_console/services/ipc_service.py +262 -0
- package/artemis-main/apps/admin_console/services/media_service.py +623 -0
- package/artemis-main/apps/admin_console/services/model_service.py +148 -0
- package/artemis-main/apps/admin_console/services/task_preset_catalog.py +514 -0
- package/artemis-main/apps/admin_console/services/task_queue_service.py +1719 -0
- package/artemis-main/apps/admin_console/services/worker_process_io.py +144 -0
- package/artemis-main/apps/showcase_ui/README.md +31 -0
- package/artemis-main/apps/showcase_ui/angular.json +132 -0
- package/artemis-main/apps/showcase_ui/package-lock.json +9472 -0
- package/artemis-main/apps/showcase_ui/package.json +39 -0
- package/artemis-main/apps/showcase_ui/proxy.conf.json +23 -0
- package/artemis-main/apps/showcase_ui/public/favicon.ico +0 -0
- package/artemis-main/apps/showcase_ui/public/logo.png +0 -0
- package/artemis-main/apps/showcase_ui/public/logo.svg +56 -0
- package/artemis-main/apps/showcase_ui/src/app/app.component.html +24 -0
- package/artemis-main/apps/showcase_ui/src/app/app.component.scss +49 -0
- package/artemis-main/apps/showcase_ui/src/app/app.component.spec.ts +42 -0
- package/artemis-main/apps/showcase_ui/src/app/app.component.ts +32 -0
- package/artemis-main/apps/showcase_ui/src/app/app.config.ts +29 -0
- package/artemis-main/apps/showcase_ui/src/app/app.routes.ts +27 -0
- package/artemis-main/apps/showcase_ui/src/app/components/agent-stream/agent-stream-startup.spec.ts +108 -0
- package/artemis-main/apps/showcase_ui/src/app/components/agent-stream/agent-stream.component.html +1436 -0
- package/artemis-main/apps/showcase_ui/src/app/components/agent-stream/agent-stream.component.scss +3973 -0
- package/artemis-main/apps/showcase_ui/src/app/components/agent-stream/agent-stream.component.ts +2235 -0
- package/artemis-main/apps/showcase_ui/src/app/components/chat-interface/chat-interface.component.html +380 -0
- package/artemis-main/apps/showcase_ui/src/app/components/chat-interface/chat-interface.component.scss +1171 -0
- package/artemis-main/apps/showcase_ui/src/app/components/chat-interface/chat-interface.component.ts +270 -0
- package/artemis-main/apps/showcase_ui/src/app/components/floating-video-player/floating-video-player.component.html +433 -0
- package/artemis-main/apps/showcase_ui/src/app/components/floating-video-player/floating-video-player.component.scss +987 -0
- package/artemis-main/apps/showcase_ui/src/app/components/floating-video-player/floating-video-player.component.ts +632 -0
- package/artemis-main/apps/showcase_ui/src/app/components/legacy-agent-stream/legacy-agent-stream.component.html +130 -0
- package/artemis-main/apps/showcase_ui/src/app/components/legacy-agent-stream/legacy-agent-stream.component.scss +406 -0
- package/artemis-main/apps/showcase_ui/src/app/components/legacy-agent-stream/legacy-agent-stream.component.ts +74 -0
- package/artemis-main/apps/showcase_ui/src/app/components/nav-switcher/nav-switcher.component.scss +115 -0
- package/artemis-main/apps/showcase_ui/src/app/components/nav-switcher/nav-switcher.component.ts +52 -0
- package/artemis-main/apps/showcase_ui/src/app/core/data/smart-tasks.data.ts +334 -0
- package/artemis-main/apps/showcase_ui/src/app/core/i18n/language.service.spec.ts +55 -0
- package/artemis-main/apps/showcase_ui/src/app/core/i18n/language.service.ts +61 -0
- package/artemis-main/apps/showcase_ui/src/app/core/i18n/translate.pipe.ts +11 -0
- package/artemis-main/apps/showcase_ui/src/app/core/i18n/zh-cn.ts +651 -0
- package/artemis-main/apps/showcase_ui/src/app/core/models/markdown.model.ts +43 -0
- package/artemis-main/apps/showcase_ui/src/app/core/models/pro-tuning.model.spec.ts +47 -0
- package/artemis-main/apps/showcase_ui/src/app/core/models/pro-tuning.model.ts +165 -0
- package/artemis-main/apps/showcase_ui/src/app/core/models/session.model.ts +85 -0
- package/artemis-main/apps/showcase_ui/src/app/core/models/stream.model.ts +246 -0
- package/artemis-main/apps/showcase_ui/src/app/core/models/system.model.ts +118 -0
- package/artemis-main/apps/showcase_ui/src/app/core/services/task-recommendation.service.ts +111 -0
- package/artemis-main/apps/showcase_ui/src/app/pages/home/home.component.html +2130 -0
- package/artemis-main/apps/showcase_ui/src/app/pages/home/home.component.scss +5204 -0
- package/artemis-main/apps/showcase_ui/src/app/pages/home/home.component.ts +1189 -0
- package/artemis-main/apps/showcase_ui/src/app/pages/legacy-workspace/legacy-workspace.component.html +39 -0
- package/artemis-main/apps/showcase_ui/src/app/pages/legacy-workspace/legacy-workspace.component.scss +151 -0
- package/artemis-main/apps/showcase_ui/src/app/pages/legacy-workspace/legacy-workspace.component.ts +58 -0
- package/artemis-main/apps/showcase_ui/src/app/pages/workspace/workspace.component.html +201 -0
- package/artemis-main/apps/showcase_ui/src/app/pages/workspace/workspace.component.scss +886 -0
- package/artemis-main/apps/showcase_ui/src/app/pages/workspace/workspace.component.ts +324 -0
- package/artemis-main/apps/showcase_ui/src/app/services/agent.service.spec.ts +418 -0
- package/artemis-main/apps/showcase_ui/src/app/services/agent.service.ts +2012 -0
- package/artemis-main/apps/showcase_ui/src/app/services/system.service.spec.ts +173 -0
- package/artemis-main/apps/showcase_ui/src/app/services/system.service.ts +620 -0
- package/artemis-main/apps/showcase_ui/src/app/utils/action-formatter.util.spec.ts +513 -0
- package/artemis-main/apps/showcase_ui/src/app/utils/action-formatter.util.ts +1015 -0
- package/artemis-main/apps/showcase_ui/src/app/utils/image-overlay.util.spec.ts +85 -0
- package/artemis-main/apps/showcase_ui/src/app/utils/image-overlay.util.ts +662 -0
- package/artemis-main/apps/showcase_ui/src/app/utils/markdown-parser.util.spec.ts +70 -0
- package/artemis-main/apps/showcase_ui/src/app/utils/markdown-parser.util.ts +383 -0
- package/artemis-main/apps/showcase_ui/src/app/utils/recording-timeline.util.spec.ts +107 -0
- package/artemis-main/apps/showcase_ui/src/app/utils/recording-timeline.util.ts +123 -0
- package/artemis-main/apps/showcase_ui/src/app/utils/run-info.util.spec.ts +92 -0
- package/artemis-main/apps/showcase_ui/src/app/utils/run-info.util.ts +116 -0
- package/artemis-main/apps/showcase_ui/src/app/utils/stream-aggregator.util.spec.ts +640 -0
- package/artemis-main/apps/showcase_ui/src/app/utils/stream-aggregator.util.ts +1021 -0
- package/artemis-main/apps/showcase_ui/src/app/utils/tool-formatter.util.spec.ts +375 -0
- package/artemis-main/apps/showcase_ui/src/app/utils/tool-formatter.util.ts +1419 -0
- package/artemis-main/apps/showcase_ui/src/index.html +37 -0
- package/artemis-main/apps/showcase_ui/src/main.ts +22 -0
- package/artemis-main/apps/showcase_ui/src/styles.scss +60 -0
- package/artemis-main/apps/showcase_ui/tsconfig.app.json +23 -0
- package/artemis-main/apps/showcase_ui/tsconfig.json +27 -0
- package/artemis-main/apps/showcase_ui/tsconfig.spec.json +23 -0
- package/artemis-main/artemis/__init__.py +44 -0
- package/artemis-main/artemis/__main__.py +18 -0
- package/artemis-main/artemis/_legacy_imports.py +94 -0
- package/artemis-main/artemis/_version.py +30 -0
- package/artemis-main/artemis/agents/__init__.py +17 -0
- package/artemis-main/artemis/agents/checker/checker.json +9 -0
- package/artemis-main/artemis/agents/checker/checker.py +859 -0
- package/artemis-main/artemis/agents/diagnoser/diagnoser.json +23 -0
- package/artemis-main/artemis/agents/diagnoser/diagnoser.py +608 -0
- package/artemis-main/artemis/agents/explorer/constants.py +52 -0
- package/artemis-main/artemis/agents/explorer/explorer.json +57 -0
- package/artemis-main/artemis/agents/explorer/explorer.py +473 -0
- package/artemis-main/artemis/agents/explorer/geometry.py +73 -0
- package/artemis-main/artemis/agents/explorer/native_runner.py +878 -0
- package/artemis-main/artemis/agents/explorer/perception_tools.py +632 -0
- package/artemis-main/artemis/agents/explorer/run_setup.py +403 -0
- package/artemis-main/artemis/agents/explorer/screen_index.py +266 -0
- package/artemis-main/artemis/agents/explorer/tiers.py +110 -0
- package/artemis-main/artemis/agents/explorer/tool_declarations.py +394 -0
- package/artemis-main/artemis/agents/explorer/universal_runner.py +317 -0
- package/artemis-main/artemis/agents/flash/context_compressor.py +452 -0
- package/artemis-main/artemis/agents/flash/flash_runner.md +51 -0
- package/artemis-main/artemis/agents/flash/flash_summarizer.md +53 -0
- package/artemis-main/artemis/agents/flash/flash_summarizer_single.md +54 -0
- package/artemis-main/artemis/agents/flash/runner.py +1167 -0
- package/artemis-main/artemis/agents/flash/summarizer.py +511 -0
- package/artemis-main/artemis/agents/history_analyzer/history_analyzer.md +8 -0
- package/artemis-main/artemis/agents/history_analyzer/history_analyzer.py +182 -0
- package/artemis-main/artemis/agents/image_processor/image_processor.md +74 -0
- package/artemis-main/artemis/agents/image_processor/image_processor.py +322 -0
- package/artemis-main/artemis/agents/log_analyzer/log_analyzer.md +15 -0
- package/artemis-main/artemis/agents/log_analyzer/log_analyzer.py +403 -0
- package/artemis-main/artemis/agents/log_analyzer/log_reader_sub_agent.md +8 -0
- package/artemis-main/artemis/agents/log_analyzer/output_analyzer.md +11 -0
- package/artemis-main/artemis/agents/log_analyzer/output_analyzer.py +163 -0
- package/artemis-main/artemis/agents/object_detector/object_detector.json +9 -0
- package/artemis-main/artemis/agents/object_detector/object_detector.py +211 -0
- package/artemis-main/artemis/agents/operator/operator.json +3 -0
- package/artemis-main/artemis/agents/operator/operator.py +1394 -0
- package/artemis-main/artemis/agents/operator/prompts.py +1064 -0
- package/artemis-main/artemis/agents/outputter/outputter.md +36 -0
- package/artemis-main/artemis/agents/outputter/outputter.py +552 -0
- package/artemis-main/artemis/agents/planner/planner.json +32 -0
- package/artemis-main/artemis/agents/planner/planner.py +436 -0
- package/artemis-main/artemis/agents/prompt_assembly.py +79 -0
- package/artemis-main/artemis/agents/summarizer/summarizer.py +190 -0
- package/artemis-main/artemis/agents/validator/action_execution.py +98 -0
- package/artemis-main/artemis/agents/validator/categories.py +33 -0
- package/artemis-main/artemis/agents/validator/execution_loop.py +488 -0
- package/artemis-main/artemis/agents/validator/incidents.py +120 -0
- package/artemis-main/artemis/agents/validator/pixel_safety_net.md +15 -0
- package/artemis-main/artemis/agents/validator/precondition_pixel.py +320 -0
- package/artemis-main/artemis/agents/validator/precondition_xml.py +672 -0
- package/artemis-main/artemis/agents/validator/tool_declarations.py +332 -0
- package/artemis-main/artemis/agents/validator/validator.py +188 -0
- package/artemis-main/artemis/agents/video_analyzer/__init__.py +15 -0
- package/artemis-main/artemis/agents/video_analyzer/audio_native.py +543 -0
- package/artemis-main/artemis/agents/video_analyzer/blackboard.py +821 -0
- package/artemis-main/artemis/agents/video_analyzer/chunk_agentic.py +455 -0
- package/artemis-main/artemis/agents/video_analyzer/chunk_conversation.py +491 -0
- package/artemis-main/artemis/agents/video_analyzer/chunk_native.py +437 -0
- package/artemis-main/artemis/agents/video_analyzer/conflict_resolution.py +137 -0
- package/artemis-main/artemis/agents/video_analyzer/gemini_files.py +162 -0
- package/artemis-main/artemis/agents/video_analyzer/native_coordinator.py +541 -0
- package/artemis-main/artemis/agents/video_analyzer/reliability.py +128 -0
- package/artemis-main/artemis/agents/video_analyzer/universal_engine.py +487 -0
- package/artemis-main/artemis/agents/video_analyzer/universal_tools.py +323 -0
- package/artemis-main/artemis/agents/video_analyzer/video_analyzer.md +51 -0
- package/artemis-main/artemis/agents/video_analyzer/video_analyzer.py +875 -0
- package/artemis-main/artemis/agents/video_analyzer/video_sub_agent.md +15 -0
- package/artemis-main/artemis/agents/video_analyzer/video_sub_agent_audio.md +12 -0
- package/artemis-main/artemis/clients/accessibility_client.py +438 -0
- package/artemis-main/artemis/clients/screen_client_factory.py +403 -0
- package/artemis-main/artemis/config/__init__.py +309 -0
- package/artemis-main/artemis/config/agent.py +1098 -0
- package/artemis-main/artemis/config/constants.py +151 -0
- package/artemis-main/artemis/config/core/__init__.py +24 -0
- package/artemis-main/artemis/config/core/engine.py +118 -0
- package/artemis-main/artemis/config/core/interpolator.py +78 -0
- package/artemis-main/artemis/config/llm.py +267 -0
- package/artemis-main/artemis/config/paths.py +244 -0
- package/artemis-main/artemis/config/runtime.py +197 -0
- package/artemis-main/artemis/config/settings.py +315 -0
- package/artemis-main/artemis/constants.py +47 -0
- package/artemis-main/artemis/context.py +289 -0
- package/artemis-main/artemis/controllers/__init__.py +41 -0
- package/artemis-main/artemis/controllers/controller_factory.py +41 -0
- package/artemis-main/artemis/controllers/device_controller.py +29 -0
- package/artemis-main/artemis/controllers/types.py +37 -0
- package/artemis-main/artemis/controllers/unified_controller.py +649 -0
- package/artemis-main/artemis/core/__init__.py +21 -0
- package/artemis-main/artemis/core/diagnostics/__init__.py +90 -0
- package/artemis-main/artemis/core/diagnostics/adb_keys.py +270 -0
- package/artemis-main/artemis/core/diagnostics/adb_server_connection.py +330 -0
- package/artemis-main/artemis/core/diagnostics/device_smoke.py +364 -0
- package/artemis-main/artemis/core/diagnostics/emulator_manager.py +501 -0
- package/artemis-main/artemis/core/diagnostics/engine.py +438 -0
- package/artemis-main/artemis/core/diagnostics/hierarchy_parity.py +216 -0
- package/artemis-main/artemis/core/diagnostics/probes/adb_probe.py +887 -0
- package/artemis-main/artemis/core/diagnostics/probes/base.py +45 -0
- package/artemis-main/artemis/core/diagnostics/probes/credentials_probe.py +296 -0
- package/artemis-main/artemis/core/diagnostics/probes/host_probe.py +427 -0
- package/artemis-main/artemis/core/diagnostics/probes/runtime_probe.py +169 -0
- package/artemis-main/artemis/core/diagnostics/probes/toolchain_probe.py +139 -0
- package/artemis-main/artemis/core/diagnostics/readiness.py +83 -0
- package/artemis-main/artemis/core/diagnostics/schema.py +118 -0
- package/artemis-main/artemis/core/tool_declaration.py +43 -0
- package/artemis-main/artemis/core/tool_failure.py +56 -0
- package/artemis-main/artemis/data_engine/context_vars.py +24 -0
- package/artemis-main/artemis/data_engine/engine.py +1615 -0
- package/artemis-main/artemis/data_engine/history_reader.py +142 -0
- package/artemis-main/artemis/data_engine/models.py +131 -0
- package/artemis-main/artemis/data_engine/storage.py +1245 -0
- package/artemis-main/artemis/data_engine/trace.py +727 -0
- package/artemis-main/artemis/drivers/__init__.py +48 -0
- package/artemis-main/artemis/drivers/android/adb_driver.py +507 -0
- package/artemis-main/artemis/drivers/android/hierarchy.py +64 -0
- package/artemis-main/artemis/drivers/android/input_ime.py +120 -0
- package/artemis-main/artemis/drivers/android/recorder.py +57 -0
- package/artemis-main/artemis/drivers/base.py +290 -0
- package/artemis-main/artemis/drivers/cloud/cloud_driver.py +117 -0
- package/artemis-main/artemis/drivers/factory.py +80 -0
- package/artemis-main/artemis/drivers/mock/mock_driver.py +198 -0
- package/artemis-main/artemis/drivers/types.py +67 -0
- package/artemis-main/artemis/graph/checkpoints.py +1243 -0
- package/artemis-main/artemis/graph/graph.py +1055 -0
- package/artemis-main/artemis/graph/perception.py +234 -0
- package/artemis-main/artemis/graph/state.py +151 -0
- package/artemis-main/artemis/graph/visibility.py +238 -0
- package/artemis-main/artemis/interfaces/cli/__init__.py +19 -0
- package/artemis-main/artemis/interfaces/cli/commands/__init__.py +15 -0
- package/artemis-main/artemis/interfaces/cli/commands/batch.py +278 -0
- package/artemis-main/artemis/interfaces/cli/commands/doctor.py +468 -0
- package/artemis-main/artemis/interfaces/cli/commands/helper.py +226 -0
- package/artemis-main/artemis/interfaces/cli/commands/init.py +193 -0
- package/artemis-main/artemis/interfaces/cli/commands/mcp.py +862 -0
- package/artemis-main/artemis/interfaces/cli/commands/run.py +463 -0
- package/artemis-main/artemis/interfaces/cli/commands/server.py +151 -0
- package/artemis-main/artemis/interfaces/cli/commands/server_lifecycle.py +328 -0
- package/artemis-main/artemis/interfaces/cli/commands/trace.py +111 -0
- package/artemis-main/artemis/interfaces/cli/commands/ui.py +404 -0
- package/artemis-main/artemis/interfaces/cli/main.py +109 -0
- package/artemis-main/artemis/interfaces/sdk/__init__.py +31 -0
- package/artemis-main/artemis/interfaces/sdk/client.py +19 -0
- package/artemis-main/artemis/interfaces/sdk/task.py +72 -0
- package/artemis-main/artemis/llm/__init__.py +25 -0
- package/artemis-main/artemis/llm/fake_model.py +179 -0
- package/artemis-main/artemis/llm/google/__init__.py +57 -0
- package/artemis-main/artemis/llm/google/provider.py +185 -0
- package/artemis-main/artemis/llm/google/usage.py +148 -0
- package/artemis-main/artemis/llm/parser.py +32 -0
- package/artemis-main/artemis/llm/reliability.py +267 -0
- package/artemis-main/artemis/llm/router.py +389 -0
- package/artemis-main/artemis/llm/schemas.py +36 -0
- package/artemis-main/artemis/llm/structured.py +196 -0
- package/artemis-main/artemis/main.py +58 -0
- package/artemis-main/artemis/mcp/__init__.py +20 -0
- package/artemis-main/artemis/mcp/action_executor.py +760 -0
- package/artemis-main/artemis/mcp/action_manifest.py +320 -0
- package/artemis-main/artemis/mcp/action_names.py +172 -0
- package/artemis-main/artemis/mcp/action_server.py +202 -0
- package/artemis-main/artemis/mcp/action_session.py +245 -0
- package/artemis-main/artemis/mcp/action_specs.py +868 -0
- package/artemis-main/artemis/mcp/action_types.py +86 -0
- package/artemis-main/artemis/mcp/actuators/__init__.py +21 -0
- package/artemis-main/artemis/mcp/actuators/adb.py +414 -0
- package/artemis-main/artemis/mcp/actuators/base.py +113 -0
- package/artemis-main/artemis/mcp/actuators/mock.py +70 -0
- package/artemis-main/artemis/mcp/adb_server.py +490 -0
- package/artemis-main/artemis/mcp/observation.py +133 -0
- package/artemis-main/artemis/mcp/xml_search_server.py +297 -0
- package/artemis-main/artemis/memory/__init__.py +90 -0
- package/artemis-main/artemis/memory/chunking.py +1843 -0
- package/artemis-main/artemis/memory/context_policy.py +226 -0
- package/artemis-main/artemis/memory/step_capsule.md +73 -0
- package/artemis-main/artemis/memory/step_memory.py +281 -0
- package/artemis-main/artemis/memory/transcript.py +798 -0
- package/artemis-main/artemis/platform/__init__.py +61 -0
- package/artemis-main/artemis/platform/base.py +104 -0
- package/artemis-main/artemis/platform/darwin.py +184 -0
- package/artemis-main/artemis/platform/linux.py +207 -0
- package/artemis-main/artemis/platform/windows.py +193 -0
- package/artemis-main/artemis/resources/__init__.py +46 -0
- package/artemis-main/artemis/runtime/__init__.py +108 -0
- package/artemis-main/artemis/runtime/adb_endpoint.py +173 -0
- package/artemis-main/artemis/runtime/awake_lease.py +283 -0
- package/artemis-main/artemis/runtime/awake_service.py +343 -0
- package/artemis-main/artemis/runtime/cancel_requests.py +254 -0
- package/artemis-main/artemis/runtime/daemon_client.py +422 -0
- package/artemis-main/artemis/runtime/device_lock.py +1013 -0
- package/artemis-main/artemis/runtime/device_pool.py +478 -0
- package/artemis-main/artemis/runtime/helper_manager.py +1030 -0
- package/artemis-main/artemis/runtime/process_probe.py +98 -0
- package/artemis-main/artemis/runtime/server_lifecycle.py +435 -0
- package/artemis-main/artemis/runtime/supervisor.py +121 -0
- package/artemis-main/artemis/runtime/trace_store.py +375 -0
- package/artemis-main/artemis/sdk/__init__.py +26 -0
- package/artemis-main/artemis/sdk/agent.py +485 -0
- package/artemis-main/artemis/sdk/builders/__init__.py +29 -0
- package/artemis-main/artemis/sdk/builders/agent_config_builder.py +474 -0
- package/artemis-main/artemis/sdk/constants.py +15 -0
- package/artemis-main/artemis/sdk/run_outcome.py +58 -0
- package/artemis-main/artemis/sdk/types/__init__.py +61 -0
- package/artemis-main/artemis/sdk/types/agent.py +89 -0
- package/artemis-main/artemis/sdk/utils.py +28 -0
- package/artemis-main/artemis/services/llm.py +1040 -0
- package/artemis-main/artemis/services/token_meter.py +267 -0
- package/artemis-main/artemis/telemetry/__init__.py +35 -0
- package/artemis-main/artemis/telemetry/models.py +64 -0
- package/artemis-main/artemis/telemetry/replay.py +34 -0
- package/artemis-main/artemis/telemetry/storage/json_storage.py +47 -0
- package/artemis-main/artemis/telemetry/tracer.py +85 -0
- package/artemis-main/artemis/toolchain/__init__.py +52 -0
- package/artemis-main/artemis/toolchain/descriptors.py +92 -0
- package/artemis-main/artemis/toolchain/resolver.py +185 -0
- package/artemis-main/artemis/tools/base.py +232 -0
- package/artemis-main/artemis/tools/command_tool.py +1240 -0
- package/artemis-main/artemis/tools/committee_prompts.json +5 -0
- package/artemis-main/artemis/tools/committee_tool.py +422 -0
- package/artemis-main/artemis/tools/diagnoser_submit_answer_tool.py +87 -0
- package/artemis-main/artemis/tools/diagnostic_tool.py +120 -0
- package/artemis-main/artemis/tools/explorer_tool.py +581 -0
- package/artemis-main/artemis/tools/history/__init__.py +398 -0
- package/artemis-main/artemis/tools/history/replay.py +120 -0
- package/artemis-main/artemis/tools/history/screenshot.py +139 -0
- package/artemis-main/artemis/tools/history/search.py +563 -0
- package/artemis-main/artemis/tools/image_processor_tool.py +124 -0
- package/artemis-main/artemis/tools/index.py +47 -0
- package/artemis-main/artemis/tools/log_tool.py +108 -0
- package/artemis-main/artemis/tools/mobile/launch_app.py +132 -0
- package/artemis-main/artemis/tools/mobile/log_utils.py +138 -0
- package/artemis-main/artemis/tools/mobile/ocr.py +202 -0
- package/artemis-main/artemis/tools/mobile/read_hierarchy.py +108 -0
- package/artemis-main/artemis/tools/mobile/read_logs.py +116 -0
- package/artemis-main/artemis/tools/mobile/search_logs.py +260 -0
- package/artemis-main/artemis/tools/object_detection_tool.py +244 -0
- package/artemis-main/artemis/tools/scratchpad.py +819 -0
- package/artemis-main/artemis/tools/system/adb_shell.py +31 -0
- package/artemis-main/artemis/tools/tool_wrapper.py +270 -0
- package/artemis-main/artemis/tools/video_tool.py +261 -0
- package/artemis-main/artemis/tools/wait_tool.py +97 -0
- package/artemis-main/artemis/utils/app_launch_utils.py +207 -0
- package/artemis-main/artemis/utils/coordinates.py +606 -0
- package/artemis-main/artemis/utils/credentials_validator.py +201 -0
- package/artemis-main/artemis/utils/cv_canvas.py +189 -0
- package/artemis-main/artemis/utils/cython_compat.py +45 -0
- package/artemis-main/artemis/utils/element_hit_test.py +80 -0
- package/artemis-main/artemis/utils/image_diff.py +335 -0
- package/artemis-main/artemis/utils/image_hash.py +80 -0
- package/artemis-main/artemis/utils/notes.py +470 -0
- package/artemis-main/artemis/utils/ocr_api.py +105 -0
- package/artemis-main/artemis/utils/ocr_xml_fusion.py +318 -0
- package/artemis-main/artemis/utils/plan_grammar.py +513 -0
- package/artemis-main/artemis/utils/python_executor.py +213 -0
- package/artemis-main/artemis/utils/startup_progress.py +60 -0
- package/artemis-main/artemis/utils/task_tree.py +1174 -0
- package/artemis-main/artemis/utils/text.py +37 -0
- package/artemis-main/artemis/utils/ui_filter.py +651 -0
- package/artemis-main/artemis/utils/video.py +685 -0
- package/artemis-main/artemis/utils/visualization.py +802 -0
- package/artemis-main/config/artemis.example.jsonc +12 -0
- package/artemis-main/mcp_server/README.md +111 -0
- package/artemis-main/mcp_server/__init__.py +21 -0
- package/artemis-main/mcp_server/__main__.py +20 -0
- package/artemis-main/mcp_server/background/__init__.py +15 -0
- package/artemis-main/mcp_server/background/task_runner.py +453 -0
- package/artemis-main/mcp_server/base.py +49 -0
- package/artemis-main/mcp_server/notifiers/__init__.py +74 -0
- package/artemis-main/mcp_server/notifiers/agentapi.py +289 -0
- package/artemis-main/mcp_server/notifiers/base.py +45 -0
- package/artemis-main/mcp_server/notifiers/composite.py +87 -0
- package/artemis-main/mcp_server/notifiers/desktop.py +108 -0
- package/artemis-main/mcp_server/notifiers/file.py +69 -0
- package/artemis-main/mcp_server/notifiers/script.py +90 -0
- package/artemis-main/mcp_server/notifiers/webhook.py +87 -0
- package/artemis-main/mcp_server/rules.md +68 -0
- package/artemis-main/mcp_server/server.py +106 -0
- package/artemis-main/mcp_server/tools/__init__.py +29 -0
- package/artemis-main/mcp_server/tools/device_state.py +107 -0
- package/artemis-main/mcp_server/tools/diagnose.py +1249 -0
- package/artemis-main/mcp_server/tools/inspect_trace.py +394 -0
- package/artemis-main/mcp_server/tools/task_manager.py +627 -0
- package/artemis-main/mcp_server/tools/task_runner.py +548 -0
- package/artemis-main/mcp_server/utils/__init__.py +15 -0
- package/artemis-main/mcp_server/utils/device_utils.py +180 -0
- package/artemis-main/mcp_server/utils/env_utils.py +65 -0
- package/artemis-main/packages/artemis-accessibility-helper/ArtemisAccessibilityHelper.apk +0 -0
- package/artemis-main/packages/artemis-accessibility-helper/app/build.gradle.kts +35 -0
- package/artemis-main/packages/artemis-accessibility-helper/app/src/main/AndroidManifest.xml +49 -0
- package/artemis-main/packages/artemis-accessibility-helper/app/src/main/java/com/artemis/helper/A11yNode.java +274 -0
- package/artemis-main/packages/artemis-accessibility-helper/app/src/main/java/com/artemis/helper/ArtemisAccessibilityService.java +227 -0
- package/artemis-main/packages/artemis-accessibility-helper/app/src/main/java/com/artemis/helper/CommandServer.java +463 -0
- package/artemis-main/packages/artemis-accessibility-helper/app/src/main/java/com/artemis/helper/DisplayUtils.java +111 -0
- package/artemis-main/packages/artemis-accessibility-helper/app/src/main/java/com/artemis/helper/GestureController.java +232 -0
- package/artemis-main/packages/artemis-accessibility-helper/app/src/main/java/com/artemis/helper/HierarchyDumper.java +832 -0
- package/artemis-main/packages/artemis-accessibility-helper/app/src/main/java/com/artemis/helper/TokenReceiver.java +36 -0
- package/artemis-main/packages/artemis-accessibility-helper/app/src/main/java/com/artemis/helper/TokenStore.java +38 -0
- package/artemis-main/packages/artemis-accessibility-helper/app/src/main/java/com/artemis/helper/XmlUtils.java +102 -0
- package/artemis-main/packages/artemis-accessibility-helper/app/src/main/res/values/strings.xml +4 -0
- package/artemis-main/packages/artemis-accessibility-helper/app/src/main/res/xml/accessibility_service_config.xml +10 -0
- package/artemis-main/packages/artemis-accessibility-helper/build.gradle.kts +3 -0
- package/artemis-main/packages/artemis-accessibility-helper/build_apk.sh +215 -0
- package/artemis-main/packages/artemis-accessibility-helper/debug.keystore +0 -0
- package/artemis-main/packages/artemis-accessibility-helper/gradle.properties +2 -0
- package/artemis-main/packages/artemis-accessibility-helper/helper_manifest.json +7 -0
- package/artemis-main/packages/artemis-accessibility-helper/settings.gradle.kts +17 -0
- package/artemis-main/packages/artemis-client/DESIGN.md +89 -0
- package/artemis-main/packages/artemis-client/README.md +112 -0
- package/artemis-main/packages/artemis-client/examples/basic.py +30 -0
- package/artemis-main/packages/artemis-client/pyproject.toml +36 -0
- package/artemis-main/packages/artemis-client/src/artemis_client/__init__.py +45 -0
- package/artemis-main/packages/artemis-client/src/artemis_client/client.py +401 -0
- package/artemis-main/packages/artemis-client/src/artemis_client/errors.py +66 -0
- package/artemis-main/packages/artemis-client/src/artemis_client/models.py +212 -0
- package/artemis-main/packages/artemis-client/src/artemis_client/py.typed +1 -0
- package/artemis-main/packages/artemis-client/src/artemis_client/transport.py +134 -0
- package/artemis-main/packages/artemis-client/tests/test_client.py +289 -0
- package/artemis-main/packages/artemis-client/tests/test_transport.py +85 -0
- package/artemis-main/pyproject.toml +259 -0
- package/artemis-main/pyright-core.json +17 -0
- package/artemis-main/scripts/check_dependency_sources.py +68 -0
- package/artemis-main/scripts/install_deps.ps1 +520 -0
- package/artemis-main/scripts/install_deps.sh +533 -0
- package/artemis-main/scripts/quality_ratchet.py +97 -0
- package/artemis-main/scripts/real_device_video_acceptance.py +483 -0
- package/artemis-main/scripts/run_edge_case_part.py +327 -0
- package/artemis-main/scripts/run_ui.py +242 -0
- package/artemis-main/scripts/start.ps1 +500 -0
- package/artemis-main/scripts/test_all_edge_cases.py +269 -0
- package/artemis-main/scripts/verify_concurrency_and_devices.py +385 -0
- package/artemis-main/setup.py +164 -0
- package/artemis-main/start.bat +35 -0
- package/artemis-main/start.sh +423 -0
- package/artemis-main/third_party/mobile_use/LICENSE +190 -0
- package/artemis-main/third_party/mobile_use/METADATA +27 -0
- package/artemis-main/third_party/mobile_use/NOTICE +12 -0
- package/artemis-main/third_party/mobile_use/__init__.py +19 -0
- package/artemis-main/third_party/mobile_use/agents/__init__.py +13 -0
- package/artemis-main/third_party/mobile_use/agents/hopper/__init__.py +13 -0
- package/artemis-main/third_party/mobile_use/agents/hopper/hopper.md +22 -0
- package/artemis-main/third_party/mobile_use/agents/hopper/hopper.py +80 -0
- package/artemis-main/third_party/mobile_use/clients/__init__.py +13 -0
- package/artemis-main/third_party/mobile_use/clients/ui_automator_client.py +482 -0
- package/artemis-main/third_party/mobile_use/config/__init__.py +13 -0
- package/artemis-main/third_party/mobile_use/config/llm.py +183 -0
- package/artemis-main/third_party/mobile_use/config/output.py +184 -0
- package/artemis-main/third_party/mobile_use/context.py +119 -0
- package/artemis-main/third_party/mobile_use/controllers/__init__.py +13 -0
- package/artemis-main/third_party/mobile_use/controllers/platform_specific_commands_controller.py +151 -0
- package/artemis-main/third_party/mobile_use/controllers/types.py +92 -0
- package/artemis-main/third_party/mobile_use/controllers/unified_controller.py +183 -0
- package/artemis-main/third_party/mobile_use/graph.py +54 -0
- package/artemis-main/third_party/mobile_use/llm_service.py +193 -0
- package/artemis-main/third_party/mobile_use/main.py +130 -0
- package/artemis-main/third_party/mobile_use/sdk/__init__.py +13 -0
- package/artemis-main/third_party/mobile_use/sdk/agent.py +1060 -0
- package/artemis-main/third_party/mobile_use/sdk/builders/__init__.py +13 -0
- package/artemis-main/third_party/mobile_use/sdk/builders/agent_config_builder.py +225 -0
- package/artemis-main/third_party/mobile_use/sdk/builders/index.py +33 -0
- package/artemis-main/third_party/mobile_use/sdk/builders/task_request_builder.py +243 -0
- package/artemis-main/third_party/mobile_use/sdk/types/__init__.py +13 -0
- package/artemis-main/third_party/mobile_use/sdk/types/agent.py +98 -0
- package/artemis-main/third_party/mobile_use/sdk/types/exceptions.py +109 -0
- package/artemis-main/third_party/mobile_use/sdk/types/task.py +234 -0
- package/artemis-main/third_party/mobile_use/testing.py +59 -0
- package/artemis-main/third_party/mobile_use/tools/__init__.py +13 -0
- package/artemis-main/third_party/mobile_use/tools/index.py +44 -0
- package/artemis-main/third_party/mobile_use/tools/mobile/__init__.py +13 -0
- package/artemis-main/third_party/mobile_use/tools/mobile/launch_app.py +89 -0
- package/artemis-main/third_party/mobile_use/tools/tool_wrapper.py +52 -0
- package/artemis-main/third_party/mobile_use/utils/__init__.py +13 -0
- package/artemis-main/third_party/mobile_use/utils/app_launch_utils.py +217 -0
- package/artemis-main/third_party/mobile_use/utils/cli_helpers.py +60 -0
- package/artemis-main/third_party/mobile_use/utils/decorators.py +158 -0
- package/artemis-main/third_party/mobile_use/utils/file.py +31 -0
- package/artemis-main/third_party/mobile_use/utils/logger.py +216 -0
- package/artemis-main/third_party/mobile_use/utils/media.py +213 -0
- package/artemis-main/third_party/mobile_use/utils/shell_utils.py +39 -0
- package/artemis-main/third_party/mobile_use/utils/video.py +398 -0
- package/artemis-main/uv.lock +4908 -0
- package/bin/android-midscene-automation.js +1 -5
- package/bin/artemis-config.mjs +15 -0
- package/bin/artemis-embedded.py +56 -0
- package/bin/start-artemis.mjs +30 -0
- package/bin/start-dev.mjs +112 -0
- package/bin/test_artemis_embedded.py +44 -0
- package/package.json +4 -2
- package/server/artemis.ts +34 -0
- package/server/http-api.ts +12 -0
- package/src/App.vue +10 -5
- package/src/api.ts +5 -0
- package/src/appium-recorder/AppiumPage.vue +1 -1
- package/src/appium-recorder/components/ComponentInspector.vue +1 -1
- package/src/appium-recorder/components/FlowNodeSearch.vue +3 -3
- package/src/appium-recorder/components/ImageCheckDialog.vue +3 -3
- package/src/appium-recorder/components/RecorderWorkspace.vue +4 -4
- package/src/appium-recorder/flow-appearance.ts +4 -4
- package/src/components/AppSidebar.vue +8 -7
- package/src/components/MidsceneMaintenanceNotice.vue +17 -0
- package/src/components/analytics/AnalysisCharts.vue +4 -4
- package/src/components/analytics/ReportAnalysis.vue +1 -1
- package/src/components/generator/TestCaseFileUpload.vue +1 -1
- package/src/components/help/HelpCenter.vue +56 -37
- package/src/pages/AiGeneratorPage.vue +1 -1
- package/src/pages/ArtemisPage.vue +72 -0
- package/src/pages/ConfigPage.vue +18 -19
- package/src/style.css +340 -121
- package/src/types.ts +1 -1
|
@@ -0,0 +1,315 @@
|
|
|
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
|
+
"""Application settings and environment variable configuration management."""
|
|
16
|
+
|
|
17
|
+
import os
|
|
18
|
+
from pathlib import Path
|
|
19
|
+
|
|
20
|
+
from dotenv import load_dotenv
|
|
21
|
+
from pydantic import Field, SecretStr, model_validator
|
|
22
|
+
from pydantic_settings import BaseSettings
|
|
23
|
+
|
|
24
|
+
from artemis.config.constants import (
|
|
25
|
+
DEFAULT_ADB_HOST,
|
|
26
|
+
DEFAULT_ADB_PORT,
|
|
27
|
+
DEFAULT_MODEL,
|
|
28
|
+
DEFAULT_PROFILE,
|
|
29
|
+
ENV_ANTHROPIC_API_KEY,
|
|
30
|
+
ENV_DATA_ENGINE_DB_PATH,
|
|
31
|
+
ENV_GCP_API_KEY,
|
|
32
|
+
ENV_GEMINI_API_KEY,
|
|
33
|
+
ENV_GOOGLE_API_KEY,
|
|
34
|
+
ENV_OCR_API_KEY,
|
|
35
|
+
ENV_OPEN_ROUTER_API_KEY,
|
|
36
|
+
ENV_OPENAI_API_KEY,
|
|
37
|
+
ENV_VISION_API_KEY,
|
|
38
|
+
ENV_XAI_API_KEY,
|
|
39
|
+
)
|
|
40
|
+
from artemis.config.paths import (
|
|
41
|
+
GLOBAL_APP_DIR,
|
|
42
|
+
get_data_engine_db_path,
|
|
43
|
+
get_default_traces_path,
|
|
44
|
+
get_env_file,
|
|
45
|
+
get_temp_dir,
|
|
46
|
+
)
|
|
47
|
+
from third_party.mobile_use.utils.logger import get_logger
|
|
48
|
+
|
|
49
|
+
# Installed wheels load .env from the user directory, outside site-packages.
|
|
50
|
+
_canonical_env = get_env_file()
|
|
51
|
+
load_dotenv(dotenv_path=_canonical_env, verbose=True)
|
|
52
|
+
_global_env = GLOBAL_APP_DIR / ".env"
|
|
53
|
+
if _global_env.exists() and _global_env.resolve() != _canonical_env.resolve():
|
|
54
|
+
load_dotenv(dotenv_path=_global_env, verbose=True)
|
|
55
|
+
|
|
56
|
+
logger = get_logger(__name__)
|
|
57
|
+
|
|
58
|
+
|
|
59
|
+
def is_placeholder_key(val: str | SecretStr | None) -> bool:
|
|
60
|
+
"""Check if an API credential is empty or an unconfigured placeholder value."""
|
|
61
|
+
if val is None:
|
|
62
|
+
return True
|
|
63
|
+
raw_str = val.get_secret_value() if isinstance(val, SecretStr) else str(val)
|
|
64
|
+
raw_str = raw_str.strip().lower()
|
|
65
|
+
if not raw_str:
|
|
66
|
+
return True
|
|
67
|
+
if raw_str in (
|
|
68
|
+
"api_key",
|
|
69
|
+
"your_api_key",
|
|
70
|
+
"your_api_key_here",
|
|
71
|
+
"your_gemini_api_key_here",
|
|
72
|
+
"your_google_cloud_vision_api_key_here",
|
|
73
|
+
"your_openai_api_key_here",
|
|
74
|
+
"your_anthropic_api_key_here",
|
|
75
|
+
"your_openrouter_api_key_here",
|
|
76
|
+
"your_xai_api_key_here",
|
|
77
|
+
"none",
|
|
78
|
+
"empty",
|
|
79
|
+
"null",
|
|
80
|
+
"undefined",
|
|
81
|
+
):
|
|
82
|
+
return True
|
|
83
|
+
if (
|
|
84
|
+
(raw_str.startswith("your_") and raw_str.endswith("_here"))
|
|
85
|
+
or (raw_str.startswith("<") and raw_str.endswith(">"))
|
|
86
|
+
or (raw_str.startswith("[") and raw_str.endswith("]"))
|
|
87
|
+
):
|
|
88
|
+
return True
|
|
89
|
+
return False
|
|
90
|
+
|
|
91
|
+
|
|
92
|
+
class Settings(BaseSettings):
|
|
93
|
+
"""Centralized ARTEMIS runtime settings loaded from environment and .env files."""
|
|
94
|
+
|
|
95
|
+
# LLM Provider Authentication
|
|
96
|
+
OPENAI_API_KEY: SecretStr | None = None
|
|
97
|
+
GOOGLE_API_KEY: SecretStr | None = None
|
|
98
|
+
GEMINI_API_KEY: SecretStr | None = None
|
|
99
|
+
GCP_API_KEY: SecretStr | None = None
|
|
100
|
+
ANTHROPIC_API_KEY: SecretStr | None = None
|
|
101
|
+
XAI_API_KEY: SecretStr | None = None
|
|
102
|
+
OPEN_ROUTER_API_KEY: SecretStr | None = None
|
|
103
|
+
|
|
104
|
+
# Google Cloud Vision OCR Authentication
|
|
105
|
+
OCR_API_KEY: SecretStr | None = None
|
|
106
|
+
VISION_API_KEY: SecretStr | None = None
|
|
107
|
+
API_KEY: SecretStr | None = None
|
|
108
|
+
|
|
109
|
+
# Custom Provider Endpoints
|
|
110
|
+
OPENAI_BASE_URL: str | None = None
|
|
111
|
+
|
|
112
|
+
# Android ADB Connectivity
|
|
113
|
+
ADB_HOST: str | None = Field(default=DEFAULT_ADB_HOST)
|
|
114
|
+
ADB_PORT: int | None = Field(default=DEFAULT_ADB_PORT)
|
|
115
|
+
ADB_DEVICE_SERIAL: str | None = None
|
|
116
|
+
# UI hierarchy backend: "auto" = Accessibility Helper with UIAutomator2
|
|
117
|
+
# fallback, "helper" = helper only, "uiautomator" = UIAutomator2 only.
|
|
118
|
+
ARTEMIS_HIERARCHY_BACKEND: str = Field(default="auto")
|
|
119
|
+
# Whether a task may install / upgrade the Accessibility Helper APK on a device
|
|
120
|
+
# it holds. False = only attach to a helper installed by `artemis helper install`.
|
|
121
|
+
ARTEMIS_HELPER_AUTO_INSTALL: bool = Field(default=True)
|
|
122
|
+
|
|
123
|
+
# Execution Defaults
|
|
124
|
+
PROJECT_NAME: str | None = None
|
|
125
|
+
ARTEMIS_DEFAULT_PROFILE: str = Field(default=DEFAULT_PROFILE)
|
|
126
|
+
ARTEMIS_DEFAULT_MODEL: str = Field(default=DEFAULT_MODEL)
|
|
127
|
+
|
|
128
|
+
# Explorer Tool Settings (the tier itself is configured in artemis.jsonc or
|
|
129
|
+
# via ARTEMIS_EXPLORER_VERSION; see artemis.config.agent.ExplorerConfig)
|
|
130
|
+
EXPLORER_CACHING: bool | None = Field(
|
|
131
|
+
default=None,
|
|
132
|
+
description=(
|
|
133
|
+
"Environment-level override for Explorer context caching; unset"
|
|
134
|
+
" defers to the agent configuration and the tier default."
|
|
135
|
+
),
|
|
136
|
+
)
|
|
137
|
+
|
|
138
|
+
# LLM Reliability
|
|
139
|
+
LLM_PAUSE_TIMEOUT_SECONDS: float = Field(
|
|
140
|
+
default=900.0,
|
|
141
|
+
description=(
|
|
142
|
+
"How long a task waits in the paused state after exhausting LLM"
|
|
143
|
+
" retries before failing with LLMExhaustedError. <= 0 waits"
|
|
144
|
+
" forever (legacy interactive behavior)."
|
|
145
|
+
),
|
|
146
|
+
)
|
|
147
|
+
|
|
148
|
+
# Paths & Storage
|
|
149
|
+
TRACES_PATH: Path = Field(default_factory=get_default_traces_path)
|
|
150
|
+
DATA_ENGINE_DB_PATH: Path = Field(default_factory=get_data_engine_db_path)
|
|
151
|
+
TEMP_PATH: Path = Field(default_factory=get_temp_dir)
|
|
152
|
+
|
|
153
|
+
model_config = {"env_file": ".env", "extra": "ignore"}
|
|
154
|
+
|
|
155
|
+
@model_validator(mode="after")
|
|
156
|
+
def fallback_api_keys(self) -> "Settings":
|
|
157
|
+
"""Normalize Google / Gemini / GCP and OCR API keys and filter placeholders."""
|
|
158
|
+
# Sanitize any placeholder values loaded from environment or .env
|
|
159
|
+
for attr in (
|
|
160
|
+
"OPENAI_API_KEY",
|
|
161
|
+
"GOOGLE_API_KEY",
|
|
162
|
+
"GEMINI_API_KEY",
|
|
163
|
+
"GCP_API_KEY",
|
|
164
|
+
"ANTHROPIC_API_KEY",
|
|
165
|
+
"XAI_API_KEY",
|
|
166
|
+
"OPEN_ROUTER_API_KEY",
|
|
167
|
+
"OCR_API_KEY",
|
|
168
|
+
"VISION_API_KEY",
|
|
169
|
+
"API_KEY",
|
|
170
|
+
):
|
|
171
|
+
val = getattr(self, attr, None)
|
|
172
|
+
if val and is_placeholder_key(val):
|
|
173
|
+
setattr(self, attr, None)
|
|
174
|
+
|
|
175
|
+
if not self.GOOGLE_API_KEY:
|
|
176
|
+
if self.GEMINI_API_KEY:
|
|
177
|
+
self.GOOGLE_API_KEY = self.GEMINI_API_KEY
|
|
178
|
+
elif self.GCP_API_KEY:
|
|
179
|
+
self.GOOGLE_API_KEY = self.GCP_API_KEY
|
|
180
|
+
|
|
181
|
+
# Fallback for OCR & Vision API keys
|
|
182
|
+
if not self.OCR_API_KEY and self.VISION_API_KEY:
|
|
183
|
+
self.OCR_API_KEY = self.VISION_API_KEY
|
|
184
|
+
return self
|
|
185
|
+
|
|
186
|
+
def get_api_key(self, provider: str) -> SecretStr | None:
|
|
187
|
+
"""Get API key for a specified provider.
|
|
188
|
+
|
|
189
|
+
Args:
|
|
190
|
+
provider: Provider name (e.g., 'google', 'gemini', 'openai', 'ocr', 'vision').
|
|
191
|
+
|
|
192
|
+
Returns:
|
|
193
|
+
SecretStr containing the API key or None if not configured.
|
|
194
|
+
"""
|
|
195
|
+
from artemis.llm.google.provider import is_google_family_provider
|
|
196
|
+
|
|
197
|
+
provider_lower = provider.lower()
|
|
198
|
+
key: SecretStr | None = None
|
|
199
|
+
if is_google_family_provider(provider_lower):
|
|
200
|
+
key = self.GOOGLE_API_KEY or self.GEMINI_API_KEY or self.GCP_API_KEY
|
|
201
|
+
elif provider_lower in ("ocr", "vision", "google_vision"):
|
|
202
|
+
key = self.OCR_API_KEY or self.VISION_API_KEY
|
|
203
|
+
elif provider_lower == "openai":
|
|
204
|
+
key = self.OPENAI_API_KEY
|
|
205
|
+
elif provider_lower in ("anthropic", "claude"):
|
|
206
|
+
key = self.ANTHROPIC_API_KEY
|
|
207
|
+
elif provider_lower == "openrouter":
|
|
208
|
+
key = self.OPEN_ROUTER_API_KEY
|
|
209
|
+
elif provider_lower in ("xai", "grok"):
|
|
210
|
+
key = self.XAI_API_KEY
|
|
211
|
+
|
|
212
|
+
if key and not is_placeholder_key(key):
|
|
213
|
+
return key
|
|
214
|
+
return None
|
|
215
|
+
|
|
216
|
+
def set_api_key(self, provider: str, key: str, persist_to_env: bool = False) -> None:
|
|
217
|
+
"""Dynamically set an API key at runtime, optionally persisting to .env in the app dir.
|
|
218
|
+
|
|
219
|
+
Args:
|
|
220
|
+
provider: Target provider name.
|
|
221
|
+
key: Secret API key string.
|
|
222
|
+
persist_to_env: Whether to save the key to the app directory's .env file.
|
|
223
|
+
"""
|
|
224
|
+
secret = SecretStr(key)
|
|
225
|
+
from artemis.llm.google.provider import is_google_family_provider
|
|
226
|
+
|
|
227
|
+
provider_lower = provider.lower()
|
|
228
|
+
env_key_name = None
|
|
229
|
+
|
|
230
|
+
if is_google_family_provider(provider_lower):
|
|
231
|
+
self.GOOGLE_API_KEY = secret
|
|
232
|
+
self.GEMINI_API_KEY = secret
|
|
233
|
+
self.GCP_API_KEY = secret
|
|
234
|
+
env_key_name = ENV_GEMINI_API_KEY
|
|
235
|
+
os.environ[ENV_GOOGLE_API_KEY] = key
|
|
236
|
+
os.environ[ENV_GEMINI_API_KEY] = key
|
|
237
|
+
os.environ[ENV_GCP_API_KEY] = key
|
|
238
|
+
elif provider_lower == "openai":
|
|
239
|
+
self.OPENAI_API_KEY = secret
|
|
240
|
+
env_key_name = ENV_OPENAI_API_KEY
|
|
241
|
+
os.environ[ENV_OPENAI_API_KEY] = key
|
|
242
|
+
elif provider_lower in ("anthropic", "claude"):
|
|
243
|
+
self.ANTHROPIC_API_KEY = secret
|
|
244
|
+
env_key_name = ENV_ANTHROPIC_API_KEY
|
|
245
|
+
os.environ[ENV_ANTHROPIC_API_KEY] = key
|
|
246
|
+
elif provider_lower == "openrouter":
|
|
247
|
+
self.OPEN_ROUTER_API_KEY = secret
|
|
248
|
+
env_key_name = ENV_OPEN_ROUTER_API_KEY
|
|
249
|
+
os.environ[ENV_OPEN_ROUTER_API_KEY] = key
|
|
250
|
+
elif provider_lower == "xai":
|
|
251
|
+
self.XAI_API_KEY = secret
|
|
252
|
+
env_key_name = ENV_XAI_API_KEY
|
|
253
|
+
os.environ[ENV_XAI_API_KEY] = key
|
|
254
|
+
elif provider_lower in ("ocr", "vision", "google_vision"):
|
|
255
|
+
self.OCR_API_KEY = secret
|
|
256
|
+
self.VISION_API_KEY = secret
|
|
257
|
+
env_key_name = ENV_OCR_API_KEY
|
|
258
|
+
os.environ[ENV_OCR_API_KEY] = key
|
|
259
|
+
os.environ[ENV_VISION_API_KEY] = key
|
|
260
|
+
|
|
261
|
+
if persist_to_env and env_key_name:
|
|
262
|
+
target_env_files = [get_env_file()]
|
|
263
|
+
seen_paths = set()
|
|
264
|
+
for env_file in target_env_files:
|
|
265
|
+
try:
|
|
266
|
+
env_file.parent.mkdir(parents=True, exist_ok=True)
|
|
267
|
+
resolved = env_file.resolve()
|
|
268
|
+
if resolved in seen_paths:
|
|
269
|
+
continue
|
|
270
|
+
seen_paths.add(resolved)
|
|
271
|
+
|
|
272
|
+
lines = []
|
|
273
|
+
if env_file.exists():
|
|
274
|
+
lines = env_file.read_text(encoding="utf-8").splitlines()
|
|
275
|
+
|
|
276
|
+
# Determine keys to update
|
|
277
|
+
keys_to_update = [env_key_name]
|
|
278
|
+
if is_google_family_provider(provider_lower):
|
|
279
|
+
keys_to_update = [ENV_GEMINI_API_KEY, ENV_GOOGLE_API_KEY, ENV_GCP_API_KEY]
|
|
280
|
+
elif provider_lower in ("ocr", "vision", "google_vision"):
|
|
281
|
+
keys_to_update = [ENV_OCR_API_KEY, ENV_VISION_API_KEY]
|
|
282
|
+
|
|
283
|
+
new_lines = []
|
|
284
|
+
updated_set = set()
|
|
285
|
+
for line in lines:
|
|
286
|
+
replaced = False
|
|
287
|
+
for k in keys_to_update:
|
|
288
|
+
if (
|
|
289
|
+
line.startswith(f"{k}=")
|
|
290
|
+
or line.startswith(f"#{k}=")
|
|
291
|
+
or line.startswith(f"# {k}=")
|
|
292
|
+
):
|
|
293
|
+
new_lines.append(f"{k}={key}")
|
|
294
|
+
updated_set.add(k)
|
|
295
|
+
replaced = True
|
|
296
|
+
break
|
|
297
|
+
if not replaced:
|
|
298
|
+
new_lines.append(line)
|
|
299
|
+
|
|
300
|
+
# Ensure the primary key is present if not replaced
|
|
301
|
+
primary_key = keys_to_update[0]
|
|
302
|
+
if primary_key not in updated_set:
|
|
303
|
+
new_lines.append(f"{primary_key}={key}")
|
|
304
|
+
|
|
305
|
+
env_file.write_text("\n".join(new_lines) + "\n", encoding="utf-8")
|
|
306
|
+
except Exception as e:
|
|
307
|
+
logger.warning(f"Could not persist {env_key_name} to {env_file}: {e}")
|
|
308
|
+
|
|
309
|
+
|
|
310
|
+
# Singleton instance
|
|
311
|
+
settings = Settings()
|
|
312
|
+
|
|
313
|
+
# Synchronize DATA_ENGINE_DB_PATH in environment for external sub-processes/tools
|
|
314
|
+
if settings.DATA_ENGINE_DB_PATH:
|
|
315
|
+
os.environ[ENV_DATA_ENGINE_DB_PATH] = str(settings.DATA_ENGINE_DB_PATH)
|
|
@@ -0,0 +1,47 @@
|
|
|
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
|
+
RECURSION_LIMIT = 30000
|
|
16
|
+
|
|
17
|
+
# Optimization Constants
|
|
18
|
+
CHECKER_MAX_ITERATIONS = 20
|
|
19
|
+
VALIDATOR_POLL_TIMEOUT = 2.0
|
|
20
|
+
VALIDATOR_POLL_INTERVAL = 0.2
|
|
21
|
+
VALIDATOR_OCR_INTERVAL = 1.0
|
|
22
|
+
VALIDATOR_UI_HIERARCHY_TIMEOUT = 1.0
|
|
23
|
+
|
|
24
|
+
from google.genai import types as _genai_types
|
|
25
|
+
|
|
26
|
+
SAFETY_SETTINGS_BLOCK_NONE = [
|
|
27
|
+
_genai_types.SafetySetting(
|
|
28
|
+
category=_genai_types.HarmCategory.HARM_CATEGORY_HATE_SPEECH,
|
|
29
|
+
threshold=_genai_types.HarmBlockThreshold.BLOCK_NONE,
|
|
30
|
+
),
|
|
31
|
+
_genai_types.SafetySetting(
|
|
32
|
+
category=_genai_types.HarmCategory.HARM_CATEGORY_HARASSMENT,
|
|
33
|
+
threshold=_genai_types.HarmBlockThreshold.BLOCK_NONE,
|
|
34
|
+
),
|
|
35
|
+
_genai_types.SafetySetting(
|
|
36
|
+
category=_genai_types.HarmCategory.HARM_CATEGORY_SEXUALLY_EXPLICIT,
|
|
37
|
+
threshold=_genai_types.HarmBlockThreshold.BLOCK_NONE,
|
|
38
|
+
),
|
|
39
|
+
_genai_types.SafetySetting(
|
|
40
|
+
category=_genai_types.HarmCategory.HARM_CATEGORY_DANGEROUS_CONTENT,
|
|
41
|
+
threshold=_genai_types.HarmBlockThreshold.BLOCK_NONE,
|
|
42
|
+
),
|
|
43
|
+
_genai_types.SafetySetting(
|
|
44
|
+
category=_genai_types.HarmCategory.HARM_CATEGORY_CIVIC_INTEGRITY,
|
|
45
|
+
threshold=_genai_types.HarmBlockThreshold.BLOCK_NONE,
|
|
46
|
+
),
|
|
47
|
+
]
|
|
@@ -0,0 +1,289 @@
|
|
|
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
|
+
"""Runtime context shared by Artemis agents, tools and drivers for a single task run."""
|
|
16
|
+
|
|
17
|
+
from __future__ import annotations
|
|
18
|
+
|
|
19
|
+
import asyncio
|
|
20
|
+
|
|
21
|
+
from pathlib import Path
|
|
22
|
+
from typing import Any, Literal, TYPE_CHECKING
|
|
23
|
+
|
|
24
|
+
if TYPE_CHECKING:
|
|
25
|
+
from artemis.data_engine.engine import DataEngine
|
|
26
|
+
|
|
27
|
+
try:
|
|
28
|
+
from adbutils import AdbClient
|
|
29
|
+
except ImportError:
|
|
30
|
+
AdbClient = Any
|
|
31
|
+
|
|
32
|
+
from pydantic import BaseModel, ConfigDict, Field, PrivateAttr
|
|
33
|
+
from artemis.utils.video import detect_video_tools_enabled
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
from artemis.config import ExplorerConfig, LLMConfig, OutputterConfig
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
from third_party.mobile_use.context import (
|
|
40
|
+
AppLaunchResult,
|
|
41
|
+
DeviceContext,
|
|
42
|
+
DeviceClientAccessors,
|
|
43
|
+
DevicePlatform,
|
|
44
|
+
ExecutionSetupBase,
|
|
45
|
+
)
|
|
46
|
+
from third_party.mobile_use.utils.logger import get_logger
|
|
47
|
+
|
|
48
|
+
logger = get_logger(__name__)
|
|
49
|
+
|
|
50
|
+
|
|
51
|
+
class ExecutionSetup(ExecutionSetupBase):
|
|
52
|
+
"""Execution setup for a task."""
|
|
53
|
+
|
|
54
|
+
video_recording_tools_enabled: bool = Field(default_factory=detect_video_tools_enabled)
|
|
55
|
+
disable_checker: bool = False
|
|
56
|
+
"""Legacy master switch (compat alias): ``True`` disables BOTH the midway
|
|
57
|
+
checkpoints and the final check, regardless of the individual gates below.
|
|
58
|
+
Factory default is ``False``: the exit final review runs out of the box."""
|
|
59
|
+
disable_midway_checks: bool = True
|
|
60
|
+
"""Midway checkpoints are OFF by default (factory layering: final check on,
|
|
61
|
+
planner validation on, midway checks off)."""
|
|
62
|
+
disable_final_check: bool = False
|
|
63
|
+
checker_max_iterations: int = 20
|
|
64
|
+
final_check_max_attempts: int = 3
|
|
65
|
+
checkpoint_max_repairs: int = 2
|
|
66
|
+
max_concurrent_checkpoints: int = 3
|
|
67
|
+
checkpoint_timeout: float = 180.0
|
|
68
|
+
settlement_timeout: float = 120.0
|
|
69
|
+
assert_failure_policy: Literal["continue", "halt"] = "continue"
|
|
70
|
+
disable_device_probes: bool = False
|
|
71
|
+
disable_planner_validation: bool = False
|
|
72
|
+
enable_committee: bool = False
|
|
73
|
+
committee_debate_rounds: int = 2
|
|
74
|
+
disable_outputter: bool = False
|
|
75
|
+
outputter: OutputterConfig = Field(default_factory=OutputterConfig)
|
|
76
|
+
explorer: ExplorerConfig = Field(default_factory=ExplorerConfig)
|
|
77
|
+
explorer_versions: dict[str, str] = Field(default_factory=dict)
|
|
78
|
+
|
|
79
|
+
@property
|
|
80
|
+
def midway_checks_enabled(self) -> bool:
|
|
81
|
+
"""Midway checkpoint gate: honors both the individual switch and the
|
|
82
|
+
legacy ``disable_checker`` master alias."""
|
|
83
|
+
return not (self.disable_midway_checks or self.disable_checker)
|
|
84
|
+
|
|
85
|
+
@property
|
|
86
|
+
def final_check_enabled(self) -> bool:
|
|
87
|
+
"""Final review gate: honors both the individual switch and the legacy
|
|
88
|
+
``disable_checker`` master alias."""
|
|
89
|
+
return not (self.disable_final_check or self.disable_checker)
|
|
90
|
+
|
|
91
|
+
@property
|
|
92
|
+
def checks_enabled(self) -> bool:
|
|
93
|
+
"""True when any check gate is active (drives conditional prompt assembly)."""
|
|
94
|
+
return self.midway_checks_enabled or self.final_check_enabled
|
|
95
|
+
|
|
96
|
+
@property
|
|
97
|
+
def explorer_version(self) -> str:
|
|
98
|
+
return self.explorer.default_version
|
|
99
|
+
|
|
100
|
+
@property
|
|
101
|
+
def explorer_flash_mode(self) -> str:
|
|
102
|
+
return self.explorer.flash_mode
|
|
103
|
+
|
|
104
|
+
@property
|
|
105
|
+
def explorer_pro_mode(self) -> str:
|
|
106
|
+
return self.explorer.pro_mode
|
|
107
|
+
|
|
108
|
+
@property
|
|
109
|
+
def explorer_caching(self) -> bool | None:
|
|
110
|
+
return self.explorer.caching
|
|
111
|
+
|
|
112
|
+
|
|
113
|
+
class ArtemisContext(DeviceClientAccessors, BaseModel):
|
|
114
|
+
model_config = ConfigDict(
|
|
115
|
+
arbitrary_types_allowed=True,
|
|
116
|
+
extra="allow",
|
|
117
|
+
)
|
|
118
|
+
|
|
119
|
+
device: DeviceContext
|
|
120
|
+
llm_config: LLMConfig | None = None
|
|
121
|
+
agent_config: Any = None
|
|
122
|
+
adb_client: Any | None = None
|
|
123
|
+
ui_adb_client: Any | None = None
|
|
124
|
+
adb_task_registry: Any | None = None
|
|
125
|
+
"""Per-task ADB background-task registry (``artemis.tools.command_tool``).
|
|
126
|
+
Created lazily on first ADB tool use; killed at task end."""
|
|
127
|
+
|
|
128
|
+
execution_setup: ExecutionSetup | None = None
|
|
129
|
+
|
|
130
|
+
data_engine: DataEngine | None = None
|
|
131
|
+
planner_task: asyncio.Task[Any] | None = None
|
|
132
|
+
background_tasks: list[asyncio.Task[Any]] = Field(default_factory=list)
|
|
133
|
+
background_task_grace_period_seconds: float = 2.0
|
|
134
|
+
package_cache: dict[str, str | None] = Field(default_factory=dict)
|
|
135
|
+
background_jobs: dict[str, dict[str, Any]] = Field(default_factory=dict)
|
|
136
|
+
|
|
137
|
+
pending_checkpoints: list[Any] = Field(default_factory=list)
|
|
138
|
+
"""Queued :class:`~artemis.graph.checkpoints.PendingCheckpoint` entries.
|
|
139
|
+
``_process_plan_write`` only enqueues; ``execution_check_node`` spawns after
|
|
140
|
+
the turn's step is recorded so the evidence anchor points at a real step."""
|
|
141
|
+
checkpoint_tasks: dict[str, Any] = Field(default_factory=dict)
|
|
142
|
+
"""checkpoint_id -> (attempt_id, asyncio.Task). One in-flight attempt per
|
|
143
|
+
checkpoint; supersede harvests/cancels the old entry before replacing it."""
|
|
144
|
+
checkpoint_attempt_seq: dict[str, int] = Field(default_factory=dict)
|
|
145
|
+
"""checkpoint_id -> monotonically increasing attempt counter."""
|
|
146
|
+
checkpoint_repairs: dict[str, int] = Field(default_factory=dict)
|
|
147
|
+
"""checkpoint_id -> number of verify-fail repairs already applied."""
|
|
148
|
+
assert_halt: bool = False
|
|
149
|
+
"""Latched by an assert failure under ``assert_failure_policy='halt'``."""
|
|
150
|
+
final_check_attempts: int = 0
|
|
151
|
+
"""Number of final-check passes already executed at exit settlement."""
|
|
152
|
+
|
|
153
|
+
last_validated_plan: str | None = None
|
|
154
|
+
"""Ratchet baseline for planner validation: the last plan content whose
|
|
155
|
+
top-level milestones were validated (or the initial plan). Drift is always
|
|
156
|
+
judged against this, never against the immediately preceding write."""
|
|
157
|
+
pending_validated_plan: str | None = None
|
|
158
|
+
"""Plan content currently under async planner validation; becomes the new
|
|
159
|
+
baseline when the validator approves it."""
|
|
160
|
+
|
|
161
|
+
guidance_unprotected_checks: set[tuple[str, str]] = Field(default_factory=set)
|
|
162
|
+
"""``(kind, text)`` of every check line that existed in the task plan when a
|
|
163
|
+
mid-run user instruction arrived. User guidance outranks the plan's declared
|
|
164
|
+
standards, so these lines lose their machine restoration for the rest of the
|
|
165
|
+
run (the Operator may drop or reword them at any later write); check lines
|
|
166
|
+
added afterwards stay protected until the next instruction."""
|
|
167
|
+
guidance_retired_checks: set[tuple[str, str]] = Field(default_factory=set)
|
|
168
|
+
"""``(kind, text)`` of the check lines the Operator actually dropped under
|
|
169
|
+
that waiver. A verdict recorded for such a line before it was retired is a
|
|
170
|
+
result for a requirement the user withdrew: the run outcome reports it as
|
|
171
|
+
retired instead of counting it as a pass or failure."""
|
|
172
|
+
|
|
173
|
+
_genai_client: Any | None = PrivateAttr(default=None)
|
|
174
|
+
_video_blackboard: Any | None = PrivateAttr(default=None)
|
|
175
|
+
_video_circuit_breaker: Any | None = PrivateAttr(default=None)
|
|
176
|
+
_mobile_controller: Any | None = PrivateAttr(default=None)
|
|
177
|
+
_active_driver: Any | None = PrivateAttr(default=None)
|
|
178
|
+
"""Cached device driver singleton (artemis.drivers.factory.get_driver)."""
|
|
179
|
+
mcp_client_ctx: Any | None = None
|
|
180
|
+
mcp_session: Any | None = None
|
|
181
|
+
action_session: Any | None = None
|
|
182
|
+
"""In-process unified action MCP session (artemis.mcp.action_session)."""
|
|
183
|
+
actuator: Any | None = None
|
|
184
|
+
"""Optional actuator backend override (artemis.mcp.actuators). ``None`` selects
|
|
185
|
+
the default AdbActuator; installing e.g. a robot-arm backend happens here."""
|
|
186
|
+
|
|
187
|
+
step_memory: Any | None = None
|
|
188
|
+
"""Shared :class:`~artemis.memory.step_memory.StepMemoryService` composition
|
|
189
|
+
root (history redesign §6.2). Created on first use via
|
|
190
|
+
``artemis.memory.ensure_step_memory``; Flash and Pro profiles share one
|
|
191
|
+
instance so summary jobs, retries, and flush semantics are unified."""
|
|
192
|
+
|
|
193
|
+
transcript_ledger: Any | None = None
|
|
194
|
+
"""Session :class:`~artemis.memory.transcript.TranscriptLedger` for the Pro
|
|
195
|
+
operator's transcript prompt path (``agent.memory.transcript.enabled``).
|
|
196
|
+
Lives beside ``step_memory``; the State never carries message payloads."""
|
|
197
|
+
|
|
198
|
+
async def __aenter__(self) -> ArtemisContext:
|
|
199
|
+
return self
|
|
200
|
+
|
|
201
|
+
async def __aexit__(self, exc_type, exc_val, exc_tb):
|
|
202
|
+
# Close the in-process action session before draining background tasks so its
|
|
203
|
+
# owner task exits cleanly rather than being cancelled below.
|
|
204
|
+
if self.action_session is not None:
|
|
205
|
+
try:
|
|
206
|
+
await self.action_session.aclose()
|
|
207
|
+
except Exception as exc:
|
|
208
|
+
logger.debug(f"Action session close failed; skipped: {exc}", exc_info=True)
|
|
209
|
+
finally:
|
|
210
|
+
self.action_session = None
|
|
211
|
+
|
|
212
|
+
# Drain in-flight step-summary jobs before the generic background-task
|
|
213
|
+
# sweep: the shared service owns its own bounded flush semantics. On an
|
|
214
|
+
# exceptional exit the flush is skipped and the jobs are cancelled below.
|
|
215
|
+
if self.step_memory is not None and callable(getattr(self.step_memory, "flush", None)):
|
|
216
|
+
try:
|
|
217
|
+
if exc_type is None:
|
|
218
|
+
await self.step_memory.flush()
|
|
219
|
+
else:
|
|
220
|
+
await self.step_memory.flush(timeout_seconds=0.0)
|
|
221
|
+
except Exception as exc:
|
|
222
|
+
logger.debug(f"Step-memory flush failed; skipped: {exc}", exc_info=True)
|
|
223
|
+
|
|
224
|
+
# Drain in-flight chunk capsule jobs the same way (M3): the chunk
|
|
225
|
+
# manager persists any harvested capsules so the DB copy is complete
|
|
226
|
+
# even though the frozen transcript text is not re-rendered.
|
|
227
|
+
chunker = getattr(self.transcript_ledger, "chunker", None)
|
|
228
|
+
if chunker is not None and callable(getattr(chunker, "flush", None)):
|
|
229
|
+
try:
|
|
230
|
+
if exc_type is None:
|
|
231
|
+
await chunker.flush()
|
|
232
|
+
else:
|
|
233
|
+
await chunker.flush(timeout_seconds=0.0)
|
|
234
|
+
except Exception as exc:
|
|
235
|
+
logger.debug(f"History chunk flush failed; skipped: {exc}", exc_info=True)
|
|
236
|
+
|
|
237
|
+
if self.background_tasks:
|
|
238
|
+
tasks = list(self.background_tasks)
|
|
239
|
+
pending_tasks = [task for task in tasks if not task.done()]
|
|
240
|
+
grace_period = (
|
|
241
|
+
max(0.0, self.background_task_grace_period_seconds) if exc_type is None else 0.0
|
|
242
|
+
)
|
|
243
|
+
|
|
244
|
+
if pending_tasks and grace_period > 0:
|
|
245
|
+
logger.info(
|
|
246
|
+
f"Draining {len(pending_tasks)} background tasks for up to "
|
|
247
|
+
f"{grace_period:.1f}s..."
|
|
248
|
+
)
|
|
249
|
+
_, pending_tasks = await asyncio.wait(
|
|
250
|
+
pending_tasks,
|
|
251
|
+
timeout=grace_period,
|
|
252
|
+
)
|
|
253
|
+
|
|
254
|
+
if pending_tasks:
|
|
255
|
+
logger.info(
|
|
256
|
+
f"Cancelling {len(pending_tasks)} unfinished background tasks; "
|
|
257
|
+
"primary automation is already complete."
|
|
258
|
+
)
|
|
259
|
+
for task in pending_tasks:
|
|
260
|
+
task.cancel()
|
|
261
|
+
|
|
262
|
+
results = await asyncio.gather(*tasks, return_exceptions=True)
|
|
263
|
+
for task, result in zip(tasks, results):
|
|
264
|
+
if isinstance(result, Exception):
|
|
265
|
+
logger.error(
|
|
266
|
+
f"Background task {task.get_name()} failed: {result}",
|
|
267
|
+
exc_info=result,
|
|
268
|
+
)
|
|
269
|
+
self.background_tasks.clear()
|
|
270
|
+
|
|
271
|
+
if self.mcp_client_ctx:
|
|
272
|
+
try:
|
|
273
|
+
await self.mcp_client_ctx.__aexit__(exc_type, exc_val, exc_tb)
|
|
274
|
+
except Exception as exc:
|
|
275
|
+
logger.debug(f"MCP client context exit failed; skipped: {exc}", exc_info=True)
|
|
276
|
+
finally:
|
|
277
|
+
self.mcp_client_ctx = None
|
|
278
|
+
self.mcp_session = None
|
|
279
|
+
|
|
280
|
+
if self.data_engine:
|
|
281
|
+
try:
|
|
282
|
+
await self.data_engine.shutdown()
|
|
283
|
+
except Exception as exc:
|
|
284
|
+
logger.debug(f"DataEngine shutdown failed; skipped: {exc}", exc_info=True)
|
|
285
|
+
|
|
286
|
+
|
|
287
|
+
from artemis.data_engine.engine import DataEngine
|
|
288
|
+
|
|
289
|
+
ArtemisContext.model_rebuild()
|
|
@@ -0,0 +1,41 @@
|
|
|
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
|
+
"""Backward compatibility layer for legacy artemis.controllers module.
|
|
16
|
+
All new development should use artemis.drivers instead.
|
|
17
|
+
"""
|
|
18
|
+
|
|
19
|
+
from artemis.drivers.base import BaseDeviceDriver, KeyCode, ScreenData, SwipeDirection
|
|
20
|
+
from artemis.drivers.factory import create_driver, get_driver
|
|
21
|
+
from artemis.drivers.types import (
|
|
22
|
+
Bounds,
|
|
23
|
+
CoordinatesSelectorRequest,
|
|
24
|
+
PercentagesSelectorRequest,
|
|
25
|
+
SwipeRequest,
|
|
26
|
+
TapOutput,
|
|
27
|
+
)
|
|
28
|
+
|
|
29
|
+
__all__ = [
|
|
30
|
+
"BaseDeviceDriver",
|
|
31
|
+
"KeyCode",
|
|
32
|
+
"ScreenData",
|
|
33
|
+
"SwipeDirection",
|
|
34
|
+
"Bounds",
|
|
35
|
+
"CoordinatesSelectorRequest",
|
|
36
|
+
"PercentagesSelectorRequest",
|
|
37
|
+
"SwipeRequest",
|
|
38
|
+
"TapOutput",
|
|
39
|
+
"create_driver",
|
|
40
|
+
"get_driver",
|
|
41
|
+
]
|