dsh-computer-use
Verified@restregular/dsh-computer-use · v0.3.3 · MIT
Windows computer use for DeepSeek Harness: consented screen takeover with an interactive mask chat panel, plus screenshots, mouse, keyboard, windows and clipboard through a native helper.
Install
dsh plugin add @restregular/dsh-computer-use Confirm the layer applied with dsh --profile default --dump-config — see the install guide.
Source
Tags
Readme
dsh-computer-use
中文 | English
让 DeepSeek Harness 的 Agent 真正看见并操作 Windows 桌面:截屏、鼠标、键盘、窗口与剪贴板。所有操作先经过一次明确的人工授权(完全权限模式下免确认);授权后进入「接管」状态——半透明蒙版覆盖整个屏幕,物理鼠标键盘不再落到桌面上,而是由蒙版自己绘制的模拟光标和聊天面板接管:你可以在蒙版里给发起接管的会话发消息,或直接取消接管。核心能力由随包提供的 Rust 原生 helper 完成,不依赖浏览器或外部自动化程序。
安装
dsh plugin --profile web add @restregular/dsh-computer-use
桌面版也可以在「插件 → 插件管理器」里安装 @restregular/dsh-computer-use,然后在插件详情里启用。首次调用工具时,插件会把原生 helper 复制到 %LOCALAPPDATA%\\dsh-computer-use\\ 并从那里启动,避免插件安装在低完整性目录时无法注入输入。
要求:Windows 10/11 x64,DSH 0.2.1 或更新。
三个工具
| 工具 | 作用 |
|---|---|
computer_screenshot |
截取整个虚拟桌面、单个显示器、某个窗口或屏幕区域,返回图片和坐标元数据 |
computer_action |
鼠标移动/点击/双击/右击/拖拽/滚轮,键盘按键/组合键/文本输入,等待,剪贴板读写,release 交还控制 |
computer_windows |
列出顶层窗口(id、标题、进程、bounds、状态、前台),聚焦窗口或最小化/最大化/还原 |
坐标规则
computer_screenshot返回的图片可能被缩小;computer_action的默认坐标就是这张图片的像素坐标。- 工具结果里的
capture是真实桌面矩形,image是模型看到的图片尺寸,插件按比例把图片坐标映射回桌面。 - 没有先截图就传图片坐标会报
SCREENSHOT_REQUIRED;确实要用绝对桌面像素时传coordinate_space: "screen"。 - 多个 Agent/会话共享同一块桌面;同一时刻只允许一个 Agent 持有接管。
推荐循环
computer_windows找到目标窗口;computer_screenshot观察,必要时带window_id只看目标窗口;computer_action执行一个动作(聚焦窗口后先点击输入框,再type);- 再截一张图确认结果。点击成功不等于任务成功。
授权与接管
- 授权。 会话审批策略为
ask时,每个回合的第一次鼠标/键盘动作都会走 DSH 审批弹窗。完全权限模式下(danger-full-access权限预设,审批策略never)不再弹框:插件把用户的完全权限选择视作授权,直接执行动作——此时 harness 本来就会自动拒绝这类询问,继续拦截只会让工具失败。无论哪种模式,接管蒙版、蒙版里的「取消接管」和急停热键依然生效。consent.scope: session可以让一次授权覆盖整个会话;consent.requireApproval: false可以完全关闭授权。 - 接管。 授权通过后,helper 会在真正注入输入之前启动接管:
- 半透明蒙版覆盖整个虚拟桌面,顶部提示条显示当前状态和急停热键;
- 安装
WH_KEYBOARD_LL/WH_MOUSE_LL低级钩子,物理事件不再落到桌面。它们没有简单被丢弃:蒙版按物理位置绘制一个模拟光标,点击由蒙版底部的聊天面板处理(输入框、发送、取消接管); - 低级钩子能拦下鼠标消息,但拦不住系统在钩子之前执行的指针移动。所以每次物理移动后,蒙版会把系统光标拉回到 Agent 最后注入的位置(在接管线程里用
SetCursorPos完成,不在钩子里做),否则用户的手一动就会把 Agent 的光标拽走; - 正因为光标被钉住,钩子里的坐标只剩钉住处附近的抖动,不能用来画用户的模拟光标。模拟光标的位置改为累积 Raw Input(
WM_INPUT)的硬件增量lLastX/lLastY——它不受SetCursorPos影响,用户的手怎么划,白箭头就怎么走(1:1,不做指针加速);点击面板同样用这个坐标做命中测试; - 蒙版本身是可命中的顶层窗口(不是
WS_EX_TRANSPARENT):精密触摸板的双指滚动和 Chromium 类应用的滚轮走WM_POINTER,低级鼠标钩子收不到,只有落到蒙版上才能吞掉。Agent 注入输入的瞬间蒙版会临时加回WS_EX_TRANSPARENT(点击穿透,60 ms 后恢复),所以 Agent 自己的点击照样能落到下面的窗口; - 点击输入框会让面板里的多行 EDIT 获得键盘焦点,物理键盘(包括系统输入法)只进入蒙版里的消息,不会影响 Agent 正在操作的窗口。面板持有焦点期间,Agent 的注入输入会被拒绝并返回
HUMAN_COMPOSING;对应的动作会等面板收起后自动重试; - Agent 通过
SendInput注入的输入带有injected标志,会正常穿过钩子,因此蒙版不会妨碍 Agent 自己操作;面板本身鼠标穿透,Agent 点在面板下面的窗口也能收到; - 蒙版、聊天面板和模拟光标都设置了
WDA_EXCLUDEFROMCAPTURE,模型的截图里看不到它们(老版本 Windows 不支持时,截图瞬间会短暂隐藏); - 蒙版本身不抢键盘焦点;只有用户主动点击输入框时,面板才会临时获得焦点。
- 结束。 回合结束(
agent/turn-stopping)、Agent 主动调用computer_action的action: "release"、Agent 被 dispose、点击蒙版里的「取消接管」、急停热键、helper 退出时,控制权都会交还用户。takeover.idleEndMs可设置空闲多久后自动释放。 - 急停。 接管期间按急停热键(默认
Ctrl+Alt+Shift+F12)会立即结束接管、恢复用户输入、让 helper 进入 PAUSED 并撤销本回合授权。正在执行的动作以TAKEOVER_ENDED中止,后续输入报PAUSED。模型不能自行恢复:需要用户再按一次热键解除 PAUSED,下一次输入还会重新请求授权。 - 关键按键。 急停热键的所有组成键始终原样透传、不会被丢弃(helper 的轮询线程必须能看到它们)。
Ctrl+Alt+Del是安全注意序列,低级钩子无法拦截;锁屏/UAC 安全桌面也不受影响——用户始终能夺回机器。 - 同一时刻一个接管者。 其他 Agent 的输入会返回
TAKEOVER_BUSY,直到控制被释放。
在手机上通过 dsh-pocket 等通道审批也可以;蒙版本身始终画在被控制的 Windows 桌面上。
在蒙版里给 Agent 发消息
蒙版底部的聊天面板对用户的模拟光标可交互:
- 点击输入框开始输入;
Enter发送,Shift+Enter换行,Esc(或点击面板外)收起输入但保留文字; - 消息会作为一条普通用户消息发给发起接管的会话,发送策略跟随 DSH 设置里的「繁忙时按 Enter」(
设置 → 会话 → 繁忙时按 Enter):queue排到下一回合,steer插到下一步。面板状态栏会显示当前策略; - 「取消接管」会立即撤销本回合授权并结束接管(fail closed:正在执行的输入会中止,下一次输入需要重新授权),但不会像急停热键那样进入
PAUSED; - 右键可以收起输入框。系统的真实光标仍由 Agent 控制,白色箭头是你的模拟光标,两者可以同时看到。
安全
- 输入是真实发生的,不能撤销;只能中断、释放按键并重新观察。
- 授权与接管默认开启。
input.requireApproval是consent.requireApproval的过时别名,仍然兼容。 - 急停热键(默认
Ctrl+Alt+Shift+F12):按下后 helper 立即进入 PAUSED、释放所有按键,后续输入报PAUSED;再按一次恢复。模型不能自行恢复。 - 接管期间物理鼠标键盘不会落到桌面,而是交给蒙版面板处理;其它软件注入的输入同样带
injected标志,和 Agent 自己的输入无法区分。 - 蒙版里输入的消息会作为普通用户消息交给会话;原生 helper 不保存消息内容。
- 默认拦截
Win+L、Ctrl+Alt+Del、Win+U,可通过input.blockedCombos调整。 - 可选进程允许/拒绝列表:
safety.allowedProcesses/safety.blockedProcesses。 - 锁屏、UAC 安全桌面会报
SECURE_DESKTOP;非提权 helper 向提权窗口注入会被 UIPI 拦截,返回失败信息。 - 截图会进入 DSH 会话附件库,可能包含屏幕上的敏感信息;插件不会把 base64 写进会话日志。
配置
在 cordis.patch.yml 里覆盖插件行;所有字段都有默认值:
- id: dsh-computer-use
config:
screenshot:
format: jpeg # png | jpeg
jpegQuality: 85
maxEdge: 1200 # 0 = 保留原始尺寸
includeCursor: true
captureMethod: auto # auto | gdi | printwindow
input:
typeIntervalMs: 0
dragSteps: 24
maxHoldMs: 5000
blockedCombos: ["win+l", "ctrl+alt+del", "win+u"]
maxTextLength: 20000
consent:
requireApproval: true
scope: turn # turn | session
takeover:
enabled: true
opacity: 0.18 # 0.05 .. 0.9
banner: true
chat: true # 蒙版里的交互式聊天面板
title: "" # 留空使用本地化默认标题
hint: "" # 留空使用带热键的本地化默认提示
idleEndMs: 0 # 0 = 回合结束时释放接管
safety:
hotkey: "ctrl+alt+shift+f12"
allowedProcesses: []
blockedProcesses: []
timeoutMs: 15000
idleStopMs: 300000
debug:
logHelper: false
| 字段 | 默认 | 说明 |
|---|---|---|
enabled |
true | 关闭后不注册工具 |
helperPath |
空 | 指定 helper exe;默认使用包内 helper 的 LOCALAPPDATA 副本 |
timeoutMs |
15000 | 单次 helper 请求超时 |
idleStopMs |
300000 | 空闲后停止 helper;0 表示常驻(接管期间会临时保活) |
screenshot.maxEdge |
1600 | 编码前最长边,越小越省图片 token |
input.typeIntervalMs |
0 | 逐字输入间隔 |
input.dragSteps |
24 | 拖拽插值步数 |
input.maxHoldMs |
5000 | 单次按住上限 |
consent.requireApproval |
true | 审批策略为 ask 时在第一次输入动作前请求授权;完全权限(never)下不弹框直接执行。input.requireApproval 仍可作为别名 |
consent.scope |
turn | turn 每回合重新授权,session 一次授权用到 Agent dispose |
takeover.enabled |
true | Agent 操作时覆盖蒙版,物理输入交给蒙版面板处理 |
takeover.opacity |
0.18 | 蒙版不透明度,0.05–0.9 |
takeover.banner |
true | 显示带急停热键的提示条 |
takeover.chat |
true | 蒙版里的交互式聊天面板:物理指针和键盘进入面板而不是桌面 |
takeover.title / hint |
空 | 提示条文案;留空用本地化默认值 |
takeover.idleEndMs |
0 | 空闲多久后释放接管;0 表示回合结束才释放 |
safety.hotkey |
ctrl+alt+shift+f12 | 急停与终止接管热键 |
safety.allowedProcesses |
[] | 非空时只允许这些进程名 |
safety.blockedProcesses |
[] | 这些进程名永远拒绝 |
开发
npm run build:native # cargo build --release,并把 exe 放到 native/bin/win32-x64
npm test # Node 单元测试
node scripts/smoke.mjs # 直接测试 helper:截图、窗口、鼠标、剪贴板
node scripts/notepad-test.mjs "$LOCALAPPDATA/dsh-computer-use/helper-test.exe" # 输入端到端测试
node scripts/takeover-test.mjs "$LOCALAPPDATA/dsh-computer-use/helper-test.exe" # 蒙版 + 钩子 + 急停
node scripts/chat-test.mjs "$LOCALAPPDATA/dsh-computer-use/helper-test.exe" # 聊天面板:输入、发送、回执、取消
原生测试请在可弃置的桌面上运行,并先把 exe 复制到 %LOCALAPPDATA%:从低完整性工作区直接启动的 helper 能截图,但注入输入会被拒绝。接管测试会验证蒙版、截图排除、注入输入穿过钩子、Host 主动释放以及急停路径;聊天面板测试会验证输入焦点、HUMAN_COMPOSING 门控、发送与回执、取消按钮和焦点恢复。
Rust helper 位于 native/,使用 windows-sys、image、serde_json。它用一行一个 JSON 的协议与 Host 通信,一次只处理一个请求;Host 在超时/取消时杀掉 helper,重启后先发送 input.release_all。接管蒙版和钩子运行在独立线程的消息循环里;helper 退出时 Windows 会自动移除钩子和蒙版。
已知限制
- 仅 Windows x64;ARM64 Windows 通过 x64 模拟运行,未单独验证。
- UIA 语义树/元素操作尚未实现;当前依赖截图 + 坐标。
- GDI 对部分 GPU/DRM 窗口可能截到黑屏;窗口截图会尝试
PrintWindow并回退到屏幕矩形。Windows Graphics Capture 是后续方案。 - 未签名的原生 exe 在部分机器上可能触发 SmartScreen/杀软提示。
- 多个会话共享同一桌面;接管只在单个 DSH 进程内协调,多个 DSH 进程之间不协调。
- 某些游戏/受保护应用使用 Raw Input,可能忽略
SendInput。 - 用户在蒙版输入框里打字时,Agent 的输入动作最多等待 30 秒,然后以
HUMAN_COMPOSING失败(可重试)。输入框是标准 Win32 EDIT,系统输入法(含中文)可用;候选词窗口由输入法自己绘制,不属于插件。 - 低级钩子只作用于交互桌面;UAC/锁屏等安全桌面不受影响——这也是
Ctrl+Alt+Del始终留给用户的原因。
License
MIT。Rust 依赖的许可证见 Cargo.lock 与各 crate 的 LICENSE;发布包只包含编译后的 helper。