dsh-vision-router 2.1.2 → 2.1.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +1 -1
- package/README.zh.md +1 -1
- package/docs/architecture/compat-inventory.md +6 -6
- package/docs/architecture/dsh-compatibility-matrix.md +7 -6
- package/docs/architecture/dsh-support-window.md +36 -16
- package/docs/architecture/p3-compat-retirement.md +7 -5
- package/docs/architecture/p3-host-native-seams.md +3 -1
- package/docs/doctor.md +4 -1
- package/docs/releases/v2.1.3.md +34 -0
- package/docs/releases/v2.1.4.md +34 -0
- package/docs/v2-capability-routing.md +20 -11
- package/index.js +156 -129
- package/lib/catalog-corrections.js +2 -0
- package/lib/client-presentation-boundary-main.js +478 -2
- package/lib/client-presentation-boundary.js +121 -1
- package/lib/client.js +4 -4
- package/lib/depth-guidance.js +4 -4
- package/lib/doctor-cli-p0.js +3 -1
- package/lib/doctor-cli.js +4 -1
- package/lib/doctor-runtime.js +8 -3
- package/lib/dsh-support-window.js +15 -7
- package/lib/live-model-discovery.js +20 -13
- package/lib/local-vision-stabilizer.js +1 -1
- package/lib/mixed-router.js +8 -8
- package/lib/pi-ai-bridge-wire-compat.js +69 -10
- package/lib/runtime-i18n-boundary.js +27 -2
- package/lib/runtime-i18n.js +6 -6
- package/lib/session-affinity-runtime.js +104 -0
- package/lib/session-affinity.js +93 -0
- package/lib/settings-ia-client-prelude.js +1 -1
- package/lib/structured-bootstrap.js +2 -2
- package/lib/structured-flow-hardening.js +159 -158
- package/lib/tesseract-exec-compat.js +9 -36
- package/lib/vision-backend-runtime-policy.js +1 -0
- package/lib/vision-background-benchmark.js +79 -52
- package/lib/vision-background-failure-policy.js +1 -24
- package/lib/vision-background-stop-store.js +10 -13
- package/lib/vision-capability-benchmark-service.js +14 -2
- package/lib/vision-tool-runtime-boundary.js +20 -3
- package/lib/windows-desktop-capture.js +247 -0
- package/package.json +5 -5
- package/lib/windows-screenshot-dpi-compat.js +0 -148
|
@@ -0,0 +1,247 @@
|
|
|
1
|
+
import { execFile as importedExecFile } from 'node:child_process'
|
|
2
|
+
import { mkdtemp, rm } from 'node:fs/promises'
|
|
3
|
+
import { tmpdir } from 'node:os'
|
|
4
|
+
import path from 'node:path'
|
|
5
|
+
|
|
6
|
+
function quotePowerShellSingle(value) {
|
|
7
|
+
return `'${String(value).replace(/'/g, "''")}'`
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
export function buildPerMonitorWindowsScreenshotScript(outputPath) {
|
|
11
|
+
const quotedPath = quotePowerShellSingle(outputPath)
|
|
12
|
+
const source = String.raw`using System;
|
|
13
|
+
using System.Drawing;
|
|
14
|
+
using System.Runtime.InteropServices;
|
|
15
|
+
using System.Windows.Forms;
|
|
16
|
+
|
|
17
|
+
public static class DshVisionDesktopCapture
|
|
18
|
+
{
|
|
19
|
+
private static readonly IntPtr PerMonitorV2 = new IntPtr(-4);
|
|
20
|
+
private static readonly IntPtr PerMonitorV1 = new IntPtr(-3);
|
|
21
|
+
|
|
22
|
+
[DllImport("user32.dll")]
|
|
23
|
+
private static extern IntPtr SetThreadDpiAwarenessContext(IntPtr dpiContext);
|
|
24
|
+
|
|
25
|
+
private static IntPtr TrySetContext(IntPtr context)
|
|
26
|
+
{
|
|
27
|
+
try
|
|
28
|
+
{
|
|
29
|
+
return SetThreadDpiAwarenessContext(context);
|
|
30
|
+
}
|
|
31
|
+
catch (EntryPointNotFoundException)
|
|
32
|
+
{
|
|
33
|
+
return IntPtr.Zero;
|
|
34
|
+
}
|
|
35
|
+
catch (DllNotFoundException)
|
|
36
|
+
{
|
|
37
|
+
return IntPtr.Zero;
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
private static IntPtr EnterPerMonitorContext()
|
|
42
|
+
{
|
|
43
|
+
IntPtr previous = TrySetContext(PerMonitorV2);
|
|
44
|
+
if (previous == IntPtr.Zero)
|
|
45
|
+
{
|
|
46
|
+
previous = TrySetContext(PerMonitorV1);
|
|
47
|
+
}
|
|
48
|
+
if (previous == IntPtr.Zero)
|
|
49
|
+
{
|
|
50
|
+
throw new InvalidOperationException(
|
|
51
|
+
"could not enter a per-monitor DPI awareness context; refusing to return a potentially misaligned screenshot");
|
|
52
|
+
}
|
|
53
|
+
return previous;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
private static void RestoreContext(IntPtr previous)
|
|
57
|
+
{
|
|
58
|
+
try
|
|
59
|
+
{
|
|
60
|
+
SetThreadDpiAwarenessContext(previous);
|
|
61
|
+
}
|
|
62
|
+
catch
|
|
63
|
+
{
|
|
64
|
+
// The helper process exits immediately after capture. Restoration
|
|
65
|
+
// is still attempted so this method is correct if reused.
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
public static void ValidateDpiContext()
|
|
70
|
+
{
|
|
71
|
+
IntPtr previous = EnterPerMonitorContext();
|
|
72
|
+
RestoreContext(previous);
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
public static void Capture(string outputPath)
|
|
76
|
+
{
|
|
77
|
+
IntPtr previous = EnterPerMonitorContext();
|
|
78
|
+
try
|
|
79
|
+
{
|
|
80
|
+
Rectangle bounds = SystemInformation.VirtualScreen;
|
|
81
|
+
if (bounds.Width <= 0 || bounds.Height <= 0)
|
|
82
|
+
{
|
|
83
|
+
throw new InvalidOperationException("the Windows virtual screen has no drawable area");
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
using (var bitmap = new Bitmap(bounds.Width, bounds.Height))
|
|
87
|
+
using (var graphics = Graphics.FromImage(bitmap))
|
|
88
|
+
{
|
|
89
|
+
graphics.CopyFromScreen(bounds.X, bounds.Y, 0, 0, bounds.Size);
|
|
90
|
+
bitmap.Save(outputPath);
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
finally
|
|
94
|
+
{
|
|
95
|
+
RestoreContext(previous);
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
}`
|
|
99
|
+
|
|
100
|
+
return [
|
|
101
|
+
'Add-Type -AssemblyName System.Windows.Forms,System.Drawing',
|
|
102
|
+
`$__dshVisionCaptureSource = @'\n${source}\n'@`,
|
|
103
|
+
'try {',
|
|
104
|
+
" Add-Type -TypeDefinition $__dshVisionCaptureSource -ReferencedAssemblies 'System.Windows.Forms.dll','System.Drawing.dll' -ErrorAction Stop",
|
|
105
|
+
'} catch {',
|
|
106
|
+
" throw ('vision_screenshot: failed to initialize the Windows DPI-aware capture helper: ' + $_.Exception.Message)",
|
|
107
|
+
'}',
|
|
108
|
+
`[DshVisionDesktopCapture]::Capture(${quotedPath})`,
|
|
109
|
+
].join('\n')
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
// Snapshot the native function at module evaluation. Screenshot correctness
|
|
113
|
+
// must not depend on the process-wide promisify(execFile) compatibility seam
|
|
114
|
+
// used by legacy Tesseract stdin support.
|
|
115
|
+
const nativeExecFile = importedExecFile
|
|
116
|
+
|
|
117
|
+
function execFileAsync(file, args, options) {
|
|
118
|
+
return new Promise((resolve, reject) => {
|
|
119
|
+
nativeExecFile(file, args, options, (error, stdout, stderr) => {
|
|
120
|
+
if (error) {
|
|
121
|
+
if (error && typeof error === 'object') {
|
|
122
|
+
try {
|
|
123
|
+
error.stdout = stdout
|
|
124
|
+
error.stderr = stderr
|
|
125
|
+
} catch {
|
|
126
|
+
/* preserve the original child-process error */
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
reject(error)
|
|
130
|
+
return
|
|
131
|
+
}
|
|
132
|
+
resolve({ stdout, stderr })
|
|
133
|
+
})
|
|
134
|
+
})
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
export function isAsciiWindowsPath(value) {
|
|
138
|
+
return typeof value === 'string' && value !== '' && /^[\x20-\x7e]+$/.test(value)
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
function captureTempRoots(env, tmpRoot) {
|
|
142
|
+
const roots = []
|
|
143
|
+
const add = (value) => {
|
|
144
|
+
if (!isAsciiWindowsPath(value) || roots.includes(value)) return
|
|
145
|
+
roots.push(value)
|
|
146
|
+
}
|
|
147
|
+
add(tmpRoot)
|
|
148
|
+
add(env?.TEMP)
|
|
149
|
+
add(env?.TMP)
|
|
150
|
+
if (isAsciiWindowsPath(env?.SystemRoot)) add(path.win32.join(env.SystemRoot, 'Temp'))
|
|
151
|
+
if (isAsciiWindowsPath(env?.WINDIR)) add(path.win32.join(env.WINDIR, 'Temp'))
|
|
152
|
+
return roots
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
/**
|
|
156
|
+
* CodeDom-backed PowerShell Add-Type uses TEMP/TMP while compiling the small
|
|
157
|
+
* DPI-aware C# helper. On Windows profiles whose temp path contains non-ASCII
|
|
158
|
+
* characters that compilation can fail before capture starts (#409). Pick a
|
|
159
|
+
* writable ASCII scratch directory without ever mutating process.env.
|
|
160
|
+
*/
|
|
161
|
+
export async function createWindowsCaptureTempDir(options = {}) {
|
|
162
|
+
const env = options.env ?? (typeof process !== 'undefined' ? process.env : {})
|
|
163
|
+
const tmpRoot = options.tmpRoot ?? tmpdir()
|
|
164
|
+
const makeTempDir = options.mkdtemp ?? mkdtemp
|
|
165
|
+
const roots = captureTempRoots(env, tmpRoot)
|
|
166
|
+
const failures = []
|
|
167
|
+
|
|
168
|
+
for (const root of roots) {
|
|
169
|
+
try {
|
|
170
|
+
return await makeTempDir(path.win32.join(root, 'dsh-vision-router-capture-'))
|
|
171
|
+
} catch (error) {
|
|
172
|
+
failures.push(error && error.message ? error.message : String(error))
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
const error = new Error(
|
|
177
|
+
'vision_screenshot: no writable ASCII temporary directory is available for the Windows DPI-aware capture helper',
|
|
178
|
+
)
|
|
179
|
+
error.code = 'WINDOWS_CAPTURE_TEMP_UNAVAILABLE'
|
|
180
|
+
if (failures.length > 0) error.cause = new Error(failures.join(' | '))
|
|
181
|
+
throw error
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
/**
|
|
185
|
+
* Capture the Windows virtual desktop with per-monitor DPI awareness.
|
|
186
|
+
*
|
|
187
|
+
* This is the production owner of the Windows screenshot operation. It calls
|
|
188
|
+
* the PMv2 -> PMv1 helper directly and fails closed if a correct DPI context
|
|
189
|
+
* cannot be entered; it never falls back to the known-broken logical-coordinate
|
|
190
|
+
* legacy script. macOS/Linux capture remains owned by index.js.
|
|
191
|
+
*/
|
|
192
|
+
export async function captureWindowsDesktop(outputPath, options = {}) {
|
|
193
|
+
const env = options.env ?? (typeof process !== 'undefined' ? process.env : {})
|
|
194
|
+
const removeTempDir = options.rm ?? rm
|
|
195
|
+
const run = options.execFileAsync ?? execFileAsync
|
|
196
|
+
let scratchDir
|
|
197
|
+
|
|
198
|
+
try {
|
|
199
|
+
scratchDir = await createWindowsCaptureTempDir({
|
|
200
|
+
env,
|
|
201
|
+
tmpRoot: options.tmpRoot,
|
|
202
|
+
mkdtemp: options.mkdtemp,
|
|
203
|
+
})
|
|
204
|
+
const script = buildPerMonitorWindowsScreenshotScript(outputPath)
|
|
205
|
+
const execOptions = {
|
|
206
|
+
windowsHide: true,
|
|
207
|
+
cwd: scratchDir,
|
|
208
|
+
env: {
|
|
209
|
+
...env,
|
|
210
|
+
TEMP: scratchDir,
|
|
211
|
+
TMP: scratchDir,
|
|
212
|
+
},
|
|
213
|
+
...(Number.isFinite(options.timeoutMs) && options.timeoutMs > 0
|
|
214
|
+
? { timeout: options.timeoutMs }
|
|
215
|
+
: {}),
|
|
216
|
+
...(options.signal === undefined ? {} : { signal: options.signal }),
|
|
217
|
+
}
|
|
218
|
+
await run(
|
|
219
|
+
options.powershell ?? 'powershell.exe',
|
|
220
|
+
['-NoProfile', '-NonInteractive', '-STA', '-Command', script],
|
|
221
|
+
execOptions,
|
|
222
|
+
)
|
|
223
|
+
} catch (error) {
|
|
224
|
+
if (error?.code === 'WINDOWS_CAPTURE_TEMP_UNAVAILABLE') throw error
|
|
225
|
+
if (error?.name === 'AbortError' || error?.code === 'ABORT_ERR') throw error
|
|
226
|
+
const wrapped = new Error(
|
|
227
|
+
`vision_screenshot: Windows DPI-aware capture failed (${error && error.message ? error.message : String(error)})`,
|
|
228
|
+
)
|
|
229
|
+
wrapped.code = 'WINDOWS_DPI_CAPTURE_FAILED'
|
|
230
|
+
wrapped.cause = error
|
|
231
|
+
throw wrapped
|
|
232
|
+
} finally {
|
|
233
|
+
if (scratchDir) {
|
|
234
|
+
try {
|
|
235
|
+
await removeTempDir(scratchDir, {
|
|
236
|
+
recursive: true,
|
|
237
|
+
force: true,
|
|
238
|
+
maxRetries: 3,
|
|
239
|
+
retryDelay: 50,
|
|
240
|
+
})
|
|
241
|
+
} catch {
|
|
242
|
+
// Antivirus/indexing can briefly retain CodeDom files. Cleanup is
|
|
243
|
+
// best-effort and must not turn a valid screenshot into a failure.
|
|
244
|
+
}
|
|
245
|
+
}
|
|
246
|
+
}
|
|
247
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "dsh-vision-router",
|
|
3
|
-
"version": "2.1.
|
|
3
|
+
"version": "2.1.4",
|
|
4
4
|
"description": "Eyes for text-only DeepSeek Harness agents: built-in free vision chain (no key) + pixel-level vision tools (Q&A, grounding, crop, pixel diff, colors, OCR, SVG trace, cutout, screenshots). One-command install, no Python, image turns work like ordinary tool-calling turns.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"repository": {
|
|
@@ -54,8 +54,8 @@
|
|
|
54
54
|
"undici": "^7.29.0"
|
|
55
55
|
},
|
|
56
56
|
"peerDependencies": {
|
|
57
|
-
"@deepseek-ai/dsh-anonymous-user-id": "^0.1.0-rc.8 || ^0.1.1-rc.1",
|
|
58
|
-
"@deepseek-ai/dsh-llm-deepseek": "^0.1.0-rc.8 || ^0.1.1-rc.1",
|
|
57
|
+
"@deepseek-ai/dsh-anonymous-user-id": "^0.1.0-rc.8 || ^0.1.1-rc.1 || ^0.1.3-alpha.2",
|
|
58
|
+
"@deepseek-ai/dsh-llm-deepseek": "^0.1.0-rc.8 || ^0.1.1-rc.1 || ^0.1.3-alpha.2",
|
|
59
59
|
"sharp": ">=0.35.3 <1"
|
|
60
60
|
},
|
|
61
61
|
"peerDependenciesMeta": {
|
|
@@ -69,13 +69,13 @@
|
|
|
69
69
|
"sharp": "^0.35.3"
|
|
70
70
|
},
|
|
71
71
|
"scripts": {
|
|
72
|
-
"test": "node --test tests/adapter-prepare-call-compat.test.js tests/core.test.js tests/issue-374-delegated-call-config.test.js tests/grounding-coordinate-frame.test.js tests/grounding-coordinate-runtime.test.js tests/capability-advisory.test.js tests/vision-execution-policy.test.js tests/vision-backend-diagnostics.test.js tests/live-model-discovery.test.js tests/live-model-credential-resolution.test.js tests/vision-model-registry.test.js tests/live-model-client-stability.test.js tests/providers-persistence.test.js tests/provider-directory-fallback.test.js tests/model-catalog-policy.test.js tests/settings-ia-client-prelude.test.js tests/settings-ia-fj.test.js tests/settings-ia-acceptance-regression.test.js tests/settings-migration.test.js tests/settings-section-order.test.js tests/attachment-admission-policy.test.js tests/vision-resilience.test.js tests/vision-resilience-lifecycle.test.js tests/vision-breaker-readonly.test.js tests/alpha1-browser-lifecycle-integration.test.js tests/issue-367-remote-session-inject.test.js tests/client.test.js tests/issue-307-regression.test.js tests/issue-284-vision-mode-toggle.test.js tests/issue-284-vision-mode-hardening.test.js tests/issue-284-attachment-id-hint.test.js tests/issue-284-model-visibility.test.js tests/issue-284-vision-selection-effort.test.js tests/client-presentation-boundary.test.js tests/remote-settings-bridge.test.js tests/remote-settings-v2-contract.test.js tests/remote-settings-risk-confirmation.test.js tests/local-remote-settings-permission.test.js tests/rc6-real-settings-persistence.test.js tests/artifact-path-security.test.js tests/http-compat.test.js tests/catalog-corrections.test.js tests/update-check.test.js tests/update-check-signal-lifecycle.test.js tests/self-update.test.js tests/doctor.test.js tests/doctor-cli.test.js tests/legacy-session-repair.test.js tests/profile-pnpm-diagnostics.test.js tests/doctor-v2.test.js tests/doctor-cli-v2.test.js tests/doctor-runtime.test.js tests/dsh-host-capabilities.test.js tests/doctor-host-capabilities.test.js tests/session-repair-v2.test.js tests/bundle-defaults.test.js tests/file-logger.test.js tests/replay-delegation.test.js tests/twin-image-capability-fallback.test.js tests/twin-image-custom-wrapper.test.js tests/adversarial-hardening.test.js tests/vision-adversarial-hardening-v2.test.js tests/vision-capability-benchmark-visual-proof.test.js tests/wrapper-directory.test.js tests/logging-ui.test.js tests/manifest-dependencies.test.js tests/structured-bootstrap.test.js tests/structured-bootstrap-gate.test.js tests/local-ollama.test.js tests/ollama-cold-start.test.js tests/ollama-cold-start-loaded.test.js tests/zero-regression-gate.test.js tests/runtime-e2e.test.js tests/local-vision-stabilizer.test.js tests/local-connection-probe.test.js tests/repetition-guard.test.js tests/guide-scroll-gate.test.js tests/settings-scroll-jank-regression.test.js tests/android-attachment-compat.test.js tests/free-cloud-first.test.js tests/rc6-rc7-compat.test.js tests/native-image-coexistence.test.js tests/issue-276-session-image-ownership.test.js tests/issue-276-entry-policy-bridge.test.js tests/issue-276-policy-hardening.test.js tests/issue-289-native-nonintervention.test.js tests/pi-ai-bridge-wire-compat.test.js tests/mixed-router.test.js tests/depth-tier.test.js tests/depth-quota-behavior.test.js tests/structured-flow-hardening.test.js tests/structured-flow-adversarial.test.js tests/vision-budget-activation.test.js tests/runtime-boundary-fixes.test.js tests/runtime-i18n.test.js tests/tesseract-node24-boot.test.js tests/session-vision-state.test.js tests/session-vision-state-integration.test.js tests/image-resource-governor.test.js tests/resource-retention-lifecycle.test.js tests/pixel-diff-stream.test.js tests/large-image-resource-integration.test.js tests/structured-guard-idempotency.test.js tests/vision-routing-product.test.js tests/vision-runtime-performance.test.js tests/vision-routing-settings-prelude.test.js tests/v2-settings-ia-integration.test.js tests/vision-capability-router.test.js tests/vision-capability-reference.test.js tests/vision-capability-benchmark.test.js tests/vision-capability-grounding-proof-contract.test.js tests/vision-capability-probe.test.js tests/vision-capability-axis-freshness.test.js tests/vision-capability-shadow-health.test.js tests/vision-capability-benchmark-service.test.js tests/vision-capability-preflight-hardening.test.js tests/vision-capability-benchmark-contract.test.js tests/vision-capability-benchmark-client.test.js tests/vision-capability-benchmark-client-productization.test.js tests/vision-capability-adapter-route.test.js tests/vision-background-benchmark.test.js tests/vision-background-lifecycle.test.js tests/vision-background-benchmark-productization.test.js tests/vision-capability-benchmark-budget.test.js tests/vision-image-input-verdict.test.js tests/vision-capability-failure-classification.test.js tests/vision-capability-gate-client.test.js tests/v2-product-review-fixes.test.js tests/qa-top5-reliability.test.js tests/qa-screenshot-runtime.test.js tests/qa-runtime-identity.test.js tests/qa-turn-budget-cancellation.test.js tests/qa-cancellation-publication.test.js tests/qa-reliability-tail.test.js tests/qa-endpoint-route-alias.test.js tests/settings-native-card-semantics.test.js tests/settings-native-card-lazy-data.test.js tests/settings-invalid-staged-dirty.test.js tests/vision-toggle-root-hardening.test.js && node --test tests/vision-backend-runtime-policy.test.js",
|
|
72
|
+
"test": "node --test tests/adapter-prepare-call-compat.test.js tests/core.test.js tests/issue-374-delegated-call-config.test.js tests/grounding-coordinate-frame.test.js tests/grounding-coordinate-runtime.test.js tests/capability-advisory.test.js tests/vision-execution-policy.test.js tests/vision-backend-diagnostics.test.js tests/live-model-discovery.test.js tests/live-model-credential-resolution.test.js tests/vision-model-registry.test.js tests/live-model-client-stability.test.js tests/providers-persistence.test.js tests/provider-directory-fallback.test.js tests/model-catalog-policy.test.js tests/settings-ia-client-prelude.test.js tests/settings-ia-fj.test.js tests/settings-ia-acceptance-regression.test.js tests/settings-migration.test.js tests/settings-section-order.test.js tests/attachment-admission-policy.test.js tests/vision-resilience.test.js tests/vision-resilience-lifecycle.test.js tests/vision-breaker-readonly.test.js tests/alpha1-browser-lifecycle-integration.test.js tests/issue-367-remote-session-inject.test.js tests/vision-mode-toggle-injection.test.js tests/client.test.js tests/issue-307-regression.test.js tests/issue-284-vision-mode-toggle.test.js tests/issue-284-vision-mode-hardening.test.js tests/issue-284-attachment-id-hint.test.js tests/issue-284-model-visibility.test.js tests/issue-284-vision-selection-effort.test.js tests/client-presentation-boundary.test.js tests/clipboard-image-paste-compat.test.js tests/remote-settings-bridge.test.js tests/remote-settings-v2-contract.test.js tests/remote-settings-risk-confirmation.test.js tests/local-remote-settings-permission.test.js tests/rc6-real-settings-persistence.test.js tests/artifact-path-security.test.js tests/http-compat.test.js tests/catalog-corrections.test.js tests/update-check.test.js tests/update-check-signal-lifecycle.test.js tests/self-update.test.js tests/doctor.test.js tests/doctor-cli.test.js tests/legacy-session-repair.test.js tests/profile-pnpm-diagnostics.test.js tests/doctor-v2.test.js tests/doctor-cli-v2.test.js tests/doctor-runtime.test.js tests/dsh-host-capabilities.test.js tests/doctor-host-capabilities.test.js tests/session-repair-v2.test.js tests/bundle-defaults.test.js tests/file-logger.test.js tests/replay-delegation.test.js tests/twin-image-capability-fallback.test.js tests/twin-image-custom-wrapper.test.js tests/adversarial-hardening.test.js tests/vision-adversarial-hardening-v2.test.js tests/vision-capability-benchmark-visual-proof.test.js tests/wrapper-directory.test.js tests/logging-ui.test.js tests/manifest-dependencies.test.js tests/structured-bootstrap.test.js tests/structured-bootstrap-gate.test.js tests/vision-model-guidance-contract.test.js tests/local-ollama.test.js tests/ollama-cold-start.test.js tests/ollama-cold-start-loaded.test.js tests/zero-regression-gate.test.js tests/runtime-e2e.test.js tests/local-vision-stabilizer.test.js tests/local-connection-probe.test.js tests/repetition-guard.test.js tests/guide-scroll-gate.test.js tests/settings-scroll-jank-regression.test.js tests/android-attachment-compat.test.js tests/free-cloud-first.test.js tests/rc6-rc7-compat.test.js tests/native-image-coexistence.test.js tests/issue-276-session-image-ownership.test.js tests/issue-276-entry-policy-bridge.test.js tests/issue-276-policy-hardening.test.js tests/issue-289-native-nonintervention.test.js tests/pi-ai-bridge-wire-compat.test.js tests/mixed-router.test.js tests/depth-tier.test.js tests/depth-quota-behavior.test.js tests/structured-flow-hardening.test.js tests/structured-flow-adversarial.test.js tests/vision-budget-activation.test.js tests/runtime-boundary-fixes.test.js tests/runtime-i18n.test.js tests/tesseract-node24-boot.test.js tests/session-vision-state.test.js tests/session-vision-state-integration.test.js tests/image-resource-governor.test.js tests/resource-retention-lifecycle.test.js tests/pixel-diff-stream.test.js tests/large-image-resource-integration.test.js tests/structured-guard-idempotency.test.js tests/vision-routing-product.test.js tests/vision-runtime-performance.test.js tests/vision-routing-settings-prelude.test.js tests/v2-settings-ia-integration.test.js tests/vision-capability-router.test.js tests/vision-capability-reference.test.js tests/vision-capability-benchmark.test.js tests/vision-capability-grounding-proof-contract.test.js tests/vision-capability-probe.test.js tests/vision-capability-axis-freshness.test.js tests/vision-capability-shadow-health.test.js tests/vision-capability-benchmark-service.test.js tests/vision-capability-preflight-hardening.test.js tests/vision-capability-benchmark-contract.test.js tests/vision-capability-benchmark-client.test.js tests/vision-capability-benchmark-client-productization.test.js tests/vision-capability-adapter-route.test.js tests/vision-background-benchmark.test.js tests/vision-background-lifecycle.test.js tests/vision-background-benchmark-productization.test.js tests/vision-capability-benchmark-budget.test.js tests/vision-image-input-verdict.test.js tests/vision-capability-failure-classification.test.js tests/vision-capability-gate-client.test.js tests/v2-product-review-fixes.test.js tests/qa-top5-reliability.test.js tests/qa-screenshot-runtime.test.js tests/qa-runtime-identity.test.js tests/qa-turn-budget-cancellation.test.js tests/qa-cancellation-publication.test.js tests/qa-reliability-tail.test.js tests/qa-endpoint-route-alias.test.js tests/settings-native-card-semantics.test.js tests/settings-native-card-lazy-data.test.js tests/settings-invalid-staged-dirty.test.js tests/vision-toggle-root-hardening.test.js && node --test tests/vision-backend-runtime-policy.test.js",
|
|
73
73
|
"test:core": "node --test tests/core.test.js tests/runtime-e2e.test.js tests/structured-bootstrap.test.js tests/structured-flow-hardening.test.js tests/structured-flow-adversarial.test.js tests/vision-execution-policy.test.js tests/vision-backend-runtime-policy.test.js",
|
|
74
74
|
"test:routing": "node --test tests/issue-374-delegated-call-config.test.js tests/twin-image-capability-fallback.test.js tests/twin-image-custom-wrapper.test.js tests/vision-routing-product.test.js tests/vision-capability-router.test.js tests/vision-capability-probe.test.js tests/vision-capability-shadow-health.test.js tests/vision-runtime-performance.test.js tests/vision-background-benchmark.test.js tests/vision-image-input-verdict.test.js",
|
|
75
75
|
"test:session": "node --test tests/session-vision-state.test.js tests/session-vision-state-integration.test.js tests/replay-delegation.test.js tests/legacy-session-repair.test.js tests/session-repair-v2.test.js tests/issue-276-session-image-ownership.test.js",
|
|
76
76
|
"test:resources": "node --test tests/artifact-path-security.test.js tests/image-resource-governor.test.js tests/resource-retention-lifecycle.test.js tests/pixel-diff-stream.test.js tests/large-image-resource-integration.test.js tests/qa-turn-budget-cancellation.test.js tests/qa-cancellation-publication.test.js",
|
|
77
77
|
"test:compat": "node --test tests/adapter-prepare-call-compat.test.js tests/attachment-admission-policy.test.js tests/rc6-rc7-compat.test.js tests/rc6-real-settings-persistence.test.js tests/android-attachment-compat.test.js tests/http-compat.test.js tests/pi-ai-bridge-wire-compat.test.js tests/native-image-coexistence.test.js tests/tesseract-node24-boot.test.js tests/dsh-host-capabilities.test.js",
|
|
78
|
-
"test:web": "node --test tests/alpha1-browser-lifecycle-integration.test.js tests/issue-367-remote-session-inject.test.js tests/client.test.js tests/client-presentation-boundary.test.js tests/settings-ia-client-prelude.test.js tests/settings-ia-acceptance-regression.test.js tests/remote-settings-bridge.test.js tests/remote-settings-v2-contract.test.js tests/local-remote-settings-permission.test.js tests/v2-settings-ia-integration.test.js tests/logging-ui.test.js",
|
|
78
|
+
"test:web": "node --test tests/alpha1-browser-lifecycle-integration.test.js tests/issue-367-remote-session-inject.test.js tests/vision-mode-toggle-injection.test.js tests/client.test.js tests/client-presentation-boundary.test.js tests/clipboard-image-paste-compat.test.js tests/settings-ia-client-prelude.test.js tests/settings-ia-acceptance-regression.test.js tests/remote-settings-bridge.test.js tests/remote-settings-v2-contract.test.js tests/local-remote-settings-permission.test.js tests/v2-settings-ia-integration.test.js tests/logging-ui.test.js",
|
|
79
79
|
"test:contract": "node --test tests/manifest-dependencies.test.js tests/bundle-defaults.test.js tests/zero-regression-gate.test.js tests/issue-289-native-nonintervention.test.js tests/issue-374-delegated-call-config.test.js tests/runtime-boundary-fixes.test.js tests/runtime-i18n.test.js tests/adapter-prepare-call-compat.test.js tests/rc6-rc7-compat.test.js tests/attachment-admission-policy.test.js tests/dsh-host-capabilities.test.js tests/doctor-host-capabilities.test.js tests/structured-bootstrap.test.js tests/structured-bootstrap-gate.test.js",
|
|
80
80
|
"test:grounding": "node --test tests/grounding-coordinate-frame.test.js tests/grounding-coordinate-runtime.test.js tests/vision-capability-grounding-proof-contract.test.js",
|
|
81
81
|
"test:stress": "node scripts/image-resource-stress.mjs && node --test tests/large-image-resource-integration.test.js tests/resource-retention-lifecycle.test.js"
|
|
@@ -1,148 +0,0 @@
|
|
|
1
|
-
const VIRTUAL_SCREEN_MARKER = '[System.Windows.Forms.SystemInformation]::VirtualScreen'
|
|
2
|
-
const COPY_FROM_SCREEN_MARKER = '$g.CopyFromScreen($b.X,$b.Y,0,0,$bmp.Size)'
|
|
3
|
-
const SAVE_PATTERN = /\$bmp\.Save\('((?:''|[^'])*)'\)/
|
|
4
|
-
|
|
5
|
-
function powerShellBasename(file) {
|
|
6
|
-
return String(file ?? '').replace(/\\/g, '/').split('/').pop().toLowerCase()
|
|
7
|
-
}
|
|
8
|
-
|
|
9
|
-
function quotePowerShellSingle(value) {
|
|
10
|
-
return `'${String(value).replace(/'/g, "''")}'`
|
|
11
|
-
}
|
|
12
|
-
|
|
13
|
-
function savedPathFromLegacyScript(script) {
|
|
14
|
-
const match = SAVE_PATTERN.exec(String(script ?? ''))
|
|
15
|
-
return match ? match[1].replace(/''/g, "'") : undefined
|
|
16
|
-
}
|
|
17
|
-
|
|
18
|
-
export function isLegacyWindowsScreenshotScript(script) {
|
|
19
|
-
const source = String(script ?? '')
|
|
20
|
-
return (
|
|
21
|
-
source.includes('Add-Type -AssemblyName System.Windows.Forms,System.Drawing') &&
|
|
22
|
-
source.includes(VIRTUAL_SCREEN_MARKER) &&
|
|
23
|
-
source.includes('[System.Drawing.Graphics]::FromImage($bmp)') &&
|
|
24
|
-
source.includes(COPY_FROM_SCREEN_MARKER) &&
|
|
25
|
-
savedPathFromLegacyScript(source) !== undefined
|
|
26
|
-
)
|
|
27
|
-
}
|
|
28
|
-
|
|
29
|
-
export function buildPerMonitorWindowsScreenshotScript(outputPath) {
|
|
30
|
-
const quotedPath = quotePowerShellSingle(outputPath)
|
|
31
|
-
const source = String.raw`using System;
|
|
32
|
-
using System.Drawing;
|
|
33
|
-
using System.Runtime.InteropServices;
|
|
34
|
-
using System.Windows.Forms;
|
|
35
|
-
|
|
36
|
-
public static class DshVisionDesktopCapture
|
|
37
|
-
{
|
|
38
|
-
private static readonly IntPtr PerMonitorV2 = new IntPtr(-4);
|
|
39
|
-
private static readonly IntPtr PerMonitorV1 = new IntPtr(-3);
|
|
40
|
-
|
|
41
|
-
[DllImport("user32.dll")]
|
|
42
|
-
private static extern IntPtr SetThreadDpiAwarenessContext(IntPtr dpiContext);
|
|
43
|
-
|
|
44
|
-
private static IntPtr TrySetContext(IntPtr context)
|
|
45
|
-
{
|
|
46
|
-
try
|
|
47
|
-
{
|
|
48
|
-
return SetThreadDpiAwarenessContext(context);
|
|
49
|
-
}
|
|
50
|
-
catch (EntryPointNotFoundException)
|
|
51
|
-
{
|
|
52
|
-
return IntPtr.Zero;
|
|
53
|
-
}
|
|
54
|
-
catch (DllNotFoundException)
|
|
55
|
-
{
|
|
56
|
-
return IntPtr.Zero;
|
|
57
|
-
}
|
|
58
|
-
}
|
|
59
|
-
|
|
60
|
-
private static IntPtr EnterPerMonitorContext()
|
|
61
|
-
{
|
|
62
|
-
IntPtr previous = TrySetContext(PerMonitorV2);
|
|
63
|
-
if (previous == IntPtr.Zero)
|
|
64
|
-
{
|
|
65
|
-
previous = TrySetContext(PerMonitorV1);
|
|
66
|
-
}
|
|
67
|
-
if (previous == IntPtr.Zero)
|
|
68
|
-
{
|
|
69
|
-
throw new InvalidOperationException(
|
|
70
|
-
"could not enter a per-monitor DPI awareness context; refusing to return a potentially misaligned screenshot");
|
|
71
|
-
}
|
|
72
|
-
return previous;
|
|
73
|
-
}
|
|
74
|
-
|
|
75
|
-
private static void RestoreContext(IntPtr previous)
|
|
76
|
-
{
|
|
77
|
-
try
|
|
78
|
-
{
|
|
79
|
-
SetThreadDpiAwarenessContext(previous);
|
|
80
|
-
}
|
|
81
|
-
catch
|
|
82
|
-
{
|
|
83
|
-
// The helper process exits immediately after capture. Restoration
|
|
84
|
-
// is still attempted so this method is correct if reused.
|
|
85
|
-
}
|
|
86
|
-
}
|
|
87
|
-
|
|
88
|
-
public static void ValidateDpiContext()
|
|
89
|
-
{
|
|
90
|
-
IntPtr previous = EnterPerMonitorContext();
|
|
91
|
-
RestoreContext(previous);
|
|
92
|
-
}
|
|
93
|
-
|
|
94
|
-
public static void Capture(string outputPath)
|
|
95
|
-
{
|
|
96
|
-
IntPtr previous = EnterPerMonitorContext();
|
|
97
|
-
try
|
|
98
|
-
{
|
|
99
|
-
Rectangle bounds = SystemInformation.VirtualScreen;
|
|
100
|
-
if (bounds.Width <= 0 || bounds.Height <= 0)
|
|
101
|
-
{
|
|
102
|
-
throw new InvalidOperationException("the Windows virtual screen has no drawable area");
|
|
103
|
-
}
|
|
104
|
-
|
|
105
|
-
using (var bitmap = new Bitmap(bounds.Width, bounds.Height))
|
|
106
|
-
using (var graphics = Graphics.FromImage(bitmap))
|
|
107
|
-
{
|
|
108
|
-
graphics.CopyFromScreen(bounds.X, bounds.Y, 0, 0, bounds.Size);
|
|
109
|
-
bitmap.Save(outputPath);
|
|
110
|
-
}
|
|
111
|
-
}
|
|
112
|
-
finally
|
|
113
|
-
{
|
|
114
|
-
RestoreContext(previous);
|
|
115
|
-
}
|
|
116
|
-
}
|
|
117
|
-
}`
|
|
118
|
-
|
|
119
|
-
return [
|
|
120
|
-
'Add-Type -AssemblyName System.Windows.Forms,System.Drawing',
|
|
121
|
-
`$__dshVisionCaptureSource = @'\n${source}\n'@`,
|
|
122
|
-
'try {',
|
|
123
|
-
" Add-Type -TypeDefinition $__dshVisionCaptureSource -ReferencedAssemblies 'System.Windows.Forms.dll','System.Drawing.dll' -ErrorAction Stop",
|
|
124
|
-
'} catch {',
|
|
125
|
-
" throw ('vision_screenshot: failed to initialize the Windows DPI-aware capture helper: ' + $_.Exception.Message)",
|
|
126
|
-
'}',
|
|
127
|
-
`[DshVisionDesktopCapture]::Capture(${quotedPath})`,
|
|
128
|
-
].join('\n')
|
|
129
|
-
}
|
|
130
|
-
|
|
131
|
-
export function rewriteWindowsScreenshotExecArgs(file, args, options = {}) {
|
|
132
|
-
const platform = options.platform ?? (typeof process !== 'undefined' ? process.platform : '')
|
|
133
|
-
if (platform !== 'win32') return args
|
|
134
|
-
const executable = powerShellBasename(file)
|
|
135
|
-
if (executable !== 'powershell.exe' && executable !== 'powershell') return args
|
|
136
|
-
if (!Array.isArray(args)) return args
|
|
137
|
-
|
|
138
|
-
const commandIndex = args.findIndex((arg) => String(arg).toLowerCase() === '-command')
|
|
139
|
-
if (commandIndex < 0 || commandIndex + 1 >= args.length) return args
|
|
140
|
-
const script = args[commandIndex + 1]
|
|
141
|
-
if (!isLegacyWindowsScreenshotScript(script)) return args
|
|
142
|
-
|
|
143
|
-
const outputPath = savedPathFromLegacyScript(script)
|
|
144
|
-
if (outputPath === undefined) return args
|
|
145
|
-
const rewritten = args.slice()
|
|
146
|
-
rewritten[commandIndex + 1] = buildPerMonitorWindowsScreenshotScript(outputPath)
|
|
147
|
-
return rewritten
|
|
148
|
-
}
|