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,109 @@
|
|
|
1
|
+
# Copyright 2025-2026 Minitap, Inc.
|
|
2
|
+
# Modifications Copyright 2026 Google LLC
|
|
3
|
+
#
|
|
4
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
|
5
|
+
# you may not use this file except in compliance with the License.
|
|
6
|
+
# You may obtain a copy of the License at
|
|
7
|
+
#
|
|
8
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
|
9
|
+
#
|
|
10
|
+
# Unless required by applicable law or agreed to in writing, software
|
|
11
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
|
12
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
13
|
+
# See the License for the specific language governing permissions and
|
|
14
|
+
# limitations under the License.
|
|
15
|
+
#
|
|
16
|
+
# Originally from mobile-use (https://github.com/minitap-ai/mobile-use).
|
|
17
|
+
# See third_party/mobile_use/METADATA for the upstream source and local modifications.
|
|
18
|
+
|
|
19
|
+
"""Exceptions for the ARTEMIS SDK.
|
|
20
|
+
|
|
21
|
+
This module defines the exception hierarchy used throughout the Artemis SDK.
|
|
22
|
+
"""
|
|
23
|
+
|
|
24
|
+
from typing import Literal
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
class ArtemisError(Exception):
|
|
28
|
+
"""Base exception class for all ARTEMIS SDK exceptions."""
|
|
29
|
+
|
|
30
|
+
def __init__(self, message="An error occurred in the Artemis SDK"):
|
|
31
|
+
self.message = message
|
|
32
|
+
super().__init__(self.message)
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
class DeviceError(ArtemisError):
|
|
36
|
+
"""Exception raised for errors related to mobile devices."""
|
|
37
|
+
|
|
38
|
+
def __init__(self, message="A device-related error occurred"):
|
|
39
|
+
super().__init__(message)
|
|
40
|
+
|
|
41
|
+
|
|
42
|
+
class DeviceNotFoundError(DeviceError):
|
|
43
|
+
"""Exception raised when no mobile device is found."""
|
|
44
|
+
|
|
45
|
+
def __init__(self, message="No mobile device found"):
|
|
46
|
+
super().__init__(message)
|
|
47
|
+
|
|
48
|
+
|
|
49
|
+
class ServerError(ArtemisError):
|
|
50
|
+
"""Exception raised for errors related to ARTEMIS servers."""
|
|
51
|
+
|
|
52
|
+
def __init__(self, message="A server-related error occurred"):
|
|
53
|
+
super().__init__(message)
|
|
54
|
+
|
|
55
|
+
|
|
56
|
+
class ServerStartupError(ServerError):
|
|
57
|
+
"""Exception raised when ARTEMIS servers fail to start."""
|
|
58
|
+
|
|
59
|
+
def __init__(self, server_name=None, message=None):
|
|
60
|
+
if server_name and not message:
|
|
61
|
+
message = f"Failed to start {server_name}"
|
|
62
|
+
elif not message:
|
|
63
|
+
message = "Failed to start Artemis servers"
|
|
64
|
+
super().__init__(message)
|
|
65
|
+
self.server_name = server_name
|
|
66
|
+
|
|
67
|
+
|
|
68
|
+
class AgentError(ArtemisError):
|
|
69
|
+
"""Exception raised for errors related to the ARTEMIS agent."""
|
|
70
|
+
|
|
71
|
+
def __init__(self, message="An agent-related error occurred"):
|
|
72
|
+
super().__init__(message)
|
|
73
|
+
|
|
74
|
+
|
|
75
|
+
class AgentNotInitializedError(AgentError):
|
|
76
|
+
"""Exception raised when attempting operations on an uninitialized agent."""
|
|
77
|
+
|
|
78
|
+
def __init__(self, message="Agent is not initialized. Call init() first"):
|
|
79
|
+
super().__init__(message)
|
|
80
|
+
|
|
81
|
+
|
|
82
|
+
class AgentTaskRequestError(AgentError):
|
|
83
|
+
"""Exception raised when a requested task is invalid."""
|
|
84
|
+
|
|
85
|
+
def __init__(self, message="An agent task-related error occurred"):
|
|
86
|
+
super().__init__(message)
|
|
87
|
+
|
|
88
|
+
|
|
89
|
+
class AgentProfileNotFoundError(AgentTaskRequestError):
|
|
90
|
+
"""Exception raised when an agent profile is not found."""
|
|
91
|
+
|
|
92
|
+
def __init__(self, profile_name: str):
|
|
93
|
+
super().__init__(f"Agent profile {profile_name} not found")
|
|
94
|
+
|
|
95
|
+
|
|
96
|
+
EXECUTABLES = Literal["adb"]
|
|
97
|
+
|
|
98
|
+
|
|
99
|
+
class ExecutableNotFoundError(ArtemisError):
|
|
100
|
+
"""Exception raised when a required executable is not found."""
|
|
101
|
+
|
|
102
|
+
def __init__(self, executable_name: EXECUTABLES):
|
|
103
|
+
install_instructions: dict[EXECUTABLES, str] = {
|
|
104
|
+
"adb": "https://developer.android.com/tools/adb",
|
|
105
|
+
}
|
|
106
|
+
message = f"Required executable '{executable_name}' not found in PATH."
|
|
107
|
+
if executable_name in install_instructions:
|
|
108
|
+
message += f"\nTo install it, please visit: {install_instructions[executable_name]}"
|
|
109
|
+
super().__init__(message)
|
|
@@ -0,0 +1,234 @@
|
|
|
1
|
+
# Copyright 2025-2026 Minitap, Inc.
|
|
2
|
+
# Modifications Copyright 2026 Google LLC
|
|
3
|
+
#
|
|
4
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
|
5
|
+
# you may not use this file except in compliance with the License.
|
|
6
|
+
# You may obtain a copy of the License at
|
|
7
|
+
#
|
|
8
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
|
9
|
+
#
|
|
10
|
+
# Unless required by applicable law or agreed to in writing, software
|
|
11
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
|
12
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
13
|
+
# See the License for the specific language governing permissions and
|
|
14
|
+
# limitations under the License.
|
|
15
|
+
#
|
|
16
|
+
# Originally from mobile-use (https://github.com/minitap-ai/mobile-use).
|
|
17
|
+
# See third_party/mobile_use/METADATA for the upstream source and local modifications.
|
|
18
|
+
|
|
19
|
+
"""Task-related type definitions for the ARTEMIS SDK."""
|
|
20
|
+
|
|
21
|
+
from collections.abc import Callable, Coroutine
|
|
22
|
+
from datetime import datetime
|
|
23
|
+
from pathlib import Path
|
|
24
|
+
from typing import Any, Generic, Literal, TypeVar, overload
|
|
25
|
+
|
|
26
|
+
from artemis.config import LLMConfig, get_default_llm_config
|
|
27
|
+
from artemis.constants import RECURSION_LIMIT
|
|
28
|
+
from artemis.context import DeviceContext
|
|
29
|
+
from artemis.sdk.utils import load_llm_config_override
|
|
30
|
+
from pydantic import BaseModel, Field
|
|
31
|
+
from artemis.utils.cython_compat import CyFunctionDetector
|
|
32
|
+
|
|
33
|
+
TaskRunStatus = Literal["pending", "running", "completed", "failed", "cancelled"]
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
class AgentProfile(BaseModel):
|
|
37
|
+
"""Represents a ARTEMIS agent profile.
|
|
38
|
+
|
|
39
|
+
Attributes:
|
|
40
|
+
name: Name of the agent - used to reference the agent when running
|
|
41
|
+
tasks.
|
|
42
|
+
llm_config: LLM configuration for the agent.
|
|
43
|
+
"""
|
|
44
|
+
|
|
45
|
+
model_config = {"ignored_types": (CyFunctionDetector,)}
|
|
46
|
+
|
|
47
|
+
name: str
|
|
48
|
+
llm_config: LLMConfig = Field(default_factory=get_default_llm_config)
|
|
49
|
+
|
|
50
|
+
@overload
|
|
51
|
+
def __init__(self, *, name: str, llm_config: LLMConfig): ...
|
|
52
|
+
|
|
53
|
+
@overload
|
|
54
|
+
def __init__(self, *, name: str, from_file: str): ...
|
|
55
|
+
|
|
56
|
+
def __init__(
|
|
57
|
+
self,
|
|
58
|
+
*,
|
|
59
|
+
name: str,
|
|
60
|
+
llm_config: LLMConfig | None = None,
|
|
61
|
+
from_file: str | None = None,
|
|
62
|
+
**kwargs,
|
|
63
|
+
):
|
|
64
|
+
kwargs["name"] = name
|
|
65
|
+
if from_file:
|
|
66
|
+
kwargs["llm_config"] = load_llm_config_override(Path(from_file))
|
|
67
|
+
elif llm_config:
|
|
68
|
+
kwargs["llm_config"] = llm_config
|
|
69
|
+
else:
|
|
70
|
+
raise ValueError("Either llm_config or from_file must be provided")
|
|
71
|
+
super().__init__(**kwargs)
|
|
72
|
+
|
|
73
|
+
def __str__(self):
|
|
74
|
+
return f"Profile {self.name}:\n{self.llm_config}"
|
|
75
|
+
|
|
76
|
+
|
|
77
|
+
T = TypeVar("T", bound=BaseModel)
|
|
78
|
+
TOutput = TypeVar("TOutput", bound=BaseModel | None)
|
|
79
|
+
|
|
80
|
+
|
|
81
|
+
class TaskRequestBase(BaseModel):
|
|
82
|
+
"""Defines common parameters of a mobile automation task request."""
|
|
83
|
+
|
|
84
|
+
model_config = {"ignored_types": (CyFunctionDetector,)}
|
|
85
|
+
max_steps: int = RECURSION_LIMIT
|
|
86
|
+
record_trace: bool = True
|
|
87
|
+
trace_path: Path = Path("traces")
|
|
88
|
+
llm_output_path: Path | None = None
|
|
89
|
+
|
|
90
|
+
|
|
91
|
+
class TaskRequestCommon(TaskRequestBase):
|
|
92
|
+
"""Defines common parameters for any task request."""
|
|
93
|
+
|
|
94
|
+
model_config = {"ignored_types": (CyFunctionDetector,)}
|
|
95
|
+
max_steps: int = RECURSION_LIMIT
|
|
96
|
+
locked_app_package: str | None = None
|
|
97
|
+
app_path: Path | None = None
|
|
98
|
+
"""Path to an app to install before running the task.
|
|
99
|
+
|
|
100
|
+
For Android: Path to an APK file.
|
|
101
|
+
|
|
102
|
+
The app will be installed automatically before the task starts.
|
|
103
|
+
"""
|
|
104
|
+
|
|
105
|
+
|
|
106
|
+
class TaskRequest(TaskRequestCommon, Generic[TOutput]):
|
|
107
|
+
"""Defines the format of a mobile automation task request.
|
|
108
|
+
|
|
109
|
+
Attributes:
|
|
110
|
+
goal: Natural language description of the goal to achieve
|
|
111
|
+
profile: Optional agent profile to use for executing the task
|
|
112
|
+
task_name: Optional name for the task
|
|
113
|
+
output_description: Optional natural language description of expected
|
|
114
|
+
output format
|
|
115
|
+
output_format: Optional pydantic model for the output format of the task
|
|
116
|
+
max_steps: Maximum number of steps the agent can take (default: 20)
|
|
117
|
+
record_trace: Whether to record a trace (screenshots, actions) of the
|
|
118
|
+
execution (default: False)
|
|
119
|
+
trace_path: Directory path to save trace data if recording is enabled
|
|
120
|
+
llm_output_path: Path to save LLM output data
|
|
121
|
+
"""
|
|
122
|
+
|
|
123
|
+
model_config = {"ignored_types": (CyFunctionDetector,)}
|
|
124
|
+
goal: str
|
|
125
|
+
profile: str | None = None
|
|
126
|
+
task_name: str | None = None
|
|
127
|
+
output_description: str | None = None
|
|
128
|
+
output_format: type[TOutput] | None = None
|
|
129
|
+
enable_remote_tracing: bool = False
|
|
130
|
+
|
|
131
|
+
|
|
132
|
+
class TaskResult(BaseModel):
|
|
133
|
+
"""Result of a mobile automation task.
|
|
134
|
+
|
|
135
|
+
Attributes:
|
|
136
|
+
content: Raw result content (could be text or structured data)
|
|
137
|
+
error: Error message if the task failed
|
|
138
|
+
execution_time_seconds: How long the task took to execute
|
|
139
|
+
steps_taken: Number of steps the agent took to complete the task
|
|
140
|
+
"""
|
|
141
|
+
|
|
142
|
+
model_config = {"ignored_types": (CyFunctionDetector,)}
|
|
143
|
+
content: Any = None
|
|
144
|
+
error: str | None = None
|
|
145
|
+
execution_time_seconds: float
|
|
146
|
+
steps_taken: int
|
|
147
|
+
|
|
148
|
+
def get_as_model(self, model_class: type[T]) -> T:
|
|
149
|
+
"""Parse the content into a Pydantic model instance.
|
|
150
|
+
|
|
151
|
+
Args:
|
|
152
|
+
model_class: The Pydantic model class to parse the data into
|
|
153
|
+
|
|
154
|
+
Returns:
|
|
155
|
+
An instance of the specified model class
|
|
156
|
+
|
|
157
|
+
Raises:
|
|
158
|
+
ValueError: If content is None or not compatible with the model
|
|
159
|
+
"""
|
|
160
|
+
if self.content is None:
|
|
161
|
+
raise ValueError("No content available to parse into a model")
|
|
162
|
+
if isinstance(self.content, model_class):
|
|
163
|
+
return self.content
|
|
164
|
+
return model_class.model_validate(self.content)
|
|
165
|
+
|
|
166
|
+
|
|
167
|
+
class Task(BaseModel):
|
|
168
|
+
"""A mobile automation task to be executed.
|
|
169
|
+
|
|
170
|
+
Attributes:
|
|
171
|
+
id: Unique identifier for the task
|
|
172
|
+
device: Information about the target device
|
|
173
|
+
status: Current status of the task execution
|
|
174
|
+
request: User task request
|
|
175
|
+
created_at: ISO timestamp when the task was created
|
|
176
|
+
ended_at: ISO timestamp when the task ended
|
|
177
|
+
"""
|
|
178
|
+
|
|
179
|
+
model_config = {"ignored_types": (CyFunctionDetector,)}
|
|
180
|
+
id: str
|
|
181
|
+
device: DeviceContext
|
|
182
|
+
status: TaskRunStatus
|
|
183
|
+
status_message: str | None = None
|
|
184
|
+
on_status_changed: Callable[[TaskRunStatus, str | None, Any | None], Coroutine] | None = None
|
|
185
|
+
request: TaskRequest
|
|
186
|
+
created_at: datetime
|
|
187
|
+
ended_at: datetime | None = None
|
|
188
|
+
result: TaskResult | None = None
|
|
189
|
+
|
|
190
|
+
async def finalize(
|
|
191
|
+
self,
|
|
192
|
+
content: Any | None = None,
|
|
193
|
+
state: dict | None = None,
|
|
194
|
+
error: str | None = None,
|
|
195
|
+
cancelled: bool = False,
|
|
196
|
+
):
|
|
197
|
+
new_status: TaskRunStatus = "completed" if error is None else "failed"
|
|
198
|
+
if new_status == "failed" and cancelled:
|
|
199
|
+
new_status = "cancelled"
|
|
200
|
+
message = "Task completed successfully"
|
|
201
|
+
if new_status == "failed":
|
|
202
|
+
message = "Task failed" + (f": {error}" if error else "")
|
|
203
|
+
elif new_status == "cancelled":
|
|
204
|
+
message = "Task cancelled" + (f": {error}" if error else "")
|
|
205
|
+
await self.set_status(status=new_status, message=message, output=content or error)
|
|
206
|
+
self.ended_at = datetime.now()
|
|
207
|
+
|
|
208
|
+
duration = self.ended_at - self.created_at
|
|
209
|
+
steps_taken = -1
|
|
210
|
+
if state is not None:
|
|
211
|
+
metadata = state.get("metadata", None)
|
|
212
|
+
if metadata:
|
|
213
|
+
steps_taken = metadata.get("step_count", -1)
|
|
214
|
+
|
|
215
|
+
self.result = TaskResult(
|
|
216
|
+
content=content,
|
|
217
|
+
error=error,
|
|
218
|
+
execution_time_seconds=duration.total_seconds(),
|
|
219
|
+
steps_taken=steps_taken,
|
|
220
|
+
)
|
|
221
|
+
|
|
222
|
+
def get_name(self) -> str:
|
|
223
|
+
return self.request.task_name or self.id
|
|
224
|
+
|
|
225
|
+
async def set_status(
|
|
226
|
+
self,
|
|
227
|
+
status: TaskRunStatus,
|
|
228
|
+
message: str | None = None,
|
|
229
|
+
output: Any | None = None,
|
|
230
|
+
):
|
|
231
|
+
self.status = status
|
|
232
|
+
self.status_message = message
|
|
233
|
+
if self.on_status_changed:
|
|
234
|
+
await self.on_status_changed(status, message, output)
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
# Copyright 2025-2026 Minitap, Inc.
|
|
2
|
+
# Modifications Copyright 2026 Google LLC
|
|
3
|
+
#
|
|
4
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
|
5
|
+
# you may not use this file except in compliance with the License.
|
|
6
|
+
# You may obtain a copy of the License at
|
|
7
|
+
#
|
|
8
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
|
9
|
+
#
|
|
10
|
+
# Unless required by applicable law or agreed to in writing, software
|
|
11
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
|
12
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
13
|
+
# See the License for the specific language governing permissions and
|
|
14
|
+
# limitations under the License.
|
|
15
|
+
#
|
|
16
|
+
# Originally from mobile-use (https://github.com/minitap-ai/mobile-use).
|
|
17
|
+
# See third_party/mobile_use/METADATA for the upstream source and local modifications.
|
|
18
|
+
|
|
19
|
+
"""Pytest fixtures for agent node tests (upstream ``agents/outputter/test_outputter.py``).
|
|
20
|
+
|
|
21
|
+
Test-only: imports pytest, so it must not be imported by runtime code.
|
|
22
|
+
"""
|
|
23
|
+
|
|
24
|
+
from unittest.mock import Mock
|
|
25
|
+
|
|
26
|
+
import pytest
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
class DummyState:
|
|
30
|
+
def __init__(self, messages, initial_goal, operator_raw_data=None):
|
|
31
|
+
self.messages = messages
|
|
32
|
+
self.initial_goal = initial_goal
|
|
33
|
+
self.operator_raw_data = operator_raw_data
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
@pytest.fixture
|
|
37
|
+
def mock_context():
|
|
38
|
+
"""Create a properly mocked context with all required fields."""
|
|
39
|
+
from artemis.config import LLM
|
|
40
|
+
from artemis.context import ArtemisContext
|
|
41
|
+
|
|
42
|
+
ctx = Mock(spec=ArtemisContext)
|
|
43
|
+
ctx.llm_config = {
|
|
44
|
+
"planner": LLM(provider="openai", model="gpt-5-nano"),
|
|
45
|
+
"operator": LLM(provider="openai", model="gpt-5-nano"),
|
|
46
|
+
"validator": LLM(provider="openai", model="gpt-5-nano"),
|
|
47
|
+
}
|
|
48
|
+
ctx.device = Mock()
|
|
49
|
+
ctx.data_engine = None
|
|
50
|
+
return ctx
|
|
51
|
+
|
|
52
|
+
|
|
53
|
+
@pytest.fixture
|
|
54
|
+
def mock_state():
|
|
55
|
+
"""Create a mock state with test data."""
|
|
56
|
+
return DummyState(
|
|
57
|
+
messages=[],
|
|
58
|
+
initial_goal="Find a green product on my website",
|
|
59
|
+
)
|
|
@@ -0,0 +1,13 @@
|
|
|
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.
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
# Copyright 2025-2026 Minitap, Inc.
|
|
2
|
+
# Modifications Copyright 2026 Google LLC
|
|
3
|
+
#
|
|
4
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
|
5
|
+
# you may not use this file except in compliance with the License.
|
|
6
|
+
# You may obtain a copy of the License at
|
|
7
|
+
#
|
|
8
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
|
9
|
+
#
|
|
10
|
+
# Unless required by applicable law or agreed to in writing, software
|
|
11
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
|
12
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
13
|
+
# See the License for the specific language governing permissions and
|
|
14
|
+
# limitations under the License.
|
|
15
|
+
#
|
|
16
|
+
# Originally from mobile-use (https://github.com/minitap-ai/mobile-use).
|
|
17
|
+
# See third_party/mobile_use/METADATA for the upstream source and local modifications.
|
|
18
|
+
|
|
19
|
+
"""Instantiate LangChain tools from tool wrappers."""
|
|
20
|
+
|
|
21
|
+
from collections.abc import Callable
|
|
22
|
+
|
|
23
|
+
from langchain_core.tools import BaseTool
|
|
24
|
+
|
|
25
|
+
from artemis.context import ArtemisContext
|
|
26
|
+
from third_party.mobile_use.tools.tool_wrapper import CompositeToolWrapper, ToolWrapper
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
def get_tools_from_wrappers(
|
|
30
|
+
ctx: "ArtemisContext",
|
|
31
|
+
wrappers: list[ToolWrapper],
|
|
32
|
+
wrap_tool: Callable[[BaseTool], BaseTool] | None = None,
|
|
33
|
+
) -> list[BaseTool]:
|
|
34
|
+
"""Instantiate and wrap LangChain tools from a list of ToolWrappers."""
|
|
35
|
+
tools: list[BaseTool] = []
|
|
36
|
+
for wrapper in wrappers:
|
|
37
|
+
if wrapper.is_available_fn is not None and not wrapper.is_available_fn(ctx):
|
|
38
|
+
continue
|
|
39
|
+
if isinstance(wrapper, CompositeToolWrapper):
|
|
40
|
+
new_tools = wrapper.composite_tools_fn_getter(ctx)
|
|
41
|
+
else:
|
|
42
|
+
new_tools = [wrapper.tool_fn_getter(ctx)]
|
|
43
|
+
tools.extend(wrap_tool(t) if wrap_tool else t for t in new_tools)
|
|
44
|
+
return tools
|
|
@@ -0,0 +1,13 @@
|
|
|
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.
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
# Copyright 2025-2026 Minitap, Inc.
|
|
2
|
+
# Modifications Copyright 2026 Google LLC
|
|
3
|
+
#
|
|
4
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
|
5
|
+
# you may not use this file except in compliance with the License.
|
|
6
|
+
# You may obtain a copy of the License at
|
|
7
|
+
#
|
|
8
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
|
9
|
+
#
|
|
10
|
+
# Unless required by applicable law or agreed to in writing, software
|
|
11
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
|
12
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
13
|
+
# See the License for the specific language governing permissions and
|
|
14
|
+
# limitations under the License.
|
|
15
|
+
#
|
|
16
|
+
# Originally from mobile-use (https://github.com/minitap-ai/mobile-use).
|
|
17
|
+
# See third_party/mobile_use/METADATA for the upstream source and local modifications.
|
|
18
|
+
|
|
19
|
+
"""Resolve a natural-language app name to an installed package via the Hopper agent."""
|
|
20
|
+
|
|
21
|
+
from artemis.context import ArtemisContext
|
|
22
|
+
from third_party.mobile_use.agents.hopper.hopper import HopperOutput, hopper
|
|
23
|
+
from third_party.mobile_use.controllers.platform_specific_commands_controller import (
|
|
24
|
+
list_packages_async,
|
|
25
|
+
)
|
|
26
|
+
from third_party.mobile_use.utils.logger import get_logger
|
|
27
|
+
|
|
28
|
+
logger = get_logger(__name__)
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
LAUNCH_APP_DOCSTRING = (
|
|
32
|
+
"[ACTION] Finds and launches an application on the device using its natural language name."
|
|
33
|
+
)
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
async def find_package(ctx: ArtemisContext, app_name: str, use_fallback: bool = True) -> str | None:
|
|
37
|
+
"""Finds the package name for a given application name.
|
|
38
|
+
|
|
39
|
+
Returns None if package not found or on error.
|
|
40
|
+
"""
|
|
41
|
+
package_cache = getattr(ctx, "package_cache", None)
|
|
42
|
+
if package_cache is None or not isinstance(package_cache, dict):
|
|
43
|
+
try:
|
|
44
|
+
ctx.package_cache = {}
|
|
45
|
+
package_cache = ctx.package_cache
|
|
46
|
+
except Exception: # pylint: disable=broad-exception-caught
|
|
47
|
+
package_cache = {}
|
|
48
|
+
|
|
49
|
+
if isinstance(package_cache, dict) and app_name in package_cache:
|
|
50
|
+
logger.info(f"Cache hit for app '{app_name}': {package_cache[app_name]}")
|
|
51
|
+
return package_cache[app_name]
|
|
52
|
+
|
|
53
|
+
try:
|
|
54
|
+
all_packages = await list_packages_async(ctx=ctx)
|
|
55
|
+
package_set = {p.strip() for p in all_packages.split("\n") if p.strip()}
|
|
56
|
+
|
|
57
|
+
# Fast path: If app_name is already directly an installed package name
|
|
58
|
+
if app_name in package_set:
|
|
59
|
+
if isinstance(package_cache, dict):
|
|
60
|
+
package_cache[app_name] = app_name
|
|
61
|
+
return app_name
|
|
62
|
+
|
|
63
|
+
hopper_output: HopperOutput = await hopper(
|
|
64
|
+
ctx=ctx,
|
|
65
|
+
request=(f"I'm looking for the package name of the following app: '{app_name}'"),
|
|
66
|
+
data=all_packages,
|
|
67
|
+
use_fallback=use_fallback,
|
|
68
|
+
)
|
|
69
|
+
if not hopper_output.found or not hopper_output.output:
|
|
70
|
+
if isinstance(package_cache, dict):
|
|
71
|
+
package_cache[app_name] = None
|
|
72
|
+
return None
|
|
73
|
+
|
|
74
|
+
package_name = hopper_output.output.strip()
|
|
75
|
+
if package_name not in package_set:
|
|
76
|
+
logger.warning(
|
|
77
|
+
f"Hopper returned package '{package_name}' for '{app_name}', "
|
|
78
|
+
"but it is NOT physically installed on the device!"
|
|
79
|
+
)
|
|
80
|
+
if isinstance(package_cache, dict):
|
|
81
|
+
package_cache[app_name] = None
|
|
82
|
+
return None
|
|
83
|
+
|
|
84
|
+
if isinstance(package_cache, dict):
|
|
85
|
+
package_cache[app_name] = package_name
|
|
86
|
+
return package_name
|
|
87
|
+
except Exception as e: # pylint: disable=broad-exception-caught
|
|
88
|
+
logger.error(f"Failed to find package for '{app_name}': {e}")
|
|
89
|
+
return None
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
# Copyright 2025-2026 Minitap, Inc.
|
|
2
|
+
# Modifications Copyright 2026 Google LLC
|
|
3
|
+
#
|
|
4
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
|
5
|
+
# you may not use this file except in compliance with the License.
|
|
6
|
+
# You may obtain a copy of the License at
|
|
7
|
+
#
|
|
8
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
|
9
|
+
#
|
|
10
|
+
# Unless required by applicable law or agreed to in writing, software
|
|
11
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
|
12
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
13
|
+
# See the License for the specific language governing permissions and
|
|
14
|
+
# limitations under the License.
|
|
15
|
+
#
|
|
16
|
+
# Originally from mobile-use (https://github.com/minitap-ai/mobile-use).
|
|
17
|
+
# See third_party/mobile_use/METADATA for the upstream source and local modifications.
|
|
18
|
+
|
|
19
|
+
"""Tool wrappers: a LangChain tool factory and the messages shown for its result."""
|
|
20
|
+
|
|
21
|
+
from collections.abc import Callable
|
|
22
|
+
|
|
23
|
+
from langchain_core.tools import BaseTool
|
|
24
|
+
from pydantic import BaseModel
|
|
25
|
+
|
|
26
|
+
from artemis.context import ArtemisContext
|
|
27
|
+
from artemis.utils.cython_compat import CyFunctionDetector
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
class ToolWrapper(BaseModel):
|
|
31
|
+
"""Wrapper holding a tool factory and lifecycle callbacks."""
|
|
32
|
+
|
|
33
|
+
model_config = {"ignored_types": (CyFunctionDetector,)}
|
|
34
|
+
tool_fn_getter: Callable[[ArtemisContext], BaseTool]
|
|
35
|
+
on_success_fn: Callable[..., str]
|
|
36
|
+
on_failure_fn: Callable[..., str]
|
|
37
|
+
is_available_fn: Callable[[ArtemisContext], bool] | None = None
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
class CompositeToolWrapper(ToolWrapper):
|
|
41
|
+
"""Wrapper holding a composite tool factory and lifecycle callbacks."""
|
|
42
|
+
|
|
43
|
+
composite_tools_fn_getter: Callable[[ArtemisContext], list[BaseTool]]
|
|
44
|
+
|
|
45
|
+
|
|
46
|
+
def make_save_note_wrapper(tool_fn_getter: Callable[[ArtemisContext], BaseTool]) -> ToolWrapper:
|
|
47
|
+
"""Wrapper for the scratchpad ``save_note`` tool with its outcome messages."""
|
|
48
|
+
return ToolWrapper(
|
|
49
|
+
tool_fn_getter=tool_fn_getter,
|
|
50
|
+
on_success_fn=lambda key: f"Saved note '{key}'.",
|
|
51
|
+
on_failure_fn=lambda key: f"Failed to save note '{key}'.",
|
|
52
|
+
)
|
|
@@ -0,0 +1,13 @@
|
|
|
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.
|