Sfoglia il codice sorgente

fix(computer-use): cancel image admission and fix source snapshots

Tianyi Cui 2 settimane fa
parent
commit
cd97419e81

+ 2 - 2
.agents/notes/implemented/architecture/2026-09-12-computer-use-provider-registration.i18n.yaml

@@ -2,5 +2,5 @@
 # side as of the last confirmed-consistent state. Both languages carry equal authority;
 # after editing either side, bring the other along and re-record with:
 #   pnpm run verify-translation-pairing --write .agents/notes/implemented/architecture/2026-09-12-computer-use-provider-registration.md
-2026-09-12-computer-use-provider-registration.md: ee408a6f09576b1898113dafafc053241b8f5d45
-2026-09-12-computer-use-provider-registration.zh.md: ea23bdf98b168c05576a4a74c14507c579ab8ccc
+2026-09-12-computer-use-provider-registration.md: 52e51ac9c4f89b6ad730e647280ba3f0c04ea3c9
+2026-09-12-computer-use-provider-registration.zh.md: 2d8802e1d8cb3c5262cbc2ba6185262f949bca01

+ 1 - 1
.agents/notes/implemented/architecture/2026-09-12-computer-use-provider-registration.md

@@ -16,7 +16,7 @@ The DSH capability is named **computer use**. [`dsh-computer-use`](../../../../p
 
 Each integration exposes the upstream tool catalog. MCP result conversion stays in `dsh-mcp-client`, whose callback-based tool adapter also converts native Cua Driver results. The computer-use service has no dependency on that adapter or either provider.
 
-Provider teardown retains the registration until tool admission stops and owned work and resources close. A grouped Cordis effect orders that cleanup; separate effects may dispose concurrently. Concurrent Sessions remain caller-coordinated because a provider registration does not own an observe, act, and verify workflow.
+Provider teardown retains the registration until tool admission stops and owned work and resources close. A grouped Cordis effect orders that cleanup; separate effects may dispose concurrently. The native provider uses `tools/execute` to share cancellation across native calls and screenshot admission while preserving execution identity. Concurrent Sessions remain caller-coordinated because a provider registration does not own an observe, act, and verify workflow.
 
 ## Alternatives considered
 

+ 1 - 1
.agents/notes/implemented/architecture/2026-09-12-computer-use-provider-registration.zh.md

@@ -16,7 +16,7 @@ DSH 能力称为 **computer use(计算机操作)**。[`dsh-computer-use`](..
 
 各集成暴露上游工具目录。MCP 结果转换保留在 `dsh-mcp-client` 中,其基于回调的工具适配函数也转换原生 Cua Driver 结果。计算机操作服务不依赖该适配函数或任一提供方。
 
-提供方卸载时保留注册,直到停止接收工具调用且自有工作和资源关闭。分组 Cordis effect 为此清理排序;独立 effect 可能并发清理。并发 Session 由调用方协调,因为提供方注册不拥有观察、操作和验证流程。
+提供方卸载时保留注册,直到停止接收工具调用且自有工作和资源关闭。分组 Cordis effect 为此清理排序;独立 effect 可能并发清理。原生提供方通过 `tools/execute` 在原生调用和截图准入之间共享取消信号,同时保留执行标识。并发 Session 由调用方协调,因为提供方注册不拥有观察、操作和验证流程。
 
 ## Alternatives considered
 

+ 2 - 2
docs/event-producer-consumer.i18n.yaml

@@ -2,5 +2,5 @@
 # side as of the last confirmed-consistent state. Both languages carry equal authority;
 # after editing either side, bring the other along and re-record with:
 #   pnpm run verify-translation-pairing --write docs/event-producer-consumer.md
-event-producer-consumer.md: 5b21c8d646226c2f19426d88245188e2b5555015
-event-producer-consumer.zh.md: 912d3fbfb1b03a8cbf59649b631cf08624df45b7
+event-producer-consumer.md: eee881cec5d668d2ff779ae4ba08eceebe9c8691
+event-producer-consumer.zh.md: a0b0a3f39b3d0b7d260fc55234410f55ff18f5df

+ 1 - 1
docs/event-producer-consumer.md

@@ -62,7 +62,7 @@ This matrix shows which packages dispatch each harness-owned event and which pac
 | `system-prompt/assemble` | `waterfall` | [`packages/core/system-prompt/src/index.ts:31`](../packages/core/system-prompt/src/index.ts) | [`system-prompt`](../packages/core/system-prompt) (`waterfall`) | [`agent`](../packages/core/agent), [`agent-presets`](../packages/preset/agent-presets), [`session-reference`](../packages/context/session-reference), [`system-prompt`](../packages/core/system-prompt) |
 | `system-prompt/change` | `emit` | [`packages/core/system-prompt/src/index.ts:37`](../packages/core/system-prompt/src/index.ts) | [`system-prompt`](../packages/core/system-prompt) (`emit`) | - |
 | `tools/change` | `emit` | [`packages/core/tools/src/index.ts:200`](../packages/core/tools/src/index.ts) | [`agent-presets`](../packages/preset/agent-presets) (`emit`), [`tools`](../packages/core/tools) (`emit`) | [`tool-subagent`](../packages/subagent/tool-subagent) |
-| `tools/execute` | `waterfall` | [`packages/core/tools/src/index.ts:156`](../packages/core/tools/src/index.ts) | [`tools`](../packages/core/tools) (`waterfall`) | [`session-checkpoint-policy`](../packages/session/session-checkpoint-policy), `timeout-policy` |
+| `tools/execute` | `waterfall` | [`packages/core/tools/src/index.ts:156`](../packages/core/tools/src/index.ts) | [`tools`](../packages/core/tools) (`waterfall`) | `computer-use-cua-driver-native`, [`session-checkpoint-policy`](../packages/session/session-checkpoint-policy), `timeout-policy` |
 | `tools/post-execute` | `waterfall` | [`packages/core/tools/src/index.ts:168`](../packages/core/tools/src/index.ts) | [`tools`](../packages/core/tools) (`waterfall`) | [`hooks-claude-code`](../packages/hooks/hooks-claude-code), [`hooks-codex`](../packages/hooks/hooks-codex), [`repeat-tool-reminder`](../packages/guard/repeat-tool-reminder), [`spill-policy`](../packages/spill/spill-policy), [`tool-fs-search`](../packages/fs/tool-fs-search) |
 | `tools/pre-execute` | `waterfall` | [`packages/core/tools/src/index.ts:145`](../packages/core/tools/src/index.ts) | [`tools`](../packages/core/tools) (`waterfall`) | [`hooks-claude-code`](../packages/hooks/hooks-claude-code), [`hooks-codex`](../packages/hooks/hooks-codex), [`tool-jobs`](../packages/jobs/tool-jobs) |
 | `tools/ptc-dispatch-log` | `waterfall` | [`packages/core/tools/src/index.ts:182`](../packages/core/tools/src/index.ts) | [`tools`](../packages/core/tools) (`waterfall`) | [`spill-policy`](../packages/spill/spill-policy) |

+ 1 - 1
docs/event-producer-consumer.zh.md

@@ -64,7 +64,7 @@
 | `system-prompt/assemble` | `waterfall` | [`packages/core/system-prompt/src/index.ts:31`](../packages/core/system-prompt/src/index.ts) | [`system-prompt`](../packages/core/system-prompt) (`waterfall`) | [`agent`](../packages/core/agent), [`agent-presets`](../packages/preset/agent-presets), [`session-reference`](../packages/context/session-reference), [`system-prompt`](../packages/core/system-prompt) |
 | `system-prompt/change` | `emit` | [`packages/core/system-prompt/src/index.ts:37`](../packages/core/system-prompt/src/index.ts) | [`system-prompt`](../packages/core/system-prompt) (`emit`) | - |
 | `tools/change` | `emit` | [`packages/core/tools/src/index.ts:200`](../packages/core/tools/src/index.ts) | [`agent-presets`](../packages/preset/agent-presets) (`emit`), [`tools`](../packages/core/tools) (`emit`) | [`tool-subagent`](../packages/subagent/tool-subagent) |
-| `tools/execute` | `waterfall` | [`packages/core/tools/src/index.ts:156`](../packages/core/tools/src/index.ts) | [`tools`](../packages/core/tools) (`waterfall`) | [`session-checkpoint-policy`](../packages/session/session-checkpoint-policy), `timeout-policy` |
+| `tools/execute` | `waterfall` | [`packages/core/tools/src/index.ts:156`](../packages/core/tools/src/index.ts) | [`tools`](../packages/core/tools) (`waterfall`) | `computer-use-cua-driver-native`, [`session-checkpoint-policy`](../packages/session/session-checkpoint-policy), `timeout-policy` |
 | `tools/post-execute` | `waterfall` | [`packages/core/tools/src/index.ts:168`](../packages/core/tools/src/index.ts) | [`tools`](../packages/core/tools) (`waterfall`) | [`hooks-claude-code`](../packages/hooks/hooks-claude-code), [`hooks-codex`](../packages/hooks/hooks-codex), [`repeat-tool-reminder`](../packages/guard/repeat-tool-reminder), [`spill-policy`](../packages/spill/spill-policy), [`tool-fs-search`](../packages/fs/tool-fs-search) |
 | `tools/pre-execute` | `waterfall` | [`packages/core/tools/src/index.ts:145`](../packages/core/tools/src/index.ts) | [`tools`](../packages/core/tools) (`waterfall`) | [`hooks-claude-code`](../packages/hooks/hooks-claude-code), [`hooks-codex`](../packages/hooks/hooks-codex), [`tool-jobs`](../packages/jobs/tool-jobs) |
 | `tools/ptc-dispatch-log` | `waterfall` | [`packages/core/tools/src/index.ts:182`](../packages/core/tools/src/index.ts) | [`tools`](../packages/core/tools) (`waterfall`) | [`spill-policy`](../packages/spill/spill-policy) |

+ 2 - 2
packages/experimental/computer-use-cua-driver-native/README.i18n.yaml

@@ -2,5 +2,5 @@
 # side as of the last confirmed-consistent state. Both languages carry equal authority;
 # after editing either side, bring the other along and re-record with:
 #   pnpm run verify-translation-pairing --write packages/experimental/computer-use-cua-driver-native/README.md
-README.md: deaa398778141f1a7d90fb0b5d58dd42a7f4937a
-README.zh.md: 01b8b4907447f581896e93311da3b828c449ab75
+README.md: 28e47f17020297ef7e1c2fa6656c8fda0b808a8f
+README.zh.md: ce25309e77fcd05836b4fccddd2482ecf2f1ad17

+ 2 - 2
packages/experimental/computer-use-cua-driver-native/README.md

@@ -58,7 +58,7 @@ env -u NODE_USE_ENV_PROXY DSH_COMPUTER_USE_NATIVE_E2E=1 node node_modules/vitest
 <details>
 <summary>Implementation internals — click to expand</summary>
 
-The provider reserves the shared computer-use registration before loading native code. A child plugin owns discovery, model tools, guidance, and the native runtime. The parent retains the registration until child teardown has removed tools, interrupted work, awaited call settlement, and completed native shutdown. Cancellation does not undo input already delivered to an application.
+The provider reserves the shared computer-use registration before loading native code. A child plugin owns discovery, model tools, guidance, and the native runtime. The parent retains the registration until child teardown has removed tools, interrupted native calls and image-capability admission, awaited settlement, and completed native shutdown. Cancellation does not undo input already delivered to an application.
 
 | File | Role |
 |---|---|
@@ -96,7 +96,7 @@ Cua Driver native computer-use tools operate the host desktop. Discover the exac
 
 Prefer background delivery. A refusal does not authorize a foreground retry. Verify the requested outcome from fresh state after an action; a delivered click alone does not prove the outcome. After cancellation, inspect current state before retrying because completed input is not rolled back. Other sessions and applications may change the same desktop.
 
-The native runtime inherits the launching host's desktop permissions. On macOS, cursor-overlay operations can be unavailable in a headless Node host even when screenshots and input work.
+On macOS, cursor-overlay operations may return facility_unavailable even when screenshots and input work.
 ```
 
 #### Token effect

+ 2 - 2
packages/experimental/computer-use-cua-driver-native/README.zh.md

@@ -58,7 +58,7 @@ env -u NODE_USE_ENV_PROXY DSH_COMPUTER_USE_NATIVE_E2E=1 node node_modules/vitest
 <details>
 <summary>实现内部——点击展开</summary>
 
-此提供者在加载原生代码前占用共享电脑操作注册名额。子插件拥有目录发现、模型工具、指导文本和原生运行时。父插件保留注册名额,直到子插件卸载完成工具移除、工作中断、调用结束等待和原生关闭。取消不会撤销已经传给应用的输入。
+此提供者在加载原生代码前占用共享电脑操作注册名额。子插件拥有目录发现、模型工具、指导文本和原生运行时。父插件保留注册名额,直到子插件卸载完成工具移除、中断原生调用和图像能力准入、等待调用结束及原生关闭。取消不会撤销已经传给应用的输入。
 
 | 文件 | 职责 |
 |---|---|
@@ -96,7 +96,7 @@ Cua Driver native computer-use tools operate the host desktop. Discover the exac
 
 Prefer background delivery. A refusal does not authorize a foreground retry. Verify the requested outcome from fresh state after an action; a delivered click alone does not prove the outcome. After cancellation, inspect current state before retrying because completed input is not rolled back. Other sessions and applications may change the same desktop.
 
-The native runtime inherits the launching host's desktop permissions. On macOS, cursor-overlay operations can be unavailable in a headless Node host even when screenshots and input work.
+On macOS, cursor-overlay operations may return facility_unavailable even when screenshots and input work.
 ```
 
 #### Token 影响

+ 15 - 13
packages/experimental/computer-use-cua-driver-native/src/index.ts

@@ -38,12 +38,12 @@ const GUIDANCE = `Cua Driver native computer-use tools operate the host desktop.
 
 Prefer background delivery. A refusal does not authorize a foreground retry. Verify the requested outcome from fresh state after an action; a delivered click alone does not prove the outcome. After cancellation, inspect current state before retrying because completed input is not rolled back. Other sessions and applications may change the same desktop.
 
-The native runtime inherits the launching host's desktop permissions. On macOS, cursor-overlay operations can be unavailable in a headless Node host even when screenshots and input work.`
+On macOS, cursor-overlay operations may return facility_unavailable even when screenshots and input work.`
 
 /**
  * Own one native runtime and expose its catalog through the MCP result adapter.
  * Startup failures roll back every registration. Unload removes tools, aborts
- * calls, awaits their settlement and SDK shutdown, then releases computer use.
+ * calls and image admission, awaits settlement and SDK shutdown, then releases computer use.
  * @param ctx - context providing the exclusive registration and tool services.
  * @returns after native import, runtime creation, and tool discovery complete.
  */
@@ -114,19 +114,21 @@ export async function apply(ctx: Context): Promise<void> {
           return JSON.parse(result.rawJson) as unknown
         },
       })
-      const execute = definition.execute.bind(definition)
-      definition.execute = async (args, exec) => {
-        lifetime.signal.throwIfAborted()
-        const operation = Promise.resolve().then(() => execute(args, exec))
-        pending.add(operation)
-        try {
-          return await operation
-        } finally {
-          pending.delete(operation)
-        }
-      }
       inner.tools.register(definition)
     }
+    inner.on('tools/execute', async (exec, next) => {
+      if (!names.has(exec.name)) return next()
+      const upstream = exec.signal
+      exec.signal = AbortSignal.any([upstream, lifetime.signal])
+      const operation = Promise.resolve().then(next)
+      pending.add(operation)
+      try {
+        return await operation
+      } finally {
+        pending.delete(operation)
+        exec.signal = upstream
+      }
+    })
     inner.systemPrompt.section({
       name: 'computer-use:cua-driver-native',
       order: inner.systemPrompt.getSectionOrder('TOOL_COMPUTER_USE'),

+ 87 - 0
packages/experimental/computer-use-cua-driver-native/tests/cancellation.spec.ts

@@ -0,0 +1,87 @@
+/** Provider unload owns screenshot admission as well as the native call. */
+
+import { mkdtemp, rm } from 'node:fs/promises'
+import { tmpdir } from 'node:os'
+import { join } from 'node:path'
+import { afterEach, beforeEach, expect, it, vi } from 'vitest'
+import { Context } from '@deepseek-ai/cordis'
+import LocalAttachmentStore from '@deepseek-ai/dsh-attachment-local'
+import { mountAgentLoopTestDependencies, mountAgentLoopTestHarness } from '@deepseek-ai/dsh-agent-loop-testkit'
+import ComputerUseRegistry from '@deepseek-ai/dsh-computer-use'
+import { LlmAdapter, ToolCallId } from '@deepseek-ai/dsh-llm'
+import type { GenerateOptions, LlmResolvedModelInfo, StreamChunk } from '@deepseek-ai/dsh-llm'
+import { SessionId } from '@deepseek-ai/dsh-session'
+import type { ToolExecution } from '@deepseek-ai/dsh-tools'
+import * as NativeProvider from '../src/index.ts'
+import { fixture, resetFixture } from './fixtures/cua-driver.ts'
+
+vi.mock('@trycua/cua-driver', async () => import('./fixtures/cua-driver.ts'))
+
+let ctx: Context
+let root: string | undefined
+
+beforeEach(async () => {
+  resetFixture()
+  ctx = new Context()
+  await mountAgentLoopTestDependencies(ctx)
+  await ctx.plugin(ComputerUseRegistry)
+  root = await mkdtemp(join(tmpdir(), 'dsh-native-cancellation-'))
+  await ctx.plugin(LocalAttachmentStore, { dshHome: root })
+})
+
+afterEach(async () => {
+  await ctx.fiber.dispose()
+  if (root !== undefined) await rm(root, { recursive: true, force: true })
+  root = undefined
+})
+
+it('aborts model admission on unload, restores caller cancellation, and closes the native runtime', async () => {
+  const resolving = Promise.withResolvers<AbortSignal>()
+  const resolved = Promise.withResolvers<LlmResolvedModelInfo>()
+  class BlockingModel extends LlmAdapter {
+    override resolveModel(_provider: string, _model: string, signal?: AbortSignal): Promise<LlmResolvedModelInfo> {
+      if (signal === undefined) throw new Error('Image admission must provide cancellation')
+      signal.throwIfAborted()
+      const abort = () => { resolved.reject(new Error('Model admission canceled')) }
+      signal.addEventListener('abort', abort, { once: true })
+      resolving.resolve(signal)
+      return resolved.promise.finally(() => { signal.removeEventListener('abort', abort) })
+    }
+
+    stream(_options: GenerateOptions): AsyncIterable<StreamChunk> {
+      throw new Error('This fixture only resolves image capabilities')
+    }
+  }
+  ctx.llm.registerAdapter(['visual'], new BlockingModel())
+  const harness = await mountAgentLoopTestHarness(ctx)
+  const agent = await harness.create(SessionId('native-admission'), { provider: 'visual', model: 'vision' })
+  const fiber = ctx.plugin(NativeProvider)
+  await fiber
+  let dispatch: ToolExecution | undefined
+  ctx.on('tools/pre-execute', async (exec, next) => {
+    dispatch = exec
+    return next()
+  })
+  const controller = new AbortController()
+  const result = ctx.tools.execute({
+    agent, signal: controller.signal, callId: ToolCallId('native-admission'),
+    name: 'cua_driver_native__get_window_state', arguments: { pid: 9, window_id: 7 },
+  })
+  const admissionSignal = await resolving.promise
+  const disposal = fiber.dispose()
+  try {
+    await vi.waitFor(() => { expect(admissionSignal.aborted).toBe(true) })
+    await disposal
+    expect((await result).isError).toBe(true)
+    expect(controller.signal.aborted).toBe(false)
+    expect(dispatch?.signal).toBe(controller.signal)
+    expect(ctx.computerUse.providerName).toBeUndefined()
+    expect(ctx.tools.schemas()).toEqual([])
+    expect(fixture.shutdowns).toBe(1)
+    expect(fixture.destroys).toBe(1)
+  } finally {
+    resolved.reject(new Error('Test cleanup'))
+    await result
+    await disposal
+  }
+})

+ 62 - 0
packages/experimental/computer-use-cua-driver-native/tests/provider.spec.ts

@@ -95,6 +95,38 @@ describe('Cua Driver native provider', () => {
     expect(result.content).toEqual([{ type: 'text', text: 'Error: background_unavailable' }])
   })
 
+  it('leaves an unrelated tool running when the native provider unloads', async () => {
+    const started = Promise.withResolvers<AbortSignal>()
+    const settled = Promise.withResolvers<boolean>()
+    const controller = new AbortController()
+    const fiber = ctx.plugin(NativeProvider)
+    await fiber
+    ctx.tools.register({
+      name: 'unrelated', description: 'Independent tool.', parameters: { type: 'object' },
+      output: { schema: { type: 'boolean' }, render: () => [{ type: 'text', text: 'Done.' }] },
+      execute(_args, exec) {
+        started.resolve(exec.signal)
+        return settled.promise
+      },
+    })
+    const result = ctx.tools.execute({
+      name: 'unrelated', callId: ToolCallId('unrelated'), arguments: {}, signal: controller.signal,
+    })
+    try {
+      const signal = await started.promise
+      await fiber.dispose()
+      expect(signal).toBe(controller.signal)
+      expect(signal.aborted).toBe(false)
+      expect(fixture.shutdowns).toBe(1)
+      expect(fixture.destroys).toBe(1)
+      expect(ctx.tools.schemas().map(tool => tool.name)).toEqual(['unrelated'])
+    } finally {
+      settled.resolve(true)
+      await result
+    }
+    expect((await result).isError).toBe(false)
+  })
+
   it('retains the reservation until aborted calls and native shutdown settle', async () => {
     const called: PromiseWithResolvers<void> = Promise.withResolvers()
     const callSettled = Promise.withResolvers<unknown>()
@@ -134,6 +166,36 @@ describe('Cua Driver native provider', () => {
     expect(fixture.destroys).toBe(1)
   })
 
+  it.each([['tools', ToolRuntime], ['systemPrompt', SystemPrompt]] as const)('closes the old native runtime before %s restarts it', async (_name, service) => {
+    const shutdownStarted: PromiseWithResolvers<void> = Promise.withResolvers()
+    const shutdownSettled: PromiseWithResolvers<void> = Promise.withResolvers()
+    fixture.shutdown = async () => {
+      shutdownStarted.resolve()
+      await shutdownSettled.promise
+    }
+    const fiber = ctx.plugin(NativeProvider)
+    await fiber
+    const dependency = [...ctx.registry.get(service)!.fibers][0]!
+    const restart = dependency.restart()
+    try {
+      await shutdownStarted.promise
+      expect(fixture.creates).toBe(1)
+      expect(fixture.destroys).toBe(0)
+      expect(ctx.computerUse.providerName).toBe('cua-driver-native')
+    } finally {
+      shutdownSettled.resolve()
+      await restart
+      await fiber
+    }
+    await vi.waitFor(() => { expect(fixture.creates).toBe(2) })
+    expect(fixture.shutdowns).toBe(1)
+    expect(fixture.destroys).toBe(1)
+    expect((await execute('check_permissions')).isError).toBe(false)
+    await fiber.dispose()
+    expect(fixture.shutdowns).toBe(2)
+    expect(fixture.destroys).toBe(2)
+  })
+
   it('interrupts discovery when unloaded during startup', async () => {
     const started: PromiseWithResolvers<void> = Promise.withResolvers()
     fixture.list = signal => new Promise((_resolve, reject) => {

+ 0 - 4
snapshots/session/computer-use-cua-driver-native/cordis.snapshot.yml

@@ -1,7 +1,3 @@
-# Keyless replay for the read-image success scenario. This profile patch swaps
-# the adapter and re-pins the recorded vision model. The replay catalog declares image input,
-# so the strict read_image gate accepts the route and the tool result carries
-# the durable image block.
 - id: llm-deepseek
   name: '@deepseek-ai/dsh-llm-deepseek'
   disabled: true

+ 0 - 4
snapshots/session/computer-use-cua-driver-native/cordis.yml

@@ -1,7 +1,3 @@
-# Image-scenario overlay: adds the durable attachment store the read_image tool
-# commits through. The store resolves its root from $DSH_HOME, which the
-# snapshot harness scopes per run, so the patch itself carries no attachment
-# path. The default-model row selects the shipped vision model.
 - id: agent-default-model
   name: '@deepseek-ai/dsh-agent-default-model'
   config:

+ 2 - 2
snapshots/session/computer-use-cua-driver-native/native-fixture.mjs

@@ -1,4 +1,4 @@
-/** Replace only the external native SDK while booting the real provider artifact. */
+/** Replace only the external SDK; the harness selects source or built provider code. */
 import { registerHooks } from 'node:module'
 
 export const name = 'computer-use-native-fixture'
@@ -16,6 +16,6 @@ export async function apply(ctx) {
     })
     return () => hooks.deregister()
   }, 'computer-use-native-fixture.module')
-  const provider = await import('../../../packages/experimental/computer-use-cua-driver-native/lib/index.js')
+  const provider = await import('@deepseek-ai/dsh-experimental-computer-use-cua-driver-native')
   await ctx.plugin(provider)
 }

+ 1 - 1
snapshots/session/computer-use-cua-driver-native/system-prompt.expected.md

@@ -35,4 +35,4 @@ Cua Driver native computer-use tools operate the host desktop. Discover the exac
 
 Prefer background delivery. A refusal does not authorize a foreground retry. Verify the requested outcome from fresh state after an action; a delivered click alone does not prove the outcome. After cancellation, inspect current state before retrying because completed input is not rolled back. Other sessions and applications may change the same desktop.
 
-The native runtime inherits the launching host's desktop permissions. On macOS, cursor-overlay operations can be unavailable in a headless Node host even when screenshots and input work.
+On macOS, cursor-overlay operations may return facility_unavailable even when screenshots and input work.