# Task desktop / 独立任务桌面 DeepSeek Harness 0.2.0-rc.2+ is the primary integration. In Settings → Win-Pilot, save the computer environment; it applies to the next computer operation in the same chat. The card distinguishes installed and running versions. A managed release can reload after active operations finish and control is released; an older running plugin or an in-place update may require a host restart. Switching cancels and cleans up the previous control session and browser claims. Fresh discovery is required before input on the selected desktop; cleanup failure blocks all dispatch. 默认关闭。在 DeepSeek Harness 设置 → Win-Pilot 中选择电脑操作环境并保存,同一聊天的下一次电脑操作即可切换。设置页区分已安装与运行中的版本;托管版本可以等待当前操作完成、释放控制后重新加载,旧运行版本或原地更新可能仍需重启宿主。切换前取消并清理旧控制会话及浏览器控制权,切换后重新观察再输入;清理失败则不执行后续动作。 | Setting / 设置 | Behavior / 行为 | |---|---| | Current / 当前桌面 | Background-first; model schemas, skills and diagnostics do not advertise an alternate desktop. 后台优先,不向 AI 提供独立桌面选项。 | | Task / 独立任务桌面 | All native actions, clipboard, observation and recovery use the owned child session. Failure does not fall back to the main desktop. 操作及观察绑定同一子会话,失败不转到主桌面。 | The compact light window has no native caption. Expand to interact directly. Before manual input is delivered, the worker confirms AI input has stopped. After 10 seconds without input and without a held key/button, the AI can resume. Pin changes always-on-top; close ends only the owned task session. Right-click the toolbar for local sign-in settings. 浅色小窗去掉原生标题栏。放大后直接操作;首次手动输入会等待 AI 停止确认。空闲10秒且没有按住鼠标或按键后,AI 可继续。置顶按钮切换置顶,关闭只清理本窗口拥有的任务会话。右键工具栏可进入本机登录设置。 Windows feature setup may require one UAC authorization. Optional remembered sign-in uses current-user DPAPI and an application-owned Credential Manager entry. Passwords are entered only in the local dialog and do not pass through model messages. Changed Windows users, passwords or policy can require setup again. .NET 8 Windows Desktop Runtime and supported Windows child sessions are required. 首次 Windows 功能准备可能需要一次系统授权。可选记住登录使用当前用户 DPAPI 和应用专用凭据条目,密码不进入模型消息;更换用户、密码或策略后可能需重新设置。需要 .NET 8 Windows Desktop Runtime 与 Windows 子会话支持。 Verification includes pure protocol/routing tests, real DSH Config/volatile integration, production-form layout checks, and explicit authorized live child-session fixture tests. Unit tests do not prove every Windows policy, DPI or monitor layout. No claim is made that every startup application is suppressed or that remembered sign-in was verified after a reboot. Other applications can occupy the single Windows child-session facility. 验证包括协议/路由测试、真实 DSH Config 与动态引用集成、生产窗体检查和授权本机子会话实测。单元测试不代表覆盖所有系统策略、DPI 和多显示器布局;没有宣称禁止全部启动应用,也没有把跨进程记住登录写成已通过重启验证。其他应用可能占用 Windows 子会话。 ## Where the code lives / 代码位置 | Layer / 层 | Path / 路径 | Role / 作用 | |---|---|---| | Host plugin / 宿主插件 | `lib/index.js`, `lib/task-desktop.js` | Register the tool; own the native host process and route every task action to it. 注册工具、管理原生宿主进程并把任务操作全部路由过去。 | | Native host / 原生宿主 | `native/task-desktop/Program.cs` | `DesktopWindow` (window + RDP connect + worker protocol + shutdown) and `Worker` (runs inside the child session). | | Credentials / 凭据 | `native/task-desktop/WindowsLoginStore.cs`, `RdpCredentials.cs`, `LoginSettingsDialog.cs` | DPAPI + Credential Manager storage, real COM vtable setters, local sign-in form. | | Session helpers / 会话辅助 | `ChildSessionQuery.cs`, `ChildSessionTask.cs`, `ExplorerStartupMarker.cs`, `PipeIdentity.cs`, `ProcessIdentity.cs` | Baseline-diff session resolution, scheduled worker launch, startup markers, peer identity. | | Build output / 构建产物 | `lib/task-desktop/`, `lib/wgc/` | Generated by `npm run build:desktop` and `npm run build:wgc`; gitignored, so a fresh clone must build before the task desktop can start. | | Dev-only / 仅开发 | `native/task-desktop-tests/`, `native/task-desktop-ui-tests/`, `native/task-desktop-fixture/`, `native/task-desktop-startup-sentinel/` | Not shipped. Pure session/protocol checks, offscreen production-form checks, and the live fixture. | `lib/task-desktop.js` (the plugin module) and `lib/task-desktop/` (generated host binaries) share a stem but are unrelated; import the module by its full `.js` path. ## Child-session recovery / 子会话恢复 When an authenticated child session is logged off externally, the native window shuts down and confirms cleanup. The next observation may start a replacement host after its output has fully drained. A click or text input cannot trigger this replacement; observe first. In-flight actions are never replayed. Unexpected host crashes, missing/negative cleanup acknowledgements, and explicitly closed providers remain blocked. 已认证的子会话被外部注销时,原生窗口结束并确认清理;输出完全收尾后,下次观察可以创建新宿主。点击或输入不能直接触发重建,须先重新观察。进行中的动作不会重放;进程崩溃、清理确认缺失或失败、明确关闭的 provider 均保持阻止操作。 Live verification on 2026-10-29: owned session 5 was logged off, cleanup was confirmed, and the same provider started session 6 and returned list_displays without restarting DSH. This does not establish prevention of UU/GameViewer session selection or CrossDeviceResume shell activation. 2026-10-29 本机验证:注销本次创建的会话5,清理确认后,同一 provider 创建会话6并成功返回显示器信息,无需重启 DSH。此结果不代表已修复 UU 的会话选择或 CrossDeviceResume 的 shell 激活错误。 ## Desktop geometry and scale / 桌面尺寸与缩放 The child session inherits the primary display's pixel size and scale factor, so a window drawn by the AI lands on the same pixels the user sees. `DesktopScaleFactor` is a VARIANT that only accepts `VT_UI4`; an Int32 VARIANT is rejected with `E_INVALIDARG`. A rejected scale is logged through `desktop_geometry` and is not fatal: the session still runs, at 100%. 子会话采用主显示器的像素尺寸与缩放比例,AI 绘制的窗口因此与用户看到的落在相同像素上。`DesktopScaleFactor` 只接受 `VT_UI4`,Int32 VARIANT 会被 `E_INVALIDARG` 拒绝。缩放被拒时通过 `desktop_geometry` 记录,不视为致命错误:会话仍以 100% 运行。 Measured on 2026-10-29: a 1920x1080 @125% main desktop produced a child session reporting 1920x1080 with `desktop_geometry` = `1920x1080@125%`. Closing the task desktop also protects the main desktop's scale. Logging off the child session sends a remote-control streamer that follows the newest session (UU's GameViewerServer) back to the user's session, and on 2026-10-30 UU's privacy screen then dropped the laptop panel from 125% to 100% two seconds after logoff. The host records each active monitor's scale before logoff, and a detached `--restore-display-scale` watcher puts back any recorded monitor whose scale changes in the next 60 seconds, at most three times per monitor. 关闭独立桌面时同时保护主桌面的缩放。注销子会话会让跟随最新会话的远程推流进程(UU 的 GameViewerServer)回到用户会话;2026-10-30 实测,UU 的隐私屏随后在注销 2 秒后把笔记本屏幕从 125% 改成了 100%。宿主在注销前记录每块活动显示器的缩放,并启动独立的 `--restore-display-scale` 守护进程:之后 60 秒内被改动的已记录显示器会被恢复,每块最多 3 次。 2026-10-29 实测:主桌面 1920x1080 @125%,子会话报告 1920x1080,`desktop_geometry` = `1920x1080@125%`。 ## Remote-control conflict / 远程控制冲突 Windows exposes no API that reserves a child session from other programs. `GameViewerServer.exe` 4.42.1.2835 (UU remote) imports `WTSEnumerateSessionsW`, `WTSQuerySessionInformationW` and `WTSGetActiveConsoleSessionId` but not `WTSGetChildSessionId`, so it treats our child session as an ordinary logged-on desktop: it enumerates sessions, finds one that is `WTSActive` with a logged-on user, and launches its streamer there. A child session is exactly that, and its `WTSClientProtocolType` is 2 (loopback RDP), so a user who connects while the task desktop exists is shown the AI desktop. Windows 没有任何接口能把子会话对其他程序保留起来。UU 远程的 `GameViewerServer.exe` 4.42.1.2835 导入了 `WTSEnumerateSessionsW`、`WTSQuerySessionInformationW` 与 `WTSGetActiveConsoleSessionId`,却没有导入 `WTSGetChildSessionId`,于是把子会话当成普通已登录桌面:枚举会话、挑一个 `WTSActive` 且已登录用户的会话,把串流进程投进去。子会话正是这个状态,且其 `WTSClientProtocolType` 为 2(回环 RDP),所以任务桌面存在期间接入远控的用户会看到 AI 的桌面。 `lib/remote-session-guard.js` reads WTS and the process table, and never creates, connects or logs off anything. Two different signals are needed, because neither alone is sound: - **Before creating a session**: an active session whose `WTSClientProtocolType` is non-zero and which is *not* our child session is a real external remote session (Remote Desktop and the like). `WTSGetActiveConsoleSessionId` is not enough on its own: it keeps returning the console while a child session is also active. - **While a session runs**: the child session is itself loopback RDP and is active by definition, so its mere existence must never be treated as exposure — that would block every healthy run. What proves exposure is a remote-control streamer process (UU's `GameViewerServer`, AnyDesk, ToDesk, RustDesk, ...) running *inside* the child session, which is what appears when a phone attaches. `lib/remote-session-guard.js` 会读取 WTS 与进程表,不创建、不连接、不注销任何东西。需要两个不同信号,因为单靠任何一个都不成立: - **创建会话之前**:某个活动会话的 `WTSClientProtocolType` 非 0 且**不是**我们的子会话,才是真正的外部远程会话(远程桌面等)。仅用 `WTSGetActiveConsoleSessionId` 不够:子会话活动时它仍返回控制台会话号。 - **会话运行期间**:子会话本身就是回环 RDP、按定义处于活动状态,所以它的存在绝不能当作暴露——否则每次正常运行都会被拦截。真正的证据是远控串流进程(UU 的 `GameViewerServer`、AnyDesk、ToDesk、RustDesk 等)运行在子会话**内部**,手机接入时正是这个状态。 `remoteControlPolicy` chooses the outcome: `fallback` (default) runs the action on the current desktop and reports `remote_control_fallback`; `refuse` returns `task_desktop_remote_in_use` without running anything. Both decisions are made before a child session exists, so the remote screen cannot move. The reverse order — desktop already open, remote attaches afterwards — is reported by `inspectExposure()` instead of being silently tolerated. `remoteControlPolicy` 决定处理方式:`fallback`(默认)改在当前桌面执行并回报 `remote_control_fallback`;`refuse` 直接返回 `task_desktop_remote_in_use`,不执行任何动作。两种判断都发生在子会话创建之前,因此远程画面不会被带走。反向顺序——桌面已存在、之后才接入远控——由 `inspectExposure()` 报告,而不是静默容忍。 Measured on 2026-10-29: with the phone attached, `status` reported `remote_in_use`/`allow=false` for the live child session and `login` was refused before any native host started. 2026-10-29 实测:手机接入期间,`status` 对活动子会话报告 `remote_in_use`/`allow=false`,`login` 在任何原生宿主启动前即被拒绝。