1
/**2
* Computer use through the in-process Cua Driver native SDK and its own tools.3
* @module @deepseek-ai/dsh-experimental-computer-use-cua-driver-native4
*/6
import type { Context } from '@deepseek-ai/cordis'7
import Schema from '@deepseek-ai/schemastery'8
import { ComputerUseProviderName } from '@deepseek-ai/dsh-computer-use/brand'9
import { createMcpToolDefinition } from '@deepseek-ai/dsh-mcp-client'10
import { z } from 'zod'11
import type { CuaDriver as NativeDriver } from '@trycua/cua-driver'12
import type {} from '@deepseek-ai/dsh-computer-use'13
import type {} from '@deepseek-ai/dsh-system-prompt'14
import type {} from '@deepseek-ai/dsh-tools'16
/** Cordis plugin identity for the native Cua Driver provider. */17
export const name = 'experimental-computer-use-cua-driver-native'19
/** Services required before the native runtime can publish tools. */20
export const inject = ['computerUse', 'tools', 'systemPrompt']22
/** The native provider uses the installed SDK's same-process defaults. */23
export const Config = Schema.object({})25
const ToolCatalog = z.object({26
tools: z.array(z.object({27
name: z.string().min(1),28
description: z.string().optional(),29
inputSchema: z.record(z.string(), z.unknown()),30
outputSchema: z.unknown().optional(),31
})),32
})34
/** DeepSeek's function-name alphabet and maximum length are protocol constants. */35
const TOOL_NAME = /^[A-Za-z0-9_-]{1,64}$/u37
const GUIDANCE = `Cua Driver native computer-use tools operate the host desktop. Discover the exact app and window, then get a fresh window snapshot before acting. Use element_token from that snapshot, or coordinates from its screenshot. A new snapshot of that window invalidates its earlier element tokens. Select either target or the legacy pid/window_id fields; do not combine them.39
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.41
On macOS, cursor-overlay operations may return facility_unavailable even when screenshots and input work.`43
/**44
* Own one native runtime and expose its catalog through the MCP result adapter.45
* Startup failures roll back every registration. Unload removes tools, aborts46
* calls and image admission, awaits settlement and SDK shutdown, then releases computer use.47
* @param ctx - context providing the exclusive registration and tool services.48
* @returns after native import, runtime creation, and tool discovery complete.49
*/50
export async function apply(ctx: Context): Promise<void> {51
const lifetime = new AbortController()52
const pending = new Set<Promise<unknown>>()53
let driver: NativeDriver | undefined54
// Cordis announces disposal before it awaits asynchronous plugin startup.55
ctx.on('internal/plugin', (fiber) => {56
if (fiber === ctx.fiber && fiber.uid === null) lifetime.abort()57
}, { global: true })58
let ready: Promise<void> = Promise.resolve()59
const dispose = ctx.effect(function* () {60
yield ctx.computerUse.register(ComputerUseProviderName('cua-driver-native'))61
yield async () => {62
lifetime.abort()63
// apply() reports startup failure; teardown still owns its native handle.64
await ready.catch(() => {})65
await Promise.allSettled(pending)66
if (driver !== undefined) {67
await driver.shutdown()68
driver.uniffiDestroy()69
}70
}71
const child = ctx.plugin({72
name: 'computer-use-cua-driver-native-runtime',73
inject: ['tools', 'systemPrompt'],74
apply: mountRuntime,75
})76
yield child.dispose77
ready = Promise.resolve(child).then(() => {})78
}, 'computer-use-cua-driver-native.runtime')79
try {80
await ready81
} catch (error) {82
await dispose()83
throw error84
}86
/** The child owns tool registrations; the outer effect owns native teardown. */87
async function mountRuntime(inner: Context): Promise<void> {88
const { CuaDriver } = await import('@trycua/cua-driver')89
lifetime.signal.throwIfAborted()90
// The generated constructor returns its class with an owned binding handle,91
// but declares only CuaDriverLike, which omits uniffiDestroy().92
const activeDriver = driver = CuaDriver.create(undefined) as NativeDriver93
const catalog = ToolCatalog.parse(JSON.parse(await activeDriver.listToolsJson({ signal: lifetime.signal })))94
lifetime.signal.throwIfAborted()95
const names = new Set<string>()96
for (const tool of catalog.tools) {97
const publicName = `cua_driver_native__${tool.name}`98
if (!TOOL_NAME.test(publicName)) {99
throw new Error(`Cua Driver tool "${tool.name}" exceeds the supported function-name format`)100
}101
if (names.has(publicName)) throw new Error(`Cua Driver listed tool "${tool.name}" more than once`)102
names.add(publicName)103
const definition = createMcpToolDefinition(inner, {104
name: publicName,105
rawName: tool.name,106
description: tool.description ?? '',107
inputSchema: tool.inputSchema,108
outputSchema: tool.outputSchema,109
async call(args, execution) {110
const combined = AbortSignal.any([execution.signal, lifetime.signal])111
combined.throwIfAborted()112
const result = await activeDriver.callTool(tool.name, JSON.stringify(args), { signal: combined })113
combined.throwIfAborted()114
return JSON.parse(result.rawJson) as unknown115
},116
})117
inner.tools.register(definition)118
}119
inner.on('tools/execute', async (exec, next) => {120
if (!names.has(exec.name)) return next()121
const upstream = exec.signal122
exec.signal = AbortSignal.any([upstream, lifetime.signal])123
const operation = Promise.resolve().then(next)124
pending.add(operation)125
try {126
return await operation127
} finally {128
pending.delete(operation)129
exec.signal = upstream130
}131
})132
inner.systemPrompt.section({133
name: 'computer-use:cua-driver-native',134
order: inner.systemPrompt.getSectionOrder('TOOL_COMPUTER_USE'),135
text: GUIDANCE,136
})137
}138
}