diff --git a/README.md b/README.md index d38b766..72df2e5 100644 --- a/README.md +++ b/README.md @@ -2,7 +2,7 @@ 基于 [ARTEMIS](https://github.com/google/artemis) 扩展的 Android AI 自动化测试平台。用自然语言描述任务,即可通过简体中文网页或 MCP 驱动真机、模拟器执行操作并收集结果。模型统一使用 **OpenAI Responses API**。 -**2.5.0**:新增通用动作序列、临时控件准备与刻度校准拖动;补齐参数流保护、取消关闭与诊断,优化会话缓存键一致性,并在执行模式弹窗显示缓存 Token 和加权命中率。见 [2.5.0 发行说明](docs/releases/2.5.0.md)。本版包含 [2.4.5 的插件开发 SDK](docs/releases/2.4.5.md),SDK 与插件继续独立版本化;升级主程序不会自动更新或授权插件。 +**2.6.0**:新增 Android 原生控件读写、条件等待、容器滚动、双指缩放与连续多指手势;支持状态确保、稳定数值反馈与无效动作保护,修复多窗口原生查询和选择器像素边界,并补齐前端中文动作名称。见 [2.6.0 发行说明](docs/releases/2.6.0.md)。包含 [2.5.0 的动作序列与缓存统计](docs/releases/2.5.0.md)及 [2.4.5 的插件开发 SDK](docs/releases/2.4.5.md);SDK 与插件继续独立版本化,升级主程序不会自动更新或授权插件。 ## 主要功能 diff --git a/apps/showcase_ui/package-lock.json b/apps/showcase_ui/package-lock.json index 4098546..80338b3 100644 --- a/apps/showcase_ui/package-lock.json +++ b/apps/showcase_ui/package-lock.json @@ -1,12 +1,12 @@ { "name": "artemis-showcase-ui", - "version": "2.5.0", + "version": "2.6.0", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "artemis-showcase-ui", - "version": "2.5.0", + "version": "2.6.0", "dependencies": { "@angular/cdk": "^22.2.4", "@angular/common": "^22.2.3", diff --git a/apps/showcase_ui/package.json b/apps/showcase_ui/package.json index 67695f7..d637575 100644 --- a/apps/showcase_ui/package.json +++ b/apps/showcase_ui/package.json @@ -1,6 +1,6 @@ { "name": "artemis-showcase-ui", - "version": "2.5.0", + "version": "2.6.0", "scripts": { "ng": "ng", "start": "ng serve --proxy-config proxy.conf.json", diff --git a/docs/releases/2.6.0.md b/docs/releases/2.6.0.md new file mode 100644 index 0000000..a38d6f9 --- /dev/null +++ b/docs/releases/2.6.0.md @@ -0,0 +1,44 @@ +# ARTEMIS 2.6.0 + +本版汇总正式 2.5.0 之后的 Android 原生动作、连续多指手势与精确操作反馈改进。Flash、普通 Pro 和探索共用这些能力,外层任务提交接口保持兼容;CUA 插件保持现有版本,本次不接入 agent-desktop。 + +## 原生控件与连续手势 + +- 新增 `read_control`,读取控件支持动作、范围、当前值、状态、选区与身份 token;新增 `control_action`,执行控件实际支持的点击、长按、聚焦、原生设进度、文本编辑和 IME 提交。不支持的动作明确拒绝,不自动回退坐标输入。 +- 新增只读条件等待 `wait_until`,支持出现、消失、可用、状态、文本、数值及持续稳定。短暂不可读在原定预算内继续观测,读取失败不能证明控件消失。 +- 新增 `scroll_to`,在指定唯一容器内有界查找目标;等待内容停稳,无进展、到达边界、失败或取消时停止。 +- 新增 `pinch` 双指缩放/旋转与 `touch_gesture` 连续多指轨迹,支持 1~5 指、分段移动与按住停留。多段/多指动作需要 Android 8.0/API 26 及以上,按助手实际能力启用。 +- 动作序列可组合原生动作、条件等待与手势。输入前校验整组,部分失败后保留已完成、取消或结果不明的动作,不重放整组。前端、报告动作摘要和截图标注补齐简体中文名称。 + +## 状态确保与数值反馈 + +- `control_action.ensure_state` 支持勾选、选中、精确文本/描述及数值条件。已满足时跳过;未满足时只操作一次并回读验证,避免盲目切换状态。 +- 拖动与动作序列可绑定真实控件的数值或时钟 `feedback`,返回前后读数、残差和容差结果;等待连续稳定后判断。手势已完成但读数不符时保留实际输入回执,不自动重发。 +- 同一目标和策略连续两次没有降低误差后,第三次拒绝继续输入并要求重新观察或调整策略。此保护依赖显式反馈,不能自动理解所有自绘控件。 +- 手势可限制在已观察的 `region` 内;刻度校准补充像素取整误差与最小有效距离检查。双指跨度倍数不等于 APP 比例尺倍数,仍需读取实际刻度和最终值。 +- 验收允许采用明确绑定当前选时、暂停后持续稳定且与固定指针一致的常驻秒级读数。录像卡片、水印、临时浮层、来源不明或真实证据冲突仍不能作为通过依据;不会改写原有历史任务结论。 + +## 多窗口查询与稳定选择器 + +- 修复观察接口能看到控件、原生接口却只查询 active 空壳窗口的问题。查询在当前前台 APP 的相关窗口中有界解析,跨根仍须唯一,不扩大到后台应用。 +- 写入前检查目标可见区域及高层窗口遮挡;完全遮挡时拒绝。Android 11/API 30 及以上使用精确可触区域,旧系统保留矩形近似限制。 +- 观察结果新增可直接复制的 `native_selector`,优先绑定资源 ID、类名和包名;重复或缺失 ID 时补充原始像素边界,避免把变化中的读数当作身份。 +- 原生选择器边界使用 Android 原始像素,列表手势边界使用 0–1000 归一化坐标,两者不能混用。`NOT_FOUND` 只表示本次没有匹配,不能据此断言控件没有原生语义。 + +完整参数、回执及时间轴使用边界见[安卓原生动作说明](../android-native-actions.md)。 + +## 助手与发布包 + +- 随包 Android 助手为 `1.3.0 / protocol 3 / versionCode 15`。连接时依据已有助手安装设置及能力声明进行检查;旧后端不支持时不暴露新动作,也不会把原生写请求改投其它后端重放。 +- 修复源码 ZIP 将随仓库提供的助手 APK 排除的问题:只保留指定运行载荷,其它 APK、构建目录、签名密钥、用户配置和运行资料继续排除。最终包验证会核对助手文件与 manifest 的 SHA256。 +- 主程序与前端根版本升级到 2.6.0;远程 Python SDK、市场插件及第三方依赖不升版。保持用户已保存的模型、接口、重试与功能配置。 + +## 验证范围 + +- 发布前默认 Python 回归、Ruff、质量基线、核心类型检查,以及同一个最终 ZIP 在独立空 npm 缓存下的官方源/国内镜像安装、前端测试与生产构建,实际结果随附件 `INSTALL-VERIFICATION.md` 提供。不会将历史测试数量当作本次验证。 +- 此前开发验证在 Android 模拟器上完成原生 Activity、连续触摸、取消与窗口解析回归;三次独立 Flash 时间轴定位取得 −3s、−3s、+1s 的暂停后稳定读数。详细范围及保留的失败样本见[开发验证记录](../plans/2026-11-02-native-actions-validation.md)。这不代表所有设备、OEM 或自绘时间轴均可达到同样精度。 +- 本次发布不新增设备业务任务或付费模型测试;没有原生范围的时间轴仍需手势、实际刻度和稳定读数配合,工具输入完成不等于业务验收通过。 + +## 升级 + +已有安装等待任务结束后运行 `update-cn.bat` 或 `update.bat`,然后重启 ARTEMIS;Codex 用户重新加载 MCP。新安装下载 `artemis-2.6.0-source.zip`,按 `SHA256SUMS.txt` 校验后解压启动,不直接覆盖正在运行的安装目录。 diff --git a/pyproject.toml b/pyproject.toml index 59b2604..1df42e2 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -14,7 +14,7 @@ [project] name = "artemis" -version = "2.5.0" +version = "2.6.0" description = "Autonomous AI Assistant for Mobile Automation" authors = [ diff --git a/scripts/package_source.py b/scripts/package_source.py index ac5781d..297a066 100644 --- a/scripts/package_source.py +++ b/scripts/package_source.py @@ -88,6 +88,7 @@ EXCLUDED_SUFFIXES = { GIT_METADATA_FILES = {"HEAD", "config", "index", "shallow", "packed-refs"} GIT_METADATA_DIRS = {"objects", "refs"} +RUNTIME_PAYLOADS = {"packages/artemis-accessibility-helper/ArtemisAccessibilityHelper.apk"} def excluded(path: Path) -> bool: @@ -104,6 +105,10 @@ def excluded(path: Path) -> bool: return True if name in {".ds_store", "thumbs.db", ".ipc_port", ".ls_address", ".npmrc", ".pypirc"}: return True + # The tracked helper is needed at runtime on fresh Android installations. + # Keep this exact payload, while excluding unrelated APKs and build outputs. + if path.as_posix() in RUNTIME_PAYLOADS: + return False return path.suffix.lower() in EXCLUDED_SUFFIXES or name.endswith((".db-wal", ".db-shm", "~")) diff --git a/tests/unit/test_source_archive.py b/tests/unit/test_source_archive.py index d3b6230..1e9f6ea 100644 --- a/tests/unit/test_source_archive.py +++ b/tests/unit/test_source_archive.py @@ -30,10 +30,16 @@ def repository(tmp_path): git(root, "init", "-b", "main") git(root, "config", "user.name", "Fixture") git(root, "config", "user.email", "fixture@example.test") - keep = ["start.bat", ".env.example", "uv.lock", "apps/showcase_ui/src/main.ts"] + keep = [ + "start.bat", + ".env.example", + "uv.lock", + "apps/showcase_ui/src/main.ts", + "packages/artemis-accessibility-helper/ArtemisAccessibilityHelper.apk", + ] tracked_builds = [ "artemis/resources/showcase_ui/main.js", - "packages/artemis-accessibility-helper/ArtemisAccessibilityHelper.apk", + "packages/artemis-accessibility-helper/debug.apk", "packages/artemis-accessibility-helper/debug.keystore", ] omit = [ @@ -85,6 +91,8 @@ def test_archive_is_updatable_clean_checkout_without_local_data(repository, tmp_ "https://git.example.invalid/team/project.git" ) assert git(extracted, "config", "--get", "branch.main.remote") == "origin" + helper = Path("packages/artemis-accessibility-helper/ArtemisAccessibilityHelper.apk") + assert (extracted / helper).read_bytes() == (root / helper).read_bytes() (extracted / tracked_builds[0]).parent.mkdir(parents=True) (extracted / tracked_builds[0]).write_text("new bundle") assert git(extracted, "status", "--porcelain") == "" @@ -107,13 +115,16 @@ def test_unpacked_archive_fast_forwards(repository, tmp_path): built_asset.parent.mkdir(parents=True) built_asset.write_text("generated after setup") (root / "start.bat").write_text("updated") - git(root, "add", "--", "start.bat") + helper = Path("packages/artemis-accessibility-helper/ArtemisAccessibilityHelper.apk") + (root / helper).write_bytes(b"updated helper") + git(root, "add", "--", "start.bat", str(helper)) git(root, "commit", "-m", "update launcher") git(root, "push", "test-upstream", "main") from scripts.update_installation import update_source assert update_source(extracted) == 10 assert (extracted / "start.bat").read_text() == "updated" + assert (extracted / helper).read_bytes() == b"updated helper" assert git(extracted, "status", "--porcelain") == "" diff --git a/uv.lock b/uv.lock index 2b60ab3..4a65727 100644 --- a/uv.lock +++ b/uv.lock @@ -248,7 +248,7 @@ wheels = [ [[package]] name = "artemis" -version = "2.5.0" +version = "2.6.0" source = { editable = "." } dependencies = [ { name = "adbutils" },