fix(cua): address shared API, packaging and migration review
This commit is contained in:
1 parent
5ee1c02eb8
commit
1991fa2b22
22 files changed
+128
-223
No files matched your search
@@ -11,7 +11,7 @@ on:
|
||||
description: '产物所属的构建运行编号'
|
||||
value: ${{ jobs.package.outputs.run-id }}
|
||||
build-key:
|
||||
description: '源码和构建配置的摘要'
|
||||
description: '提交 SHA、.NET SDK 版本和 win-x64 RID 的 SHA-256 摘要'
|
||||
value: ${{ jobs.package.outputs.build-key }}
|
||||
|
||||
permissions:
|
||||
@@ -32,15 +32,21 @@ jobs:
|
||||
- uses: actions/[email protected]
|
||||
with:
|
||||
persist-credentials: false
|
||||
- uses: actions/setup-dotnet@v5
|
||||
- uses: actions/setup-dotnet@v5.4.0
|
||||
with:
|
||||
dotnet-version: '10.0.x'
|
||||
- name: Restore locked Windows helper and run protocol checks
|
||||
run: |
|
||||
dotnet restore packages/experimental/cua-windows/native/cua-windows.slnx --locked-mode
|
||||
if ($LASTEXITCODE -ne 0) { exit $LASTEXITCODE }
|
||||
dotnet run --project packages/experimental/cua-windows/native/tests/Protocol.Tests.csproj --configuration Release --no-restore
|
||||
- name: Publish Windows CUA helper
|
||||
run: >-
|
||||
dotnet publish packages/experimental/cua-windows/native/entry/CuaWindows.csproj
|
||||
--configuration Release
|
||||
--runtime win-x64
|
||||
--self-contained true
|
||||
--no-restore
|
||||
--output dist/cua-helper-win-x64
|
||||
# Standalone helper zip contract: flat root with cua-helper.json,
|
||||
# dsh-cua-windows.exe, and its runtime files. Installed providers place
|
||||
@@ -82,7 +88,15 @@ jobs:
|
||||
Set-Content -Path (Join-Path $directory 'cua-helper.json') -Value "$json`n" -NoNewline -Encoding utf8NoBOM
|
||||
Add-Content -Path $env:GITHUB_OUTPUT -Value "build-key=$buildKey`n" -NoNewline -Encoding utf8
|
||||
- name: Archive publish output
|
||||
run: Compress-Archive -Path dist/cua-helper-win-x64/* -DestinationPath dist/cua-helper-win-x64.zip
|
||||
run: |
|
||||
$zipPath = 'dist/cua-helper-win-x64.zip'
|
||||
Compress-Archive -Path dist/cua-helper-win-x64/* -DestinationPath $zipPath
|
||||
$zip = [IO.Compression.ZipFile]::OpenRead((Resolve-Path $zipPath).Path)
|
||||
try {
|
||||
foreach ($name in 'dsh-cua-windows.exe', 'cua-helper.json') {
|
||||
if (-not $zip.GetEntry($name)) { throw "Missing root entry in helper ZIP: $name" }
|
||||
}
|
||||
} finally { $zip.Dispose() }
|
||||
- uses: actions/[email protected]
|
||||
id: upload
|
||||
with:
|
||||
|
||||
@@ -3,8 +3,8 @@
|
||||
# After editing either side, bring the other along and re-record with:
|
||||
# pnpm run verify-translation-pairing --write docs/subsystems/computer-use.md
|
||||
/computer-use:
|
||||
en: e02a2e4cc9970cba
|
||||
zh: 29aac6c0530a582f
|
||||
en: 25f78f4f0019ca86
|
||||
zh: a3f7ac9225c1ced4
|
||||
/computer-use/choose-a-provider:
|
||||
en: fa2e0d7b41c0450b
|
||||
zh: af9a799711f901e6
|
||||
|
||||
@@ -14,7 +14,7 @@ The CUA provider owns `ctx.cuaAccess` for system-permission status, explicit sys
|
||||
|---|---|
|
||||
| `CuaApplicationId`, `CuaApplicationGrant` | A platform-provided application identity and its displayed saved decision. |
|
||||
| `CuaSystemPermission` | Accessibility or Screen Recording, selected by the human. |
|
||||
| `CuaPermissionProcess`, `CuaSystemPermissionState` | Native access observations tagged `darwin` or `win32`, with helper process identity. macOS includes permission checks, parent identity and responsible-application metadata when available (falling back to the helper’s own bundle); Windows includes desktop lock, capture availability and elevation status. |
|
||||
| `CuaPermissionProcess`, `CuaSystemPermissionState` | Native access observations tagged `darwin` or `windows`, with the helper’s name and path when available. macOS includes permission checks, parent identity and responsible-application metadata when available (falling back to the helper’s own bundle); Windows includes desktop lock, capture availability and elevation status. |
|
||||
| `CuaApplicationState` | Saved grants, refused applications, and whether persistence is available. Session-only grants remain internal to the provider. System observations and application icons are queried separately so the page can display each result as it arrives. |
|
||||
|
||||
The [shared object API](../../packages/experimental/cua/README.md) uses `connectCua` to select an application, then `app.getWindows()` and `app.getWindow(windowId)` to select a window for screenshots and pointer input. Accessibility operations use element IDs from the latest tree for that application handle. Providers declare keyboard scopes and application-level accessibility in `getCapabilities()`.
|
||||
|
||||
@@ -14,7 +14,7 @@ CUA provider 通过 `ctx.cuaAccess` 提供系统权限状态、用户主动申
|
||||
|---|---|
|
||||
| `CuaApplicationId`、`CuaApplicationGrant` | 平台提供的应用身份,以及显示给用户的已保存决定。 |
|
||||
| `CuaSystemPermission` | 用户选择申请的辅助功能或屏幕录制权限。 |
|
||||
| `CuaPermissionProcess`、`CuaSystemPermissionState` | 以 `darwin` 或 `win32` 标记的原生访问状态和 helper 进程身份。macOS 包含权限检查、父进程身份和责任应用信息(系统无法识别时使用 helper 自身的应用包);Windows 包含桌面锁定、截图可用性和提权状态。 |
|
||||
| `CuaPermissionProcess`、`CuaSystemPermissionState` | 以 `darwin` 或 `windows` 标记的原生访问状态,包含可获取的 helper 名称和路径。macOS 包含权限检查、父进程身份和责任应用信息(系统无法识别时使用 helper 自身的应用包);Windows 包含桌面锁定、截图可用性和提权状态。 |
|
||||
| `CuaApplicationState` | 持久授权、拒绝记录,以及能否持久保存。会话授权只保留在 provider 内部。系统权限和应用图标分别查询,页面在各项结果返回后分别显示。 |
|
||||
|
||||
调用[共享对象 API](../../packages/experimental/cua/README.zh.md) 时,通过 `connectCua` 连接,选择应用后用 `app.getWindows()` 和 `app.getWindow(windowId)` 选择要截图或进行鼠标操作的窗口。辅助功能操作使用该应用句柄最近一次读取的树中的元素 ID。提供方通过 `getCapabilities()` 声明键盘输入支持的范围,以及是否支持应用级辅助功能。
|
||||
|
||||
@@ -9,8 +9,8 @@
|
||||
en: 207c515390c5e529
|
||||
zh: bd4d90881efb011d
|
||||
/native-cua-application-and-window-api/change:
|
||||
en: d3b7e02c362b0049
|
||||
zh: 49f1a190495e813d
|
||||
en: 074a8a1d4cae7733
|
||||
zh: 24139a7d8a27d15c
|
||||
/native-cua-application-and-window-api/migration:
|
||||
en: 90d4d985da7e6618
|
||||
zh: 9d9b4d3444cf0e3b
|
||||
en: 4533c6e8ef6613d7
|
||||
zh: d5862c063d24809c
|
||||
@@ -9,15 +9,15 @@ English | [中文](guide.zh.md)
|
||||
|
||||
## Change
|
||||
|
||||
Screenshots and pointer input require explicit windows. App (`appHandle`) and Window (`windowId`) objects share their application handle's latest accessibility snapshot. Discovery reports running matches' PIDs.
|
||||
Screenshots and pointer input require explicit windows. App (`appHandle`) and Window (`windowId`) objects share their application handle's latest accessibility snapshot. Window IDs are opaque strings. Discovery and permission metadata omit process IDs.
|
||||
|
||||
Providers supply startup capabilities and implement `getGuidance(kind)` instead of supplying a `guidance` string. `raw` selects instructions for the raw `cua` tool; `standalone` selects instructions for `cua_run_code`. The native helper reports runtime permissions and session lock state; the Host combines these with startup capabilities in `getCapabilities()`. Human-facing permission responses carry a `darwin` or `win32` platform tag.
|
||||
Providers supply startup capabilities and implement `getGuidance(kind)` instead of supplying a `guidance` string. `raw` selects instructions for the raw `cua` tool; `standalone` selects instructions for `cua_run_code`. The native helper reports runtime permissions and session lock state; the Host combines these with startup capabilities in `getCapabilities()`. Human-facing permission responses carry a `darwin` or `windows` platform tag.
|
||||
|
||||
## Migration
|
||||
|
||||
1. After `cua.getApp(selector)`, select the target from `app.getWindows()` with `app.getWindow(windowId)`. Use `window.getScreenshot()`, `window.click()`, `window.scroll()`, and `window.drag()`. Replace `hover` with `window.mouseMove()`. On macOS, `window.click(id)` sends mouse events at the element center. Switch to `invoke(id)` for the default accessibility action, or `performAction(id, "AXShowMenu")` to request an advertised menu action.
|
||||
1. Pass a current `app.getWindows()` ID unchanged to `app.getWindow(windowId)`. Rediscover saved numeric IDs. Use `window.getScreenshot()`, `window.click()`, `window.scroll()`, and `window.drag()`. Replace `hover` with `window.mouseMove()`. On macOS, `window.click(id)` sends mouse events at the element center. Switch to `invoke(id)` for the default accessibility action, or `performAction(id, "AXShowMenu")` to request an advertised menu action.
|
||||
2. Replace `getAXState()` with `getAccessibilityTree()`. Replace `getAXStateAndScreenshot()` with separate tree and screenshot reads. Application and window selection does not read AX. Before using element IDs, call `getAccessibilityTree()` explicitly; use `{ emit: false }` to retain the snapshot without displaying it. A screenshot does not require a successful AX read. Before coordinate input, including relative coordinates, obtain a successful AX observation covering the window or a screenshot; absolute coordinates require a screenshot. See [macOS AX read limits](../cua-ax-read-limits/guide.md) for changes to traversal defaults and configuration.
|
||||
3. Pass the opaque string ID from the latest tree wherever you passed a numeric element index. In raw requests use `elementId` with `snapshotId`. Replace `performSecondaryAction` with `listActions(id)` and `performAction(id, exactName, argument?)`, or use `invoke(id)` for the default action. `setValue` accepts the provider’s supported JSON values. On macOS, `performAction(id, 'AXRaise')` performs the advertised raise action. Call `app.activate()` first when the application must become active.
|
||||
4. Replace a packed chord such as `pressKey('shift+Tab')` with `pressKey('Tab', ['shift'])`. Check `(await cua.getCapabilities()).methods` before using `sendKeyEvent`. On macOS, call it inside one `run_code` or `cua_run_code` to hold a key across tool calls. To request one repeat, send another down for a non-modifier key held by that execution. Returning, throwing or pausing releases any remaining keys. If your script handles unknown macOS key names as `NATIVE_ERROR`, handle `INVALID_ARGUMENT` instead. Correct the key name before retrying.
|
||||
5. Read `dispatched` from action results, then observe the application to verify the outcome. `invoke` returns `dispatched: false` when the element has no default action. Remove reads of the old `verified` field and of `pid` on selected objects or window records. Use `listApps()` or running `findApps()` matches when you need a PID.
|
||||
4. Replace a packed chord such as `pressKey('shift+Tab')` with `pressKey('Tab', ['shift'])`. Check `(await cua.getCapabilities()).methods` before using `sendKeyEvent`. On macOS, call it inside one `run_code` or `cua_run_code` to hold a key across tool calls. Repeat a held non-modifier with another down. Returning, throwing or pausing releases any remaining keys. If your script handles unknown macOS key names as `NATIVE_ERROR`, handle `INVALID_ARGUMENT` instead. Correct the key name before retrying.
|
||||
5. Read `dispatched`, then observe the outcome. `invoke` returns `dispatched: false` when the element has no default action. Remove reads of the old `verified` field and of `pid` on selected objects or window records. Pass the `id` from `listApps()` to `getApp(id)`. Update permission readers that test for `win32` to use `windows`.
|
||||
6. Custom providers replace `CuaBackend.description` with `capabilities` and `guidance` with `getGuidance(kind)`, and supply `permissionParameters` to `installCua`. Both kinds may return identical text. Rebuild custom `helperPath` against the [operation schema](../../../../packages/experimental/cua/src/protocol.ts) and [tagged permission response](../../../../packages/experimental/cua/src/access-types.ts). Restart and verify both tool forms' guidance, advertised methods, keyboard scopes, and platform controls on the Computer Use permission page.
|
||||
@@ -9,15 +9,15 @@ description: "迁移 CUA 脚本和自定义原生提供方,使用显式窗口
|
||||
|
||||
## 变更
|
||||
|
||||
截图和指针输入要求显式选择窗口。App(`appHandle`)和 Window(`windowId`)对象共享所属应用句柄的最新辅助功能快照。应用发现返回运行中匹配项的 PID。
|
||||
截图和指针输入要求显式选择窗口。App(`appHandle`)和 Window(`windowId`)对象共享所属应用句柄的最新辅助功能快照。窗口 ID 是不透明字符串,应用发现结果和权限信息不再包含 PID。
|
||||
|
||||
提供方声明启动能力,并实现 `getGuidance(kind)`,不再传入 `guidance` 字符串。`raw` 选择原始 `cua` 工具的指引,`standalone` 选择 `cua_run_code` 的指引。原生 helper 返回运行时权限和会话锁定状态,Host 将其与启动能力合并到 `getCapabilities()` 中。供权限页面使用的响应带有 `darwin` 或 `win32` 平台标记。
|
||||
提供方声明启动能力,并实现 `getGuidance(kind)`,不再传入 `guidance` 字符串。`raw` 选择原始 `cua` 工具的指引,`standalone` 选择 `cua_run_code` 的指引。原生 helper 返回运行时权限和会话锁定状态,Host 将其与启动能力合并到 `getCapabilities()` 中。供权限页面使用的响应带有 `darwin` 或 `windows` 平台标记。
|
||||
|
||||
## 迁移
|
||||
|
||||
1. 调用 `cua.getApp(selector)` 后,从 `app.getWindows()` 返回的列表选定目标,再调用 `app.getWindow(windowId)`。在窗口上调用 `getScreenshot()`、`click()`、`scroll()` 和 `drag()`,将原有的 `hover` 调用改为 `window.mouseMove()`。在 macOS 上,`window.click(id)` 在元素中心发送鼠标事件。需要执行默认辅助功能动作时,改用 `invoke(id)`。需要打开控件菜单且控件声明了 `AXShowMenu` 时,调用 `performAction(id, "AXShowMenu")`。
|
||||
1. 从 `app.getWindows()` 返回的列表选定目标,把原始字符串传给 `app.getWindow(windowId)`。曾以数字保存的窗口 ID 需要重新获取。在窗口上调用 `getScreenshot()`、`click()`、`scroll()` 和 `drag()`,将原有的 `hover` 调用改为 `window.mouseMove()`。在 macOS 上,`window.click(id)` 在元素中心发送鼠标事件。需要执行默认辅助功能动作时,改用 `invoke(id)`。需要打开控件菜单且控件声明了 `AXShowMenu` 时,调用 `performAction(id, "AXShowMenu")`。
|
||||
2. 将 `getAXState()` 改为 `getAccessibilityTree()`,将 `getAXStateAndScreenshot()` 拆成读取树和截图两次调用。选择应用或窗口不读取 AX。使用元素 ID 前须显式调用 `getAccessibilityTree()`;使用 `{ emit: false }` 可保留快照但不显示树。截图不要求 AX 读取成功。所有坐标输入(包括相对坐标)之前都须成功读取覆盖该窗口的 AX 或取得截图;绝对坐标要求截图。[读取上限的迁移说明](../cua-ax-read-limits/guide.zh.md)列出了 macOS 默认值和配置含义的变化。
|
||||
3. 将数字元素索引改为从最新树中取得的字符串 ID。直接发送请求时使用 `elementId` 和 `snapshotId`。原先调用 `performSecondaryAction` 的地方,先用 `listActions(id)` 取得完整动作名,再调用 `performAction(id, exactName, argument?)`。要执行默认动作,可调用 `invoke(id)`。`setValue` 接受提供方支持的 JSON 值。 在 macOS 上,`performAction(id, 'AXRaise')` 只执行元素声明的抬起窗口动作。需要把应用切到前台时先调用 `app.activate()`。
|
||||
4. 将 `pressKey('shift+Tab')` 之类的组合键写法改为 `pressKey('Tab', ['shift'])`。调用 `sendKeyEvent` 前检查 `(await cua.getCapabilities()).methods`。在 macOS 上,同一次 `run_code` 或 `cua_run_code` 中可调用此方法跨工具调用保持按键。如果需要一次重复按下,可在这次执行中对已按住的普通键再发一次 `down`。执行返回、异常退出或用户暂停时会释放仍按着的键。如果脚本用 `NATIVE_ERROR` 识别 macOS 上的未知键名,请改为处理 `INVALID_ARGUMENT`,并在重试前修正键名。
|
||||
5. 从动作结果读取 `dispatched`,再观察应用确认效果。元素没有默认动作时,`invoke` 返回 `dispatched: false`。移除读取旧字段 `verified` 的代码,以及对已选对象和窗口记录中 `pid` 的读取。需要 PID 时,使用 `listApps()` 或 `findApps()` 返回的运行中应用记录。
|
||||
5. 从动作结果读取 `dispatched`,再观察应用确认效果。元素没有默认动作时,`invoke` 返回 `dispatched: false`。移除读取旧字段 `verified` 的代码,以及对已选对象和窗口记录中 `pid` 的读取。把 `listApps()` 返回的 `id` 传给 `getApp(id)`。读取权限状态时,将原先判断 `win32` 的代码改为判断 `windows`。
|
||||
6. 自定义提供方将 `CuaBackend.description` 改为 `capabilities`,将 `guidance` 改为 `getGuidance(kind)`,并向 `installCua` 传入 `permissionParameters`。两种形态可以返回相同文本。按[操作协议](../../../../packages/experimental/cua/src/protocol.ts)和[带平台标记的权限响应](../../../../packages/experimental/cua/src/access-types.ts)重新构建自定义 `helperPath` 对应的 helper。重启后验证两种工具形态的指引、声明的方法、键盘输入范围及 Computer Use 权限页的平台控件。
|
||||
@@ -12,8 +12,11 @@
|
||||
en: 3d01094229f39847
|
||||
zh: 6ef07286fab57de1
|
||||
/deepseek-ai-dsh-cua-windows/table-of-contents:
|
||||
en: 873f5e3022e22ff3
|
||||
zh: c8e442bf641a59da
|
||||
en: 8ee204b4021505b9
|
||||
zh: 67040b79fcd9643b
|
||||
/deepseek-ai-dsh-cua-windows/setup-and-configuration:
|
||||
en: 9dec4721f40cfe32
|
||||
zh: c1ec768912e6d752
|
||||
/deepseek-ai-dsh-cua-windows/interface-semantics:
|
||||
en: 13ff5154e3ed57cf
|
||||
zh: b6a976028b104068
|
||||
@@ -21,11 +24,11 @@
|
||||
en: 50a841cef8bc070c
|
||||
zh: 3f4bdedd4cb64cd0
|
||||
/deepseek-ai-dsh-cua-windows/interface-semantics/application-and-window-selection:
|
||||
en: 7e5bae65d7da5f1d
|
||||
zh: a5303b3883bfa842
|
||||
en: ea1d0bc4fd95bec1
|
||||
zh: 242e07a4d73ac771
|
||||
/deepseek-ai-dsh-cua-windows/interaction-rules:
|
||||
en: 6534efafae6f35ed
|
||||
zh: b7e349fd5b676c15
|
||||
en: 7c6e48717022a5e7
|
||||
zh: c4ca64066538381c
|
||||
/deepseek-ai-dsh-cua-windows/model-experience:
|
||||
en: 215e7ba838619b7b
|
||||
zh: a311da3843709f90
|
||||
@@ -42,8 +45,8 @@
|
||||
en: 1a91ac329caa77d8
|
||||
zh: 25eafe98efcf7860
|
||||
/deepseek-ai-dsh-cua-windows/known-limitations-and-deferred-work:
|
||||
en: 1751f0c8dcc8504f
|
||||
zh: 2f01b08512a230bd
|
||||
en: 2cfd5e0280457c57
|
||||
zh: 6f99c1f84677bd5e
|
||||
/deepseek-ai-dsh-cua-windows/known-limitations-and-deferred-work/dev-note:
|
||||
en: 0a1253c6f7b5e8b9
|
||||
zh: 9f56528562c7c914
|
||||
@@ -13,11 +13,19 @@ Discover Windows applications, obtain consent, then inspect and operate their wi
|
||||
|
||||
## Table of Contents
|
||||
|
||||
- [Setup and configuration](#setup)
|
||||
- [Interface semantics](#interface-semantics)
|
||||
- [Interaction rules](#interaction-rules)
|
||||
- [Model Experience](#model-experience)
|
||||
- [Known Limitations and Deferred Work](#known-limitations-and-deferred-work)
|
||||
|
||||
<a id="setup"></a>
|
||||
## Setup and configuration
|
||||
|
||||
In a native Windows source checkout, activation uses the .NET 10 SDK on PATH to build the helper for the current architecture under `native/bin/win-<arch>`. Set `helperPath` to the absolute path of an existing executable to use it instead. For a manual build, run `pnpm run build:native:x64` or `pnpm run build:native:arm64` in this package. The framework-dependent `build:native` output in `native/bin/local` requires a matching Windows .NET runtime and an explicit `helperPath`. Extract the complete standalone win-x64 ZIP. The npm release pipeline does not yet stage that payload.
|
||||
|
||||
`buildTimeoutMs` defaults to 300000, `connectTimeoutMs` to 30000, and `pollIntervalMs` to 25. Accessibility reads default to 2000 nodes and depth 25. Pass `maxElements` and `maxDepth` to override either limit for one read. The bundle loads this provider only on native Windows. When configured explicitly in WSL, `windowsPowerShellPath` defaults to `powershell.exe`. The WSL cache currently copies only the exe, so a multi-file publish cannot start from that cache.
|
||||
|
||||
<a id="interface-semantics"></a>
|
||||
## Interface semantics
|
||||
|
||||
@@ -33,7 +41,7 @@ Each result contains `id`, `displayName`, and `isRunning`. The ID is an AUMID, i
|
||||
|
||||
- `cua.getApp(id)` requests application consent and returns an App on success. It does not read accessibility state or activate the application. Windows does not support application launch; the windowless-selection limitation below applies.
|
||||
- `app.getWindows()` reads the application's current window list. One application can have multiple windows across multiple processes, and the list can become empty as windows close.
|
||||
- `app.getWindow(windowId)` returns a Window object without reading accessibility state. Window IDs are decimal strings; preserve them without converting them to JavaScript numbers. Window operations check that the target still belongs to the selected application.
|
||||
- `app.getWindow(windowId)` returns a Window object without reading accessibility state. Pass the opaque window ID returned by the provider unchanged. Window operations check that the target still belongs to the selected application.
|
||||
- `window.getAccessibilityTree()` explicitly reads accessibility state. Element IDs belong to the current observation; reading another window of the same App replaces the facade's current snapshot. Do not mix elements or snapshots across windows.
|
||||
- `window.getScreenshot()` captures that explicit window and does not read or replace its accessibility tree. Screenshots can remain usable when an accessibility read fails.
|
||||
- `app.activate()` explicitly requests foreground activation. `app.show()` restores a default window without requesting activation; `window.show()` restores the named window. An absent default returns `WINDOW_NOT_FOUND`; an ambiguous default returns `AMBIGUOUS_WINDOW`.
|
||||
@@ -43,7 +51,7 @@ Each result contains `id`, `displayName`, and `isRunning`. The ID is an AUMID, i
|
||||
|
||||
1. Check `cua.getCapabilities()` before choosing an operation. Accessibility and keyboard input are window-scoped on Windows; screenshots and pointer actions also require a Window object.
|
||||
2. Read an accessibility tree before using element IDs. Absolute pointer coordinates use pixels in the latest returned screenshot, relative to that window. `relative: true` uses integer thousandths from 0 to 999 after an appropriate AX observation or screenshot. Capture again after a resize.
|
||||
3. System keyboard and pointer input require the intended foreground window. Explain any necessary focus change, explicitly activate the application or ask the user to focus the target, and observe again. Do not assume that selecting an App or Window changes focus.
|
||||
3. In shortcuts, `option` selects Alt and `control` selects Ctrl. The provider rejects Windows-key shortcuts using `command`. System keyboard and pointer input require the intended foreground window. Explain any necessary focus change, explicitly activate the application or ask the user to focus the target, and observe again. Do not assume that selecting an App or Window changes focus.
|
||||
4. Treat `dispatched` as dispatch evidence, not confirmation of the intended effect. Observe the result before continuing. On a changed window, stale snapshot, timeout, or cancellation, do not blindly repeat input or switch to another window.
|
||||
5. Consent applies to the application within the conversation. Respect denial and revocation; do not use another tool to bypass them. Reconnects invalidate retained handles, so select the application again before further operations.
|
||||
|
||||
@@ -70,7 +78,6 @@ New observations append to history; retaining an App or Window object does not i
|
||||
|
||||
- `findApps`, application launch, app-level accessibility, and held-key `sendKeyEvent` are unavailable. Ask the user to open the required application or window.
|
||||
- Applications with `isRunning: false` can be discovered, but the current `getApp` flow attempts an unsupported launch and fails with `UNSUPPORTED`. Open a window before selecting such an application.
|
||||
- Per-read `maxElements` and `maxDepth` overrides are currently ignored on Windows; the provider's configured limits remain effective.
|
||||
- Observation and input require an accessible, unlocked interactive Windows session. Screenshots require Windows Graphics Capture support; a listed window can still be unavailable for capture or input.
|
||||
- Application consent does not offer an **Always allow** grant. Another conversation needs its own consent.
|
||||
|
||||
|
||||
@@ -13,11 +13,19 @@ kind: "package-reference"
|
||||
|
||||
## 目录
|
||||
|
||||
- [运行条件与配置](#setup)
|
||||
- [接口语义](#interface-semantics)
|
||||
- [交互规范](#interaction-rules)
|
||||
- [模型体验](#model-experience)
|
||||
- [已知限制与后续工作](#known-limitations-and-deferred-work)
|
||||
|
||||
<a id="setup"></a>
|
||||
## 运行条件与配置
|
||||
|
||||
在原生 Windows 的源码检出中,提供方启用时通过 PATH 中的 .NET 10 SDK 构建当前架构的 helper,输出到 `native/bin/win-<arch>`。`helperPath` 可指定现有 exe 的绝对路径。手动构建时,在本包中执行 `pnpm run build:native:x64` 或 `pnpm run build:native:arm64`。使用 `build:native` 生成的 `native/bin/local` 产物时,需要匹配的 Windows .NET 运行时,并显式配置 `helperPath`。独立的 win-x64 ZIP 需完整解压,当前 npm 发布流程尚未装入该载荷。
|
||||
|
||||
`buildTimeoutMs` 默认 300000,`connectTimeoutMs` 默认 30000,`pollIntervalMs` 默认 25。辅助功能树默认最多读取 2000 个节点、深度 25。一次读取可传入 `maxElements` 和 `maxDepth`。组合包仅在原生 Windows 上加载此提供方。显式在 WSL 中配置提供方时,`windowsPowerShellPath` 默认是 `powershell.exe`。目前 WSL 缓存只复制 exe,多文件发布目录不能直接从该缓存启动。
|
||||
|
||||
<a id="interface-semantics"></a>
|
||||
## 接口语义
|
||||
|
||||
@@ -33,7 +41,7 @@ kind: "package-reference"
|
||||
|
||||
- `cua.getApp(id)` 请求应用授权,成功后返回 App。它不读取辅助功能树,也不激活应用。Windows 不支持启动应用;无窗口应用的选择还受下方限制约束。
|
||||
- `app.getWindows()` 读取应用当前的窗口列表。一个应用可以跨多个进程拥有多个窗口;窗口关闭后,列表也可以变为空。
|
||||
- `app.getWindow(windowId)` 返回 Window 对象,不读取辅助功能树。窗口 ID 是十进制字符串,应原样保留,不要转换为 JavaScript 数字。执行窗口操作时会检查目标是否仍属于所选应用。
|
||||
- `app.getWindow(windowId)` 返回 Window 对象,不读取辅助功能树。窗口 ID 是提供方返回的不透明字符串,请原样传递。执行窗口操作时会检查目标是否仍属于所选应用。
|
||||
- `window.getAccessibilityTree()` 显式读取辅助功能树。元素 ID 属于当前观察;读取同一 App 的另一个窗口会替换门面当前使用的快照。不要跨窗口混用元素或快照。
|
||||
- `window.getScreenshot()` 截取明确指定的窗口,不读取或替换辅助功能树。辅助功能读取失败时,截图仍可能可用。
|
||||
- `app.activate()` 显式请求前台激活。`app.show()` 恢复默认窗口而不请求激活;`window.show()` 恢复指定窗口。没有默认窗口时返回 `WINDOW_NOT_FOUND`,无法唯一确定默认窗口时返回 `AMBIGUOUS_WINDOW`。
|
||||
@@ -43,7 +51,7 @@ kind: "package-reference"
|
||||
|
||||
1. 选择操作前先检查 `cua.getCapabilities()`。Windows 的辅助功能和键盘输入仅支持窗口作用域;截图和指针操作也必须使用 Window 对象。
|
||||
2. 使用元素 ID 前先读取辅助功能树。绝对指针坐标采用最近一次返回的截图像素,以该窗口为原点。`relative: true` 在相应的 AX 观察或截图之后使用 0 到 999 的整数千分比坐标。窗口尺寸变化后重新截图。
|
||||
3. 系统键盘和指针输入要求目标窗口位于前台。解释必要的焦点切换,显式激活应用或让用户聚焦目标窗口,再重新观察。不要假定选择 App 或 Window 会改变焦点。
|
||||
3. 快捷键中 `option` 表示 Alt,`control` 表示 Ctrl。提供方拒绝使用 Windows 键的 `command` 快捷键。系统键盘和指针输入要求目标窗口位于前台。解释必要的焦点切换,显式激活应用或让用户聚焦目标窗口,再重新观察。不要假定选择 App 或 Window 会改变焦点。
|
||||
4. `dispatched` 只表示分派情况,不证明预期效果已经发生。继续操作前先观察结果。窗口变化、快照过期、超时或取消后,不要盲目重放输入,也不要自动切换目标窗口。
|
||||
5. 授权针对当前会话中的应用。尊重拒绝和撤销,不使用其他工具绕过。重新连接会使已保留的句柄失效,继续操作前必须重新选择应用。
|
||||
|
||||
@@ -70,7 +78,6 @@ kind: "package-reference"
|
||||
|
||||
- 不支持 `findApps`、启动应用、应用级辅助功能和持续按键 `sendKeyEvent`。请让用户打开所需应用或窗口。
|
||||
- 可以发现 `isRunning: false` 的应用,但当前 `getApp` 流程会尝试未支持的启动操作,并以 `UNSUPPORTED` 失败。选择这类应用前应先打开窗口。
|
||||
- Windows 当前忽略按次传入的 `maxElements` 和 `maxDepth` 覆盖值,仍使用提供方配置中的限制。
|
||||
- 观察和输入需要可访问、未锁定的交互式 Windows 会话。截图需要 Windows Graphics Capture 支持;列出的窗口仍可能无法截图或接收输入。
|
||||
- 应用授权不提供 **Always allow(始终允许)** 选项。其他会话需要分别获得授权。
|
||||
|
||||
|
||||
@@ -2,3 +2,4 @@
|
||||
**/obj/
|
||||
.build/
|
||||
**/.vs
|
||||
*.user
|
||||
@@ -5,6 +5,7 @@
|
||||
<AutoGenerateBindingRedirects>true</AutoGenerateBindingRedirects>
|
||||
<BaseOutputPath>$(MSBuildThisFileDirectory).build/$(MSBuildProjectName)/bin/</BaseOutputPath>
|
||||
<BaseIntermediateOutputPath>$(MSBuildThisFileDirectory).build/$(MSBuildProjectName)/obj/</BaseIntermediateOutputPath>
|
||||
<RuntimeIdentifiers>win-x64;win-arm64</RuntimeIdentifiers>
|
||||
<RestorePackagesWithLockFile>true</RestorePackagesWithLockFile>
|
||||
</PropertyGroup>
|
||||
|
||||
|
||||
@@ -1,9 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Project ToolsVersion="Current" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
<PropertyGroup />
|
||||
<ItemGroup>
|
||||
<Compile Update="Program.cs">
|
||||
<SubType>Form</SubType>
|
||||
</Compile>
|
||||
</ItemGroup>
|
||||
</Project>
|
||||
@@ -8,6 +8,8 @@
|
||||
"resolved": "1.15.0",
|
||||
"contentHash": "FbU0El+EEjdpuIX4iDbeS7ki1uzpJPx8vbqOzEtqnl1GZeAGJfq+jCbxeJL2y0EPnUNk8dRnnqR2xnYXg9Tf+g=="
|
||||
}
|
||||
}
|
||||
},
|
||||
"net10.0-windows10.0.19041/win-arm64": {},
|
||||
"net10.0-windows10.0.19041/win-x64": {}
|
||||
}
|
||||
}
|
||||
@@ -47,6 +47,8 @@
|
||||
"JsonSchema.Net": "[9.4.0, )"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"net10.0-windows10.0.19041/win-arm64": {},
|
||||
"net10.0-windows10.0.19041/win-x64": {}
|
||||
}
|
||||
}
|
||||
@@ -1,167 +0,0 @@
|
||||
{
|
||||
"version": 1,
|
||||
"dependencies": {
|
||||
".NETFramework,Version=v4.8": {
|
||||
"Microsoft.NETFramework.ReferenceAssemblies": {
|
||||
"type": "Direct",
|
||||
"requested": "[1.0.3, )",
|
||||
"resolved": "1.0.3",
|
||||
"contentHash": "vUc9Npcs14QsyOD01tnv/m8sQUnGTGOw1BCmKcv77LBJY7OxhJ+zJF7UD/sCL3lYNFuqmQEVlkfS4Quif6FyYg==",
|
||||
"dependencies": {
|
||||
"Microsoft.NETFramework.ReferenceAssemblies.net48": "1.0.3"
|
||||
}
|
||||
},
|
||||
"PolySharp": {
|
||||
"type": "Direct",
|
||||
"requested": "[1.15.0, )",
|
||||
"resolved": "1.15.0",
|
||||
"contentHash": "FbU0El+EEjdpuIX4iDbeS7ki1uzpJPx8vbqOzEtqnl1GZeAGJfq+jCbxeJL2y0EPnUNk8dRnnqR2xnYXg9Tf+g=="
|
||||
},
|
||||
"System.Text.Json": {
|
||||
"type": "Direct",
|
||||
"requested": "[10.0.14, 10.0.14]",
|
||||
"resolved": "10.0.14",
|
||||
"contentHash": "JSSLGJTJJDvS76VnLdY1X289rFcHuYApk7QGw0hCoP5wVR1mk7BvJbOYxQLXq4x1qK/Ml9OHa/AKaAbBVCfSSQ==",
|
||||
"dependencies": {
|
||||
"Microsoft.Bcl.AsyncInterfaces": "10.0.14",
|
||||
"System.Buffers": "4.6.1",
|
||||
"System.IO.Pipelines": "10.0.14",
|
||||
"System.Memory": "4.6.3",
|
||||
"System.Runtime.CompilerServices.Unsafe": "6.1.2",
|
||||
"System.Text.Encodings.Web": "10.0.14",
|
||||
"System.Threading.Tasks.Extensions": "4.6.3",
|
||||
"System.ValueTuple": "4.6.2"
|
||||
}
|
||||
},
|
||||
"Humanizer.Core": {
|
||||
"type": "Transitive",
|
||||
"resolved": "3.0.10",
|
||||
"contentHash": "yZIhtw8sYuvsONzQbZxWpR60tMWYHXoo0DL6nyOqSFiU5POjBTSEyWFpTQtJEZuy+oqiYTXKXY/Mjx7KnqIQFw==",
|
||||
"dependencies": {
|
||||
"System.Collections.Immutable": "9.0.10",
|
||||
"System.ComponentModel.Annotations": "5.0.0",
|
||||
"System.Memory": "4.6.3"
|
||||
}
|
||||
},
|
||||
"Json.More.Net": {
|
||||
"type": "Transitive",
|
||||
"resolved": "3.0.1",
|
||||
"contentHash": "fRctF2J2SILYG6wqP21drmeEODmCVkVQ/b3MndDu2fT1swfySyUgq7ePCk+aENGlDcIm05fyfjh9vcuqDEfv3w==",
|
||||
"dependencies": {
|
||||
"System.Collections.Immutable": "10.0.5",
|
||||
"System.Text.Json": "10.0.5"
|
||||
}
|
||||
},
|
||||
"JsonPointer.Net": {
|
||||
"type": "Transitive",
|
||||
"resolved": "7.0.2",
|
||||
"contentHash": "oClYHv2ooeRrtPZyC9sb/Za/ie5pGhjTbNHlAyFg4XOCyU+606FdoZKS7UdaWvtsPlrF+U2w0Ja3TRvn5+spyA==",
|
||||
"dependencies": {
|
||||
"Humanizer.Core": "3.0.10",
|
||||
"Json.More.Net": "3.0.1"
|
||||
}
|
||||
},
|
||||
"JsonSchema.Net": {
|
||||
"type": "Transitive",
|
||||
"resolved": "9.4.0",
|
||||
"contentHash": "muE4nPuzbD9x5XA1mkXJNqX4mhz47oF3EY8qFLY1pyUY7lhXFKq65t/dSKIHeNSLBV17zwZuq24MD1em90rMNg==",
|
||||
"dependencies": {
|
||||
"JsonPointer.Net": "7.0.2"
|
||||
}
|
||||
},
|
||||
"Microsoft.Bcl.AsyncInterfaces": {
|
||||
"type": "Transitive",
|
||||
"resolved": "10.0.14",
|
||||
"contentHash": "HCu9NbiXDh9++j4eRNRHCOiKPw/7I5UEJlFIRaDM3v0+ttZaH8p8rjxSWfNgP2cGv5JAa4EZnmc/nlcUOTFlpA==",
|
||||
"dependencies": {
|
||||
"System.Threading.Tasks.Extensions": "4.6.3"
|
||||
}
|
||||
},
|
||||
"Microsoft.NETFramework.ReferenceAssemblies.net48": {
|
||||
"type": "Transitive",
|
||||
"resolved": "1.0.3",
|
||||
"contentHash": "zMk4D+9zyiEWByyQ7oPImPN/Jhpj166Ky0Nlla4eXlNL8hI/BtSJsgR8Inldd4NNpIAH3oh8yym0W2DrhXdSLQ=="
|
||||
},
|
||||
"System.Buffers": {
|
||||
"type": "Transitive",
|
||||
"resolved": "4.6.1",
|
||||
"contentHash": "N8GXpmiLMtljq7gwvyS+1QvKT/W2J8sNAvx+HVg4NGmsG/H+2k/y9QI23auLJRterrzCiDH+IWAw4V/GPwsMlw=="
|
||||
},
|
||||
"System.Collections.Immutable": {
|
||||
"type": "Transitive",
|
||||
"resolved": "10.0.5",
|
||||
"contentHash": "nozrOKfEgfrvvswkCfxaumY68RA/x1F3ZYwtwRvva8ul91NCnUzb6MKpl5/P7u9v/nIagVL4OXXj8d007tucxw==",
|
||||
"dependencies": {
|
||||
"System.Memory": "4.6.3",
|
||||
"System.Runtime.CompilerServices.Unsafe": "6.1.2"
|
||||
}
|
||||
},
|
||||
"System.ComponentModel.Annotations": {
|
||||
"type": "Transitive",
|
||||
"resolved": "5.0.0",
|
||||
"contentHash": "dMkqfy2el8A8/I76n2Hi1oBFEbG1SfxD2l5nhwXV3XjlnOmwxJlQbYpJH4W51odnU9sARCSAgv7S3CyAFMkpYg=="
|
||||
},
|
||||
"System.IO.Pipelines": {
|
||||
"type": "Transitive",
|
||||
"resolved": "10.0.14",
|
||||
"contentHash": "pLVClkIC4XfXhcOQXNtNDJl5LYJOS4XAQAdzbqF8rj6sk2zdXO/5uikBsZqgqnAAb07z9Fmtu0sMj3TwEiBtIw==",
|
||||
"dependencies": {
|
||||
"System.Buffers": "4.6.1",
|
||||
"System.Memory": "4.6.3",
|
||||
"System.Threading.Tasks.Extensions": "4.6.3"
|
||||
}
|
||||
},
|
||||
"System.Memory": {
|
||||
"type": "Transitive",
|
||||
"resolved": "4.6.3",
|
||||
"contentHash": "qdcDOgnFZY40+Q9876JUHnlHu7bosOHX8XISRoH94fwk6hgaeQGSgfZd8srWRZNt5bV9ZW2TljcegDNxsf+96A==",
|
||||
"dependencies": {
|
||||
"System.Buffers": "4.6.1",
|
||||
"System.Numerics.Vectors": "4.6.1",
|
||||
"System.Runtime.CompilerServices.Unsafe": "6.1.2"
|
||||
}
|
||||
},
|
||||
"System.Numerics.Vectors": {
|
||||
"type": "Transitive",
|
||||
"resolved": "4.6.1",
|
||||
"contentHash": "sQxefTnhagrhoq2ReR0D/6K0zJcr9Hrd6kikeXsA1I8kOCboTavcUC4r7TSfpKFeE163uMuxZcyfO1mGO3EN8Q=="
|
||||
},
|
||||
"System.Runtime.CompilerServices.Unsafe": {
|
||||
"type": "Transitive",
|
||||
"resolved": "6.1.2",
|
||||
"contentHash": "2hBr6zdbIBTDE3EhK7NSVNdX58uTK6iHW/P/Axmm9sl1xoGSLqDvMtpecn226TNwHByFokYwJmt/aQQNlO5CRw=="
|
||||
},
|
||||
"System.Text.Encodings.Web": {
|
||||
"type": "Transitive",
|
||||
"resolved": "10.0.14",
|
||||
"contentHash": "+aDpFLtsHyft9ZVx9pZQ9MILqwrr8LK1te1L39VMKQYhoYDbs+RUqBhCT3Zydb5ymi2Buo4Cn+3Wi208IxD6rw==",
|
||||
"dependencies": {
|
||||
"System.Buffers": "4.6.1",
|
||||
"System.Memory": "4.6.3",
|
||||
"System.Runtime.CompilerServices.Unsafe": "6.1.2"
|
||||
}
|
||||
},
|
||||
"System.Threading.Tasks.Extensions": {
|
||||
"type": "Transitive",
|
||||
"resolved": "4.6.3",
|
||||
"contentHash": "7sCiwilJLYbTZELaKnc7RecBBXWXA+xMLQWZKWawBxYjp6DBlSE3v9/UcvKBvr1vv2tTOhipiogM8rRmxlhrVA==",
|
||||
"dependencies": {
|
||||
"System.Runtime.CompilerServices.Unsafe": "6.1.2"
|
||||
}
|
||||
},
|
||||
"System.ValueTuple": {
|
||||
"type": "Transitive",
|
||||
"resolved": "4.6.2",
|
||||
"contentHash": "yQgmjfFximrNm9LIV3mL6T5MzjeC+epeE5rl4hXxAlYmxby7RM1dPSkIKXk9HNkl6G54h2JHOmLD46+Pey+IRg=="
|
||||
},
|
||||
"cuaprotocol": {
|
||||
"type": "Project",
|
||||
"dependencies": {
|
||||
"JsonSchema.Net": "[9.4.0, )",
|
||||
"System.Text.Json": "[10.0.14, 10.0.14]"
|
||||
}
|
||||
}
|
||||
},
|
||||
".NETFramework,Version=v4.8/win-x86": {}
|
||||
}
|
||||
}
|
||||
@@ -36,6 +36,8 @@
|
||||
"Json.More.Net": "3.0.1"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"net10.0-windows10.0.19041/win-arm64": {},
|
||||
"net10.0-windows10.0.19041/win-x64": {}
|
||||
}
|
||||
}
|
||||
@@ -41,6 +41,8 @@
|
||||
"JsonSchema.Net": "[9.4.0, )"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"net10.0-windows10.0.19041/win-arm64": {},
|
||||
"net10.0-windows10.0.19041/win-x64": {}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
- id: windows-provider
|
||||
name: cordis:windows-provider
|
||||
config:
|
||||
agentPreset: false
|
||||
helperPath: !!js process.execPath
|
||||
@@ -0,0 +1,40 @@
|
||||
/** Windows provider registration through the Loader without an interactive desktop. */
|
||||
import { Context } from '@deepseek-ai/cordis'
|
||||
import Include from '@deepseek-ai/cordis-plugin-include'
|
||||
import Loader from '@deepseek-ai/cordis-plugin-loader'
|
||||
import { mountAgentLoopTestDependencies, mountAgentLoopTestHarness } from '@deepseek-ai/dsh-agent-loop-testkit'
|
||||
import ComputerUseRegistry from '@deepseek-ai/dsh-computer-use'
|
||||
import { ToolCallId } from '@deepseek-ai/dsh-llm'
|
||||
import { SessionId } from '@deepseek-ai/dsh-session'
|
||||
import SubprocessLocal from '@deepseek-ai/dsh-subprocess-local'
|
||||
import { expect, it, onTestFinished } from 'vitest'
|
||||
import * as WindowsProvider from '../src/index.ts'
|
||||
|
||||
it.skipIf(process.platform !== 'win32')('loads the Windows provider and rejects a numeric window ID before native dispatch', async () => {
|
||||
const ctx = new Context()
|
||||
onTestFinished(() => ctx.fiber.dispose())
|
||||
await mountAgentLoopTestDependencies(ctx)
|
||||
await mountAgentLoopTestHarness(ctx)
|
||||
await ctx.plugin(ComputerUseRegistry)
|
||||
await ctx.plugin(SubprocessLocal)
|
||||
ctx.baseUrl = import.meta.url
|
||||
await ctx.plugin(Loader)
|
||||
Object.assign(ctx.loader.builtins, { include: Include, 'windows-provider': WindowsProvider })
|
||||
await ctx.loader.create({ name: 'cordis:include', config: { path: new URL('./fixtures/provider.yml', import.meta.url).href } })
|
||||
await ctx.loader.await()
|
||||
const entry = [...ctx.loader.entries()].find(entry => entry.options.id === 'windows-provider')
|
||||
if (!entry?.fiber) throw new Error('Loader did not activate the Windows provider')
|
||||
await entry.fiber.await()
|
||||
const { agent } = await ctx.agents.create({ sessionId: SessionId('cua-windows-loader') })
|
||||
expect(ctx.tools.get('cua', agent)).toBeDefined()
|
||||
const prompt = await ctx.systemPrompt.assemble({ scope: agent })
|
||||
expect(prompt.sections.find(section => section.name === 'computer-use:dsh-cua')?.text).toContain('Windows:')
|
||||
const batch = await ctx.tools.execute({ agent, signal: new AbortController().signal, calls: [{
|
||||
type: 'tool-call', id: ToolCallId('invalid-window'), name: 'cua',
|
||||
arguments: JSON.stringify({ method: 'screenshot', params: { appHandle: 'app', windowId: 7 } }),
|
||||
}] })
|
||||
expect(batch.results[0]?.result.isError).toBe(true)
|
||||
expect(JSON.stringify(batch.results[0]?.result.content)).toContain('string')
|
||||
await entry.fiber.dispose()
|
||||
expect(ctx.tools.get('cua', agent)).toBeUndefined()
|
||||
})
|
||||
@@ -240,10 +240,7 @@ export function installCua(ctx: Context, config: CuaConfig, backend: CuaBackend)
|
||||
? { ...params, maxClipboardBytes: config.maxClipboardBytes, pasteReadTimeoutMs: config.pasteReadTimeoutMs,
|
||||
maxElements: config.maxElements, maxDepth: config.maxDepth }
|
||||
: method === 'getState'
|
||||
// Windows keeps its configured limits until per-read overrides are implemented natively.
|
||||
? backend.capabilities.platform === 'windows'
|
||||
? { ...params, maxElements: config.maxElements, maxDepth: config.maxDepth }
|
||||
: { maxElements: config.maxElements, maxDepth: config.maxDepth, ...params }
|
||||
? { maxElements: config.maxElements, maxDepth: config.maxDepth, ...params }
|
||||
: method === 'drag' ? { ...params, durationMs: config.dragDurationMs, steps: config.dragSteps }
|
||||
: params
|
||||
const requestNative = () => client.request(method, nativeParams, signal, execution.callId)
|
||||
@@ -286,7 +283,6 @@ export function installCua(ctx: Context, config: CuaConfig, backend: CuaBackend)
|
||||
}).parse(result)
|
||||
await access.select(agent, client, prepared.appHandle, authorization, signal, applicationIcon)
|
||||
signal.throwIfAborted()
|
||||
// TODO: launchApp on Windows
|
||||
const selected = prepared.running
|
||||
? prepared
|
||||
: AppResult.parse(await access.run(client, prepared.appHandle, signal,
|
||||
|
||||
@@ -249,12 +249,11 @@ it('settles an in-flight native call before releasing the provider connection an
|
||||
expect(ctx.tools.get('cua_run_code', agent)).toBeUndefined()
|
||||
})
|
||||
|
||||
it('keeps Windows accessibility reads within the configured traversal limits', async () => {
|
||||
it('forwards per-read accessibility limits to the Windows backend', async () => {
|
||||
const { run, native } = await setup([
|
||||
'var app = await cua.getApp("test.fixture"); var win = await app.getWindow("7"); await win.getAccessibilityTree({ maxElements: 9999, maxDepth: 100, emit: false })',
|
||||
'var app = await cua.getApp("test.fixture"); var win = await app.getWindow("7"); await win.getAccessibilityTree({ maxElements: 30, maxDepth: 3, emit: false })',
|
||||
], 'windows')
|
||||
await run()
|
||||
const defaults = CuaConfig({})
|
||||
expect(native.calls.find(call => call.method === 'getState')?.params)
|
||||
.toMatchObject({ maxElements: defaults.maxElements, maxDepth: defaults.maxDepth })
|
||||
.toMatchObject({ maxElements: 30, maxDepth: 3 })
|
||||
})
|
||||
Reference in new issue
Block a user