dsh-vision-router 3.0.0 → 3.0.2
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 +9 -2
- package/README.zh.md +7 -2
- package/docs/architecture/3.0-r11-adversarial-review.md +11 -5
- package/docs/architecture/ci-risk-posture.md +45 -0
- package/docs/architecture/dsh-021-alpha1-adaptation.md +46 -0
- package/docs/architecture/dsh-support-window.md +8 -0
- package/docs/releases/v3.0.1.md +77 -0
- package/docs/releases/v3.0.2.md +105 -0
- package/index.js +15 -3
- package/lib/adapter-ownership.js +51 -0
- package/lib/client-presentation-boundary-main.js +113 -50
- package/lib/client-presentation-boundary.js +36 -46
- package/lib/client.js +2 -1
- package/lib/core-primitives.js +29 -1
- package/lib/core-vision-surface.js +3 -1
- package/lib/desktop-screenshot-tool.js +11 -9
- package/lib/diagnostic-redaction.js +61 -12
- package/lib/doctor-cli-p0.js +36 -5
- package/lib/dsh-support-window.js +4 -1
- package/lib/file-logger.js +9 -9
- package/lib/image-offload-compat.js +10 -3
- package/lib/local-vision-stabilizer.js +10 -7
- package/lib/native-image-coexistence.js +11 -1
- package/lib/proxy-routing.js +18 -3
- package/lib/remote-settings-risk-client-boundary.js +6 -1
- package/lib/runtime-composition.js +8 -6
- package/lib/session-vision-mode-authority.js +2 -7
- package/lib/session-vision-policy.js +9 -4
- package/lib/settings-client-017-compat.js +7 -3
- package/lib/settings-number-contract.js +3 -1
- package/lib/vision-background-benchmark.js +14 -2
- package/lib/vision-background-stop-store-core.js +14 -2
- package/lib/vision-diagnostics-routes.js +9 -1
- package/lib/vision-model-visibility-boundary-main.js +15 -16
- package/lib/vision-resilience.js +31 -2
- package/lib/vision-tool-runtime-boundary.js +2 -7
- package/package.json +2 -2
|
@@ -24,7 +24,10 @@ export function supportWindowUpgradeAdvice(capabilities = {}) {
|
|
|
24
24
|
})
|
|
25
25
|
}
|
|
26
26
|
|
|
27
|
-
|
|
27
|
+
// Capability evidence is tri-state (`true` / `false` / `'unknown'`). Only an
|
|
28
|
+
// explicit `true` proves the support floor, so a missing field, a wrong-typed
|
|
29
|
+
// value and `'unknown'` all mean "not proven" rather than "capable".
|
|
30
|
+
if (batchAttachments !== true || maxImageDimension !== true) {
|
|
28
31
|
return Object.freeze({
|
|
29
32
|
level: 'unknown',
|
|
30
33
|
code: 'HOST_CURRENT_FLOOR_UNKNOWN',
|
package/lib/file-logger.js
CHANGED
|
@@ -4,6 +4,7 @@ import process from 'node:process'
|
|
|
4
4
|
import { execFile } from 'node:child_process'
|
|
5
5
|
import { format as formatArgs, promisify } from 'node:util'
|
|
6
6
|
import { createRequire } from 'node:module'
|
|
7
|
+
import { redactCredentialShapes } from './diagnostic-redaction.js'
|
|
7
8
|
import { resolveDshHome } from './doctor.js'
|
|
8
9
|
import { isLoopbackRequest } from './web-capability-boundary.js'
|
|
9
10
|
|
|
@@ -32,20 +33,19 @@ export function resolveVisionRouterLogPaths(dshHome = resolveDshHome()) {
|
|
|
32
33
|
* Diagnostics are intended to be shareable in bug reports. Existing runtime
|
|
33
34
|
* messages should not contain secrets, but redact common credential shapes as
|
|
34
35
|
* defense in depth before anything reaches disk.
|
|
36
|
+
*
|
|
37
|
+
* The shape list lives in one place now: this module used to carry its own
|
|
38
|
+
* subset, which leaked `Authorization: token ghp_…`, `Cookie:` headers, bare
|
|
39
|
+
* platform tokens and `#token=` fragments that the other copy already covered.
|
|
35
40
|
*/
|
|
36
41
|
export function sanitizeLogText(value) {
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
text =
|
|
40
|
-
text = text.replace(
|
|
42
|
+
// Query strings here keep the historical `key=`/`auth=` spelling; everything
|
|
43
|
+
// else (schemes, cookies, vendor keys, JWTs) comes from the shared pass.
|
|
44
|
+
const text = String(value ?? '').replace(
|
|
41
45
|
/([?&](?:api[_-]?key|access[_-]?token|token|key|auth)=)[^&\s]+/gi,
|
|
42
46
|
'$1[REDACTED]',
|
|
43
47
|
)
|
|
44
|
-
|
|
45
|
-
/\b(authorization|api[_-]?key|access[_-]?token)\s*[:=]\s*["']?[^\s"',}]+/gi,
|
|
46
|
-
'$1=[REDACTED]',
|
|
47
|
-
)
|
|
48
|
-
return text
|
|
48
|
+
return redactCredentialShapes(text)
|
|
49
49
|
}
|
|
50
50
|
|
|
51
51
|
function positiveInteger(value, fallback) {
|
|
@@ -15,8 +15,15 @@ export function isOffloadedImageBlock(block) {
|
|
|
15
15
|
const value = objectRecord(block);
|
|
16
16
|
return value?.type === 'image' && value.offloaded === true;
|
|
17
17
|
}
|
|
18
|
-
|
|
19
|
-
|
|
18
|
+
/**
|
|
19
|
+
* Deepest content nesting this walker inspects, mirroring the bound
|
|
20
|
+
* `catalog-corrections.convertBlocks` already applies to the same block tree.
|
|
21
|
+
* The walker only needs message → content → nested content; an unbounded
|
|
22
|
+
* recursion would turn a pathological tree into a stack overflow.
|
|
23
|
+
*/
|
|
24
|
+
const MAX_CONTENT_NESTING_DEPTH = 4;
|
|
25
|
+
export function blocksHaveRetainedImage(content, depth = 0) {
|
|
26
|
+
if (!Array.isArray(content) || depth > MAX_CONTENT_NESTING_DEPTH)
|
|
20
27
|
return false;
|
|
21
28
|
for (const block of content) {
|
|
22
29
|
const value = objectRecord(block);
|
|
@@ -24,7 +31,7 @@ export function blocksHaveRetainedImage(content) {
|
|
|
24
31
|
continue;
|
|
25
32
|
if (value.type === 'image' && value.offloaded !== true)
|
|
26
33
|
return true;
|
|
27
|
-
if (Array.isArray(value.content) && blocksHaveRetainedImage(value.content))
|
|
34
|
+
if (Array.isArray(value.content) && blocksHaveRetainedImage(value.content, depth + 1))
|
|
28
35
|
return true;
|
|
29
36
|
}
|
|
30
37
|
return false;
|
|
@@ -4,6 +4,7 @@ import { tmpdir } from 'node:os'
|
|
|
4
4
|
import path from 'node:path'
|
|
5
5
|
import { promisify } from 'node:util'
|
|
6
6
|
import { sameOriginRequest } from './adversarial-hardening.js'
|
|
7
|
+
import { objectRecord } from './core-primitives.js'
|
|
7
8
|
import { normalizeRuntimeVisionConfig } from './runtime-config-normalizer.js'
|
|
8
9
|
import { isOffloadedImageBlock, offloadedImagePlaceholder } from './image-offload-compat.js'
|
|
9
10
|
|
|
@@ -14,12 +15,13 @@ import { isOffloadedImageBlock, offloadedImagePlaceholder } from './image-offloa
|
|
|
14
15
|
* specific to opt-in local vision, while non-local providers continue through
|
|
15
16
|
* the original adapters byte-for-byte.
|
|
16
17
|
*/
|
|
17
|
-
export async function triggerDesktopScreenshotPermission({
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
18
|
+
export async function triggerDesktopScreenshotPermission(rawOptions) {
|
|
19
|
+
const {
|
|
20
|
+
platform = typeof process !== 'undefined' ? process.platform : '',
|
|
21
|
+
run = promisify(execFile),
|
|
22
|
+
remove = unlink,
|
|
23
|
+
tempDir = tmpdir(),
|
|
24
|
+
} = objectRecord(rawOptions) ?? {}
|
|
23
25
|
// Windows has no Screen Recording privacy prompt equivalent and Linux
|
|
24
26
|
// capture permission is compositor-specific. macOS is the platform where
|
|
25
27
|
// enabling this feature must proactively cross an OS privacy boundary.
|
|
@@ -43,7 +45,7 @@ export async function triggerDesktopScreenshotPermission({
|
|
|
43
45
|
}
|
|
44
46
|
}
|
|
45
47
|
|
|
46
|
-
export function installLocalVisionStabilizer(ctx, config = {}, core) {
|
|
48
|
+
export function installLocalVisionStabilizer(ctx, config = {}, core, installOptions = {}) {
|
|
47
49
|
config = normalizeRuntimeVisionConfig(config)
|
|
48
50
|
if (!ctx || typeof ctx !== 'object') return { ctx, bootConfig: config }
|
|
49
51
|
|
|
@@ -270,6 +272,7 @@ export function installLocalVisionStabilizer(ctx, config = {}, core) {
|
|
|
270
272
|
const text = await core.callLocalBackend(local, await localMessages(options), {
|
|
271
273
|
maxTokens: local.maxTokens ?? 4096,
|
|
272
274
|
signal: options.signal,
|
|
275
|
+
providerTransport: installOptions?.providerTransport,
|
|
273
276
|
resolveCredential,
|
|
274
277
|
})
|
|
275
278
|
if (text !== '') {
|
|
@@ -1,4 +1,10 @@
|
|
|
1
1
|
import { AsyncLocalStorage } from 'node:async_hooks'
|
|
2
|
+
import {
|
|
3
|
+
VISION_ROUTER_ADAPTER_OWNER,
|
|
4
|
+
isVisionRouterOwnedAdapter,
|
|
5
|
+
} from './adapter-ownership.js'
|
|
6
|
+
|
|
7
|
+
export { VISION_ROUTER_ADAPTER_OWNER, isVisionRouterOwnedAdapter }
|
|
2
8
|
|
|
3
9
|
/**
|
|
4
10
|
* Turn-local image ownership used by the public entry composition.
|
|
@@ -21,7 +27,8 @@ export const IMAGE_OWNERSHIP = Object.freeze({
|
|
|
21
27
|
* an enumerable symbol also survives the rare Object.assign/object-spread
|
|
22
28
|
* adapter wrapper. Normal JSON/Object.keys surfaces still ignore symbols.
|
|
23
29
|
*/
|
|
24
|
-
|
|
30
|
+
// The symbol and the ownership rule are owned by ./adapter-ownership.ts and
|
|
31
|
+
// re-exported here so existing importers keep working unchanged.
|
|
25
32
|
|
|
26
33
|
const imageTurn = new AsyncLocalStorage()
|
|
27
34
|
const markedAdapters = new WeakMap()
|
|
@@ -166,6 +173,9 @@ export function visionRouterAdapterOwner(adapter) {
|
|
|
166
173
|
}
|
|
167
174
|
}
|
|
168
175
|
|
|
176
|
+
// isVisionRouterOwnedAdapter is owned by ./adapter-ownership.ts (imported and
|
|
177
|
+
// re-exported at the top of this file) so every caller shares one rule.
|
|
178
|
+
|
|
169
179
|
function routeList(routes) {
|
|
170
180
|
const list = Array.isArray(routes) ? routes : [routes]
|
|
171
181
|
return list
|
package/lib/proxy-routing.js
CHANGED
|
@@ -22,10 +22,25 @@ export function canonicalProxyHost(value) {
|
|
|
22
22
|
host = host.slice(0, -1);
|
|
23
23
|
if (host === '')
|
|
24
24
|
return '';
|
|
25
|
+
// A `*.host` entry means "this host and its subdomains", which is exactly what the matcher
|
|
26
|
+
// already does for a bare hostname. `domainToASCII` rejects the star, so leaving it in place
|
|
27
|
+
// makes the entry equal no real hostname: the configured host silently proxies nothing.
|
|
28
|
+
if (host.startsWith('*.'))
|
|
29
|
+
host = host.slice(2);
|
|
30
|
+
if (host === '')
|
|
31
|
+
return '';
|
|
25
32
|
// WHATWG URL.hostname keeps IPv6 literals bracketed. domainToASCII does not
|
|
26
|
-
// accept
|
|
27
|
-
//
|
|
28
|
-
|
|
33
|
+
// accept an IPv6 literal at all, so case-fold it directly. The brackets are
|
|
34
|
+
// pure spelling for the same address: stripping them is what lets a configured
|
|
35
|
+
// `::1` match a URL-derived `[::1]`. A bracketed non-literal (say
|
|
36
|
+
// `[example.com]`) keeps its spelling, so nothing about DNS is widened.
|
|
37
|
+
if (host.startsWith('[') && host.endsWith(']')) {
|
|
38
|
+
const literal = host.slice(1, -1).trim();
|
|
39
|
+
if (literal === '')
|
|
40
|
+
return '';
|
|
41
|
+
return literal.includes(':') ? literal.toLowerCase() : host.toLowerCase();
|
|
42
|
+
}
|
|
43
|
+
if (host.includes(':'))
|
|
29
44
|
return host.toLowerCase();
|
|
30
45
|
const ascii = domainToASCII(host);
|
|
31
46
|
return (ascii || host).toLowerCase();
|
|
@@ -4,8 +4,13 @@
|
|
|
4
4
|
*
|
|
5
5
|
* PRODUCT CONTRACT (2.3+): acceptedRisk is reminder/friction only. It is not
|
|
6
6
|
* authentication, authorization, local-presence proof, or a credential.
|
|
7
|
+
*
|
|
8
|
+
* This function is serialized with `toString()` into the browser boundary source
|
|
9
|
+
* below, so its body must stay self-contained: no imported helper and no free
|
|
10
|
+
* identifier may appear inside it.
|
|
7
11
|
*/
|
|
8
|
-
export function createRemoteSettingsRiskClientBoundary(
|
|
12
|
+
export function createRemoteSettingsRiskClientBoundary(rawOptions) {
|
|
13
|
+
var options = rawOptions !== null && typeof rawOptions === 'object' ? rawOptions : {}
|
|
9
14
|
var confirmImpl = options.confirmImpl
|
|
10
15
|
var alertImpl = options.alertImpl
|
|
11
16
|
var locale = options.locale
|
|
@@ -98,7 +98,7 @@ function legacyCoreVisionSettings(ctx, fallback) {
|
|
|
98
98
|
return { ...current, autoActivateOnImage: false }
|
|
99
99
|
}
|
|
100
100
|
|
|
101
|
-
function installHostAndSecurityBoundaries(ctx, config, core) {
|
|
101
|
+
function installHostAndSecurityBoundaries(ctx, config, core, providerTransport) {
|
|
102
102
|
// SessionVisionIndex calls planGuardStopShadows before Core.apply. Keep that
|
|
103
103
|
// one helper behind the live-locale facade; the final Core itself still gets
|
|
104
104
|
// the ordinary composition identity and is localized by the scoped context.
|
|
@@ -144,6 +144,7 @@ function installHostAndSecurityBoundaries(ctx, config, core) {
|
|
|
144
144
|
ollamaColdStartCtx,
|
|
145
145
|
hardenedConfig,
|
|
146
146
|
core,
|
|
147
|
+
{ providerTransport },
|
|
147
148
|
)
|
|
148
149
|
|
|
149
150
|
const routingProduct = resolveVisionRoutingProduct(bootConfig)
|
|
@@ -260,7 +261,7 @@ function createRuntimeOwners(host, core) {
|
|
|
260
261
|
}
|
|
261
262
|
}
|
|
262
263
|
|
|
263
|
-
function installRoutingAndHostProducts(host, owners, core) {
|
|
264
|
+
function installRoutingAndHostProducts(host, owners, core, providerTransport) {
|
|
264
265
|
const { capabilityStore, logging, runtimeConfig, runtimePerformanceStore } = host
|
|
265
266
|
const { sessionVisionModeCompat } = owners
|
|
266
267
|
// Final structured-flow guard sits closest to core.apply so it sees the
|
|
@@ -281,7 +282,7 @@ function installRoutingAndHostProducts(host, owners, core) {
|
|
|
281
282
|
runtimeConfig,
|
|
282
283
|
core,
|
|
283
284
|
capabilityStore,
|
|
284
|
-
{ logger: logging.logger },
|
|
285
|
+
{ logger: logging.logger, providerTransport },
|
|
285
286
|
)
|
|
286
287
|
const sessionTurnResolver = createSessionTurnResolver(backgroundProfiling.ctx)
|
|
287
288
|
const sessionEventTailReader = createSessionEventTailReader(backgroundProfiling.ctx)
|
|
@@ -479,9 +480,10 @@ function applyMatureCoreBridge(host, owners, routing, execution, core, runtime)
|
|
|
479
480
|
}
|
|
480
481
|
|
|
481
482
|
export function applyVisionRuntimeComposition(ctx, config = {}, core, runtime = {}) {
|
|
482
|
-
const
|
|
483
|
+
const providerTransport = runtime?.providerTransport
|
|
484
|
+
const host = installHostAndSecurityBoundaries(ctx, config, core, providerTransport)
|
|
483
485
|
const owners = createRuntimeOwners(host, core)
|
|
484
|
-
const routing = installRoutingAndHostProducts(host, owners, core)
|
|
485
|
-
const execution = installExecutionBoundaries(host, owners, routing, core,
|
|
486
|
+
const routing = installRoutingAndHostProducts(host, owners, core, providerTransport)
|
|
487
|
+
const execution = installExecutionBoundaries(host, owners, routing, core, providerTransport)
|
|
486
488
|
return applyMatureCoreBridge(host, owners, routing, execution, core, runtime)
|
|
487
489
|
}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { AsyncLocalStorage } from 'node:async_hooks';
|
|
2
|
+
import { isVisionRouterOwnedAdapter } from './adapter-ownership.js';
|
|
2
3
|
import { parseSessionVisionPolicy } from './session-vision-policy.js';
|
|
3
|
-
const VISION_ROUTER_ADAPTER_OWNER = Symbol.for('dsh-vision-router.adapter-owner');
|
|
4
4
|
const VISION_ROUTER_OWNERSHIP = 'vision-router-owned';
|
|
5
5
|
const DEFAULT_WRAPPER_ROUTE = 'deepseek-vision';
|
|
6
6
|
const DEFAULT_CHAIN_ROUTE = 'vision-chain';
|
|
@@ -139,12 +139,7 @@ function currentAdapter(ctx, provider) {
|
|
|
139
139
|
function adapterOwnedByVisionRouter(adapter) {
|
|
140
140
|
if (!isWeakKey(adapter))
|
|
141
141
|
return false;
|
|
142
|
-
|
|
143
|
-
return objectRecord(adapter)?.[VISION_ROUTER_ADAPTER_OWNER] !== undefined;
|
|
144
|
-
}
|
|
145
|
-
catch {
|
|
146
|
-
return false;
|
|
147
|
-
}
|
|
142
|
+
return isVisionRouterOwnedAdapter(adapter);
|
|
148
143
|
}
|
|
149
144
|
function sameRoute(left, right) {
|
|
150
145
|
const rightRoute = routeFrom(right);
|
|
@@ -12,12 +12,15 @@ export function parseSessionVisionPolicy(value) {
|
|
|
12
12
|
if (value === null || typeof value !== 'object' || Array.isArray(value))
|
|
13
13
|
return undefined;
|
|
14
14
|
const record = value;
|
|
15
|
-
|
|
15
|
+
// Own keys only: a policy row is persisted JSON, so a value inherited from a
|
|
16
|
+
// prototype is evidence of a malformed row rather than of a policy.
|
|
17
|
+
if (!Object.hasOwn(record, 'revision') || record.revision !== SESSION_VISION_POLICY_REVISION)
|
|
16
18
|
return undefined;
|
|
17
|
-
if (typeof record.enabled !== 'boolean')
|
|
19
|
+
if (!Object.hasOwn(record, 'enabled') || typeof record.enabled !== 'boolean')
|
|
18
20
|
return undefined;
|
|
19
|
-
if (record.source !== 'user' && record.source !== 'delegation')
|
|
21
|
+
if (!Object.hasOwn(record, 'source') || (record.source !== 'user' && record.source !== 'delegation')) {
|
|
20
22
|
return undefined;
|
|
23
|
+
}
|
|
21
24
|
if (record.source === 'user') {
|
|
22
25
|
if (record.inheritedFrom !== undefined)
|
|
23
26
|
return undefined;
|
|
@@ -27,7 +30,9 @@ export function parseSessionVisionPolicy(value) {
|
|
|
27
30
|
source: 'user',
|
|
28
31
|
});
|
|
29
32
|
}
|
|
30
|
-
const inheritedFrom =
|
|
33
|
+
const inheritedFrom = Object.hasOwn(record, 'inheritedFrom')
|
|
34
|
+
? nonEmptyString(record.inheritedFrom)
|
|
35
|
+
: undefined;
|
|
31
36
|
if (inheritedFrom === undefined)
|
|
32
37
|
return undefined;
|
|
33
38
|
return Object.freeze({
|
|
@@ -197,13 +197,17 @@ export const SETTINGS_017_CLIENT_PRELUDE = String.raw`(function(){
|
|
|
197
197
|
get: function(target, property) {
|
|
198
198
|
if (property === 'settingsScope') {
|
|
199
199
|
var original = safeGet(target, 'settingsScope');
|
|
200
|
-
if (original && typeof original.bind === 'function') return original;
|
|
201
200
|
// DSH 0.1.7 configForms only mirrors Config fields declared volatile.
|
|
202
201
|
// DVR must keep its public Config ordinary for the older supported Host
|
|
203
202
|
// window, so its namespace intentionally uses the local-only ConfigEditor
|
|
204
|
-
// bridge
|
|
203
|
+
// bridge. Even when a Host exposes a legacy-shaped settingsScope, its
|
|
204
|
+
// vision-router view may be empty; keep it only as the delegate for
|
|
205
|
+
// every other namespace instead of letting it bypass the local bridge.
|
|
205
206
|
var official = configFormsBinder(target);
|
|
206
|
-
return syntheticBinder(
|
|
207
|
+
return syntheticBinder(
|
|
208
|
+
ctx,
|
|
209
|
+
original && typeof original.bind === 'function' ? original : official
|
|
210
|
+
);
|
|
207
211
|
}
|
|
208
212
|
if (property === 'get') {
|
|
209
213
|
var getter = Reflect.get(target, property, target);
|
|
@@ -10,7 +10,9 @@ export const SETTINGS_NUMBER_META = Object.freeze({
|
|
|
10
10
|
})
|
|
11
11
|
|
|
12
12
|
export function parseSettingsNumber(key, raw, { allowClear = false } = {}) {
|
|
13
|
-
|
|
13
|
+
// Own keys only: a bare lookup would resolve inherited names such as
|
|
14
|
+
// `__proto__` or `constructor` to prototype machinery instead of "unknown key".
|
|
15
|
+
const meta = Object.hasOwn(SETTINGS_NUMBER_META, key) ? SETTINGS_NUMBER_META[key] : undefined
|
|
14
16
|
if (!meta) return undefined
|
|
15
17
|
if (raw === '' || raw === null || raw === undefined) return allowClear ? { clear: true } : undefined
|
|
16
18
|
const value = Number(raw)
|
|
@@ -758,7 +758,7 @@ export function createBackgroundCapabilityProfiler({
|
|
|
758
758
|
}, interval)
|
|
759
759
|
try { policyTimer?.unref?.() } catch {}
|
|
760
760
|
|
|
761
|
-
const runner = runAxisBenchmark ??
|
|
761
|
+
const runner = runAxisBenchmark ?? runAxis
|
|
762
762
|
await runner({
|
|
763
763
|
ctx,
|
|
764
764
|
config: current,
|
|
@@ -768,6 +768,7 @@ export function createBackgroundCapabilityProfiler({
|
|
|
768
768
|
axis: work.axis,
|
|
769
769
|
signal: controller.signal,
|
|
770
770
|
logger,
|
|
771
|
+
options: invokerOptions,
|
|
771
772
|
onProgress(progress = {}) {
|
|
772
773
|
if (!runningWork || runningWork.candidate.key !== work.candidate.key || runningWork.axis !== work.axis) return
|
|
773
774
|
const completed = Math.max(0, Number(progress.completed) || 0)
|
|
@@ -999,6 +1000,16 @@ export function installBackgroundCapabilityProfiling(ctx, config, core, store, o
|
|
|
999
1000
|
const logger = options.logger ?? ctx?.logger
|
|
1000
1001
|
const imageVerdictStore = options.imageVerdictStore ?? createImageInputVerdictStore({ logger })
|
|
1001
1002
|
const backgroundStopStore = options.backgroundStopStore ?? createBackgroundBenchmarkStopStore({ logger })
|
|
1003
|
+
// Provider transport is one explicit runtime capability shared by automatic
|
|
1004
|
+
// profiling and the user-triggered exact check. Top-level ownership wins so
|
|
1005
|
+
// a stale nested test/customization bag cannot silently bypass live proxy
|
|
1006
|
+
// policy after composition or HMR changes.
|
|
1007
|
+
const invokerOptions = {
|
|
1008
|
+
...(options.invokerOptions ?? {}),
|
|
1009
|
+
...(options.providerTransport === undefined
|
|
1010
|
+
? {}
|
|
1011
|
+
: { providerTransport: options.providerTransport }),
|
|
1012
|
+
}
|
|
1002
1013
|
const profiler = createBackgroundCapabilityProfiler({
|
|
1003
1014
|
ctx,
|
|
1004
1015
|
config,
|
|
@@ -1006,6 +1017,7 @@ export function installBackgroundCapabilityProfiling(ctx, config, core, store, o
|
|
|
1006
1017
|
store,
|
|
1007
1018
|
logger,
|
|
1008
1019
|
...options,
|
|
1020
|
+
invokerOptions,
|
|
1009
1021
|
imageVerdictStore,
|
|
1010
1022
|
backgroundStopStore,
|
|
1011
1023
|
})
|
|
@@ -1120,7 +1132,7 @@ export function installBackgroundCapabilityProfiling(ctx, config, core, store, o
|
|
|
1120
1132
|
provider: body.provider,
|
|
1121
1133
|
model: body.model,
|
|
1122
1134
|
signal: AbortSignal.timeout(VISION_CHECK_TIMEOUT_MS),
|
|
1123
|
-
invokerOptions
|
|
1135
|
+
invokerOptions,
|
|
1124
1136
|
})
|
|
1125
1137
|
await profiler.recordImageSupported(body.provider, body.model)
|
|
1126
1138
|
sendJson(res, 200, result)
|
|
@@ -105,8 +105,20 @@ async function load(file, fsOps, at) {
|
|
|
105
105
|
return new Map();
|
|
106
106
|
}
|
|
107
107
|
const records = retainedStopMap(stops, at);
|
|
108
|
-
|
|
109
|
-
|
|
108
|
+
// Rewrite whenever loading changed the persisted set: v2 stored raw entries
|
|
109
|
+
// that this version must drop, and any later retention rule (category,
|
|
110
|
+
// expiry, fingerprint shape) has to reach the file the same way instead of
|
|
111
|
+
// leaving a stale record that only the runtime filter happens to hide.
|
|
112
|
+
if (version === 2 || records.size !== stops.length) {
|
|
113
|
+
try {
|
|
114
|
+
await save(file, records, fsOps, at);
|
|
115
|
+
}
|
|
116
|
+
catch {
|
|
117
|
+
// This rewrite is opportunistic: a read-only or full disk must not discard the records
|
|
118
|
+
// the load could still read (the catch below returns an empty map). The store's own
|
|
119
|
+
// persist() retries the write and reports it through the logger.
|
|
120
|
+
}
|
|
121
|
+
}
|
|
110
122
|
return records;
|
|
111
123
|
}
|
|
112
124
|
catch (error) {
|
|
@@ -106,7 +106,15 @@ export function installVisionDiagnosticsRoutes(ctx, options = {}) {
|
|
|
106
106
|
// inside the adapter's proxy scope) do not have. Use the fetch the boundary
|
|
107
107
|
// currently owns, with the module-load capture as the fallback.
|
|
108
108
|
const injectedFetch = typeof options.fetchImpl === 'function' ? options.fetchImpl : undefined
|
|
109
|
-
|
|
109
|
+
// Probes must describe the egress real vision turns use. That egress is the
|
|
110
|
+
// Router-owned provider transport, which also owns the proxy/proxyHosts decision;
|
|
111
|
+
// the legacy pair scope below only covers non-router-owned providers, so a
|
|
112
|
+
// vision-http direct backend would otherwise probe with ambient fetch.
|
|
113
|
+
const transport = options.transport
|
|
114
|
+
const transportFetch = typeof transport?.fetch === 'function'
|
|
115
|
+
? (input, init) => transport.fetch(input, init, { allowProxy: true })
|
|
116
|
+
: undefined
|
|
117
|
+
const resolveFetch = () => injectedFetch ?? transportFetch ?? currentLegacyGlobalProxyFetch() ?? moduleFetch
|
|
110
118
|
const connectionConfig = () => {
|
|
111
119
|
try {
|
|
112
120
|
const live = ctx?.get?.('settings')?.get?.('vision-router')
|
|
@@ -419,6 +419,7 @@ export const VISION_MODEL_VISIBILITY_PRELUDE = String.raw`(function(){
|
|
|
419
419
|
function create() {
|
|
420
420
|
var result = originalCreate.apply(this, arguments);
|
|
421
421
|
patchLoader(loader);
|
|
422
|
+
if (result && result !== loader) patchLoader(result);
|
|
422
423
|
return result;
|
|
423
424
|
}
|
|
424
425
|
try { Object.defineProperty(create, '__visionRouterModelVisibility', { value: true }); } catch (_) {}
|
|
@@ -427,31 +428,29 @@ export const VISION_MODEL_VISIBILITY_PRELUDE = String.raw`(function(){
|
|
|
427
428
|
}
|
|
428
429
|
|
|
429
430
|
function install() {
|
|
430
|
-
if (window.__ModuleLoader__) {
|
|
431
|
-
patchLoader(window.__ModuleLoader__);
|
|
432
|
-
return;
|
|
433
|
-
}
|
|
434
431
|
var descriptor = Object.getOwnPropertyDescriptor(window, '__ModuleLoader__');
|
|
432
|
+
var current;
|
|
433
|
+
try { current = window.__ModuleLoader__; } catch (_) { current = undefined; }
|
|
434
|
+
if (current) patchLoader(current);
|
|
435
435
|
if (descriptor && descriptor.configurable === false) return;
|
|
436
|
-
var
|
|
436
|
+
var previousGet = descriptor && descriptor.get;
|
|
437
|
+
var previousSet = descriptor && descriptor.set;
|
|
438
|
+
var stored = descriptor && Object.prototype.hasOwnProperty.call(descriptor, 'value')
|
|
439
|
+
? descriptor.value
|
|
440
|
+
: current;
|
|
437
441
|
Object.defineProperty(window, '__ModuleLoader__', {
|
|
438
442
|
configurable: true,
|
|
439
|
-
enumerable:
|
|
440
|
-
get: function(){ return stored; },
|
|
443
|
+
enumerable: !descriptor || descriptor.enumerable !== false,
|
|
444
|
+
get: function(){ return previousGet ? previousGet.call(window) : stored; },
|
|
441
445
|
set: function(value) {
|
|
442
|
-
stored = value;
|
|
443
|
-
patchLoader(value);
|
|
444
|
-
Object.defineProperty(window, '__ModuleLoader__', {
|
|
445
|
-
configurable: true,
|
|
446
|
-
enumerable: true,
|
|
447
|
-
writable: true,
|
|
448
|
-
value: stored
|
|
449
|
-
});
|
|
446
|
+
if (previousSet) previousSet.call(window, value); else stored = value;
|
|
447
|
+
try { patchLoader(previousGet ? previousGet.call(window) : value); } catch (_) {}
|
|
450
448
|
}
|
|
451
449
|
});
|
|
450
|
+
if (stored) patchLoader(stored);
|
|
452
451
|
}
|
|
453
452
|
|
|
454
|
-
install();
|
|
453
|
+
try { install(); } catch (_) {}
|
|
455
454
|
})();`
|
|
456
455
|
|
|
457
456
|
export function injectVisionModelVisibilityBoundary(html) {
|
package/lib/vision-resilience.js
CHANGED
|
@@ -109,6 +109,35 @@ export function kindForHttpStatus(status) {
|
|
|
109
109
|
return undefined
|
|
110
110
|
}
|
|
111
111
|
|
|
112
|
+
/** Read one field without letting a hostile getter escape the classifier. */
|
|
113
|
+
function safeFailureField(error, name) {
|
|
114
|
+
try {
|
|
115
|
+
return error === null || error === undefined ? undefined : error[name]
|
|
116
|
+
} catch {
|
|
117
|
+
return undefined
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
/** Read the human-readable text; a throwing toString() must not escape either. */
|
|
122
|
+
function safeFailureText(error) {
|
|
123
|
+
try {
|
|
124
|
+
return String(safeFailureField(error, 'message') ?? error ?? '')
|
|
125
|
+
} catch {
|
|
126
|
+
return ''
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
/** Map a branded error code to a kind using own keys only (no prototype hits). */
|
|
131
|
+
function kindForCode(code) {
|
|
132
|
+
let key
|
|
133
|
+
try {
|
|
134
|
+
key = code === null || code === undefined ? '' : String(code)
|
|
135
|
+
} catch {
|
|
136
|
+
key = ''
|
|
137
|
+
}
|
|
138
|
+
return Object.hasOwn(CODE_KIND_MAP, key) ? CODE_KIND_MAP[key] : undefined
|
|
139
|
+
}
|
|
140
|
+
|
|
112
141
|
/**
|
|
113
142
|
* Classify a backend failure into the shared taxonomy plus a provider-retry
|
|
114
143
|
* verdict. Deterministic failures (AUTH / INVALID_REQUEST / REGION / TOS)
|
|
@@ -117,8 +146,8 @@ export function kindForHttpStatus(status) {
|
|
|
117
146
|
* the task deadline still has budget.
|
|
118
147
|
*/
|
|
119
148
|
export function classifyVisionFailure(error) {
|
|
120
|
-
const message =
|
|
121
|
-
let kind =
|
|
149
|
+
const message = safeFailureText(error)
|
|
150
|
+
let kind = kindForCode(safeFailureField(error, 'code')) ?? kindForHttpStatus(safeFailureField(error, 'status'))
|
|
122
151
|
if (kind === undefined) {
|
|
123
152
|
for (const [candidate, patterns] of KIND_BY_PATTERN) {
|
|
124
153
|
if (patterns.some((pattern) => pattern.test(message))) {
|
|
@@ -11,6 +11,7 @@ import {
|
|
|
11
11
|
LiveDescribeCache,
|
|
12
12
|
} from './runtime-reliability.js'
|
|
13
13
|
import { projectDelegatedCallConfig } from './delegated-call-config.js'
|
|
14
|
+
import { isVisionRouterOwnedAdapter } from './adapter-ownership.js'
|
|
14
15
|
import { VISION_RESULT_CODES } from './vision-resilience.js'
|
|
15
16
|
|
|
16
17
|
const toolRuntime = new AsyncLocalStorage()
|
|
@@ -26,7 +27,6 @@ const wrappedLlms = new WeakMap()
|
|
|
26
27
|
const wrappedAdapters = new WeakMap()
|
|
27
28
|
const wrappedDelegatingAdapters = new WeakMap()
|
|
28
29
|
const wrappedTransparentAdapters = new WeakMap()
|
|
29
|
-
const VISION_ROUTER_ADAPTER_OWNER = Symbol.for('dsh-vision-router.adapter-owner')
|
|
30
30
|
const MAX_TRANSPARENT_REASONING_MEMORY = 512
|
|
31
31
|
const VISION_FAILURE_RESULT_CODES = new Set(Object.values(VISION_RESULT_CODES))
|
|
32
32
|
|
|
@@ -434,12 +434,7 @@ function visionChainAdapter(adapter, routes) {
|
|
|
434
434
|
}
|
|
435
435
|
|
|
436
436
|
function visionRouterOwnedAdapter(adapter) {
|
|
437
|
-
|
|
438
|
-
try {
|
|
439
|
-
return adapter[VISION_ROUTER_ADAPTER_OWNER] !== undefined
|
|
440
|
-
} catch {
|
|
441
|
-
return false
|
|
442
|
-
}
|
|
437
|
+
return isVisionRouterOwnedAdapter(adapter)
|
|
443
438
|
}
|
|
444
439
|
|
|
445
440
|
function explicitReasoningEffort(options) {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "dsh-vision-router",
|
|
3
|
-
"version": "3.0.
|
|
3
|
+
"version": "3.0.2",
|
|
4
4
|
"description": "Eyes for text-only DeepSeek Harness agents: built-in free vision chain + pixel-level tools. Remote vision providers, including the default free fallback, receive image content unless strict local-only use is configured.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"repository": {
|
|
@@ -89,7 +89,7 @@
|
|
|
89
89
|
"@deepseek-ai/dsh-storage-domain": "0.2.0-rc.2"
|
|
90
90
|
},
|
|
91
91
|
"scripts": {
|
|
92
|
-
"test": "node --test tests/index-modularization.test.js 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/cache-atomic-write-concurrency.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/degraded-local-evidence.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/artifact-path-security.test.js tests/http-compat.test.js tests/catalog-corrections.test.js tests/release-registry-identity.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/activation-shim-contract.test.js tests/bundle-defaults.test.js tests/file-logger.test.js tests/official-deepseek-catalog.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/architecture-2.3-gate.test.js tests/esm-cycle-audit.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/local-only-vision.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/vision-diagnostics-routes.test.js tests/repetition-guard.test.js tests/guide-scroll-gate.test.js tests/settings-scroll-jank-regression.test.js tests/free-cloud-first.test.js tests/dsh-host-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/issue-512-tool-restriction.test.js tests/issue-606-session-source-ownership.test.js tests/pi-ai-bridge-wire-compat.test.js tests/fetch-wrapper-lifecycle.test.js tests/fetch-wrapper-composition.test.js tests/provider-probe-proxy-scope.test.js tests/lifecycle-invariants.test.js tests/vision-tool-settings-generation.test.js tests/release-package-content-identity.test.js tests/release-state-machine.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/security-resource-boundaries.test.js tests/security-property-fuzz.test.js tests/security-adversarial-fuzz-contract.test.js tests/runtime-i18n.test.js tests/long-ocr-empty-retry.test.js tests/tesseract-adaptive-ocr.test.js tests/tesseract-node24-boot.test.js tests/session-vision-state.test.js tests/session-vision-state-integration.test.js tests/session-vision-event-feed.test.js tests/session-vision-policy.test.js tests/session-vision-policy-domain.test.js tests/session-vision-delegation.test.js tests/session-vision-delegation-boundary.test.js tests/subagent-vision-inheritance-characterization.test.js tests/subagent-vision-runtime-integration.test.js tests/subagent-vision-cold-resume.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 && pnpm test:regression-closure && node --test tests/vision-backend-runtime-policy.test.js",
|
|
92
|
+
"test": "node --test tests/index-modularization.test.js 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/cache-atomic-write-concurrency.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/degraded-local-evidence.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/artifact-path-security.test.js tests/http-compat.test.js tests/catalog-corrections.test.js tests/release-registry-identity.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/activation-shim-contract.test.js tests/bundle-defaults.test.js tests/file-logger.test.js tests/official-deepseek-catalog.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/provider-transport-threading.test.js tests/architecture-2.3-gate.test.js tests/host-settings-seam.test.js tests/drift-canary-settings-seam.test.js tests/esm-cycle-audit.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/local-only-vision.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/vision-diagnostics-routes.test.js tests/repetition-guard.test.js tests/guide-scroll-gate.test.js tests/settings-scroll-jank-regression.test.js tests/free-cloud-first.test.js tests/dsh-host-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/issue-512-tool-restriction.test.js tests/issue-606-session-source-ownership.test.js tests/pi-ai-bridge-wire-compat.test.js tests/fetch-wrapper-lifecycle.test.js tests/fetch-wrapper-composition.test.js tests/provider-probe-proxy-scope.test.js tests/lifecycle-invariants.test.js tests/vision-tool-settings-generation.test.js tests/release-package-content-identity.test.js tests/release-state-machine.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/security-resource-boundaries.test.js tests/security-property-fuzz.test.js tests/security-adversarial-fuzz-contract.test.js tests/runtime-i18n.test.js tests/long-ocr-empty-retry.test.js tests/tesseract-adaptive-ocr.test.js tests/tesseract-node24-boot.test.js tests/session-vision-state.test.js tests/session-vision-state-integration.test.js tests/session-vision-event-feed.test.js tests/session-vision-policy.test.js tests/session-vision-policy-domain.test.js tests/session-vision-delegation.test.js tests/session-vision-delegation-boundary.test.js tests/subagent-vision-inheritance-characterization.test.js tests/subagent-vision-runtime-integration.test.js tests/subagent-vision-cold-resume.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 && pnpm test:regression-closure && node --test tests/vision-backend-runtime-policy.test.js tests/vision-router-adversarial-review-fixes.test.js tests/web-panel-delegation.test.js",
|
|
93
93
|
"test:core": "node --test tests/core.test.js tests/degraded-local-evidence.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",
|
|
94
94
|
"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",
|
|
95
95
|
"test:session": "node --test tests/session-vision-state.test.js tests/session-vision-state-integration.test.js tests/session-vision-policy.test.js tests/session-vision-delegation.test.js tests/session-vision-delegation-boundary.test.js tests/subagent-vision-inheritance-characterization.test.js tests/subagent-vision-runtime-integration.test.js tests/subagent-vision-cold-resume.test.js tests/session-vision-policy-domain.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",
|