dsh-vision-router 2.2.0 → 2.2.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 +3 -3
- package/README.zh.md +3 -3
- package/docs/releases/v2.2.1.md +22 -0
- package/docs/releases/v2.2.2.md +21 -0
- package/lib/client.js +67 -27
- package/lib/core-primitives.js +11 -0
- package/lib/dsh-contract-compat.js +28 -5
- package/lib/dsh-settings-017-compat.js +416 -0
- package/lib/fetch-wrapper-lifecycle.js +56 -0
- package/lib/legacy-global-proxy-boundary.js +4 -3
- package/lib/pi-ai-bridge-wire-compat.js +4 -3
- package/lib/pixel-diff-stream.js +12 -6
- package/lib/public-entry.js +21 -1
- package/lib/session-message-source-compat.js +92 -0
- package/lib/session-vision-index.js +238 -10
- package/lib/settings-client-017-compat.js +331 -0
- package/lib/settings-client-rc8-lifecycle.js +13 -0
- package/lib/vision-breaker-shadow-health.js +11 -1
- package/lib/web/dsh-settings-017-root-transport.js +299 -0
- package/lib/web/remote-settings-client.js +200 -0
- package/package.json +3 -3
|
@@ -31,6 +31,14 @@ export const SETTINGS_RC8_CLIENT_PRELUDE = String.raw`(function(){
|
|
|
31
31
|
return undefined;
|
|
32
32
|
}
|
|
33
33
|
|
|
34
|
+
function isLoopbackLocation(locationLike) {
|
|
35
|
+
var hostname = locationLike && typeof locationLike.hostname === 'string'
|
|
36
|
+
? locationLike.hostname.toLowerCase().replace(/^\[|\]$/g, '')
|
|
37
|
+
: '';
|
|
38
|
+
if (hostname === 'localhost' || hostname.endsWith('.localhost') || hostname === '::1') return true;
|
|
39
|
+
return /^127(?:\.\d{1,3}){3}$/.test(hostname);
|
|
40
|
+
}
|
|
41
|
+
|
|
34
42
|
async function writePermission(operation, value, revision) {
|
|
35
43
|
if (typeof fetch !== 'function') throw new Error('Vision Router local settings transport is unavailable');
|
|
36
44
|
var payload = { operation: operation };
|
|
@@ -269,6 +277,11 @@ export const SETTINGS_RC8_CLIENT_PRELUDE = String.raw`(function(){
|
|
|
269
277
|
if (connectionCache && connectionCache.has(connection)) return connectionCache.get(connection);
|
|
270
278
|
var wrapped = new Proxy(connection, {
|
|
271
279
|
get: function(target, property) {
|
|
280
|
+
if (property === 'isLoopback') {
|
|
281
|
+
var locationLike;
|
|
282
|
+
try { locationLike = window && window.location; } catch (_) { locationLike = undefined; }
|
|
283
|
+
if (isLoopbackLocation(locationLike)) return true;
|
|
284
|
+
}
|
|
272
285
|
if (property === 'rpc') return wrapRpc(Reflect.get(target, property, target));
|
|
273
286
|
var value = Reflect.get(target, property, target);
|
|
274
287
|
return typeof value === 'function' ? value.bind(target) : value;
|
|
@@ -3,7 +3,17 @@ import { createSessionTurnResolver } from './session-turn-resolver.js'
|
|
|
3
3
|
|
|
4
4
|
function legacyTurnNumberOf(session) {
|
|
5
5
|
try {
|
|
6
|
-
|
|
6
|
+
// dsh 0.1.2-alpha.4 removed the bare `session.events` array in favor of
|
|
7
|
+
// `session.snapshotEvents()`; mirror legacySessionEvents
|
|
8
|
+
// (session-vision-index.js), which prefers the snapshot when both surfaces
|
|
9
|
+
// exist — transitional Hosts keep the bare array but leave it stale, so a
|
|
10
|
+
// bare-array-first read would resolve a stale turn on them.
|
|
11
|
+
let events
|
|
12
|
+
if (typeof session?.snapshotEvents === 'function') {
|
|
13
|
+
events = session.snapshotEvents()
|
|
14
|
+
} else {
|
|
15
|
+
events = session?.events
|
|
16
|
+
}
|
|
7
17
|
if (!Array.isArray(events)) return 0
|
|
8
18
|
const last = events.findLast((event) => event && event.type === 'turn/start')
|
|
9
19
|
return last && Number.isInteger(last.data?.turn) ? last.data.turn : 0
|
|
@@ -0,0 +1,299 @@
|
|
|
1
|
+
import {
|
|
2
|
+
DSH_017_SETTINGS_COMPAT_MARK,
|
|
3
|
+
LOCAL_SETTINGS_PATH,
|
|
4
|
+
} from '../dsh-settings-017-compat.js'
|
|
5
|
+
|
|
6
|
+
const BODY_LIMIT_BYTES = 256 * 1024
|
|
7
|
+
const ROOT_TRANSPORT_REGISTRY_KEY = Symbol.for('dsh-vision-router.settings-017-root-local-transport')
|
|
8
|
+
const SLOW_MUTATION_MS = 5_000
|
|
9
|
+
const TRACE_ENV = 'DVR_SETTINGS_017_TRACE'
|
|
10
|
+
|
|
11
|
+
function objectLike(value) {
|
|
12
|
+
return value !== null && typeof value === 'object'
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
function trace(stage, detail) {
|
|
16
|
+
if (process.env[TRACE_ENV] !== '1') return
|
|
17
|
+
try {
|
|
18
|
+
console.warn(`vision-router: DSH 0.1.7 settings trace ${stage}${detail === undefined ? '' : ` ${JSON.stringify(detail)}`}`)
|
|
19
|
+
} catch {}
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
function transportRegistry() {
|
|
23
|
+
let registry = globalThis[ROOT_TRANSPORT_REGISTRY_KEY]
|
|
24
|
+
if (!(registry instanceof WeakMap)) {
|
|
25
|
+
registry = new WeakMap()
|
|
26
|
+
Object.defineProperty(globalThis, ROOT_TRANSPORT_REGISTRY_KEY, {
|
|
27
|
+
value: registry,
|
|
28
|
+
configurable: true,
|
|
29
|
+
})
|
|
30
|
+
}
|
|
31
|
+
return registry
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
function rootOf(ctx) {
|
|
35
|
+
try {
|
|
36
|
+
if (objectLike(ctx?.root)) return ctx.root
|
|
37
|
+
} catch {}
|
|
38
|
+
return ctx
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
function stateFor(root) {
|
|
42
|
+
const registry = transportRegistry()
|
|
43
|
+
let state = registry.get(root)
|
|
44
|
+
if (!state) {
|
|
45
|
+
state = {
|
|
46
|
+
current: undefined,
|
|
47
|
+
routeOwner: undefined,
|
|
48
|
+
routeInstalling: false,
|
|
49
|
+
generationSequence: 0,
|
|
50
|
+
requestSequence: 0,
|
|
51
|
+
}
|
|
52
|
+
registry.set(root, state)
|
|
53
|
+
}
|
|
54
|
+
return state
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
function serviceOf(ctx, name) {
|
|
58
|
+
try {
|
|
59
|
+
const value = typeof ctx?.get === 'function' ? ctx.get(name) : undefined
|
|
60
|
+
if (value !== undefined && value !== null) return value
|
|
61
|
+
} catch {}
|
|
62
|
+
try {
|
|
63
|
+
const value = ctx?.[name]
|
|
64
|
+
return value === undefined || value === null ? undefined : value
|
|
65
|
+
} catch {
|
|
66
|
+
return undefined
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
function compatibleSettings(ctx) {
|
|
71
|
+
const settings = serviceOf(ctx, 'settings')
|
|
72
|
+
if (settings?.[DSH_017_SETTINGS_COMPAT_MARK] !== true) return undefined
|
|
73
|
+
if (typeof settings.describe !== 'function' || typeof settings.mutate !== 'function') return undefined
|
|
74
|
+
return settings
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
function namespaceDescriptor(settings) {
|
|
78
|
+
const descriptors = settings.describe({ redactSecrets: true })
|
|
79
|
+
return Array.isArray(descriptors) ? descriptors.find((entry) => entry?.ns === 'vision-router') : undefined
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
function sendJson(res, status, body) {
|
|
83
|
+
res.writeHead(status, {
|
|
84
|
+
'content-type': 'application/json; charset=utf-8',
|
|
85
|
+
'cache-control': 'no-store',
|
|
86
|
+
})
|
|
87
|
+
res.end(JSON.stringify(body))
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
async function readJson(req) {
|
|
91
|
+
let size = 0
|
|
92
|
+
const chunks = []
|
|
93
|
+
for await (const chunk of req) {
|
|
94
|
+
const bytes = Buffer.isBuffer(chunk) ? chunk : Buffer.from(chunk)
|
|
95
|
+
size += bytes.length
|
|
96
|
+
if (size > BODY_LIMIT_BYTES) throw Object.assign(new Error('request body too large'), { statusCode: 413 })
|
|
97
|
+
chunks.push(bytes)
|
|
98
|
+
}
|
|
99
|
+
if (chunks.length === 0) return {}
|
|
100
|
+
return JSON.parse(Buffer.concat(chunks).toString('utf8'))
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
function currentSettings(state) {
|
|
104
|
+
const generation = state.current
|
|
105
|
+
if (!generation || generation.active === false) return undefined
|
|
106
|
+
try {
|
|
107
|
+
return generation.resolve()
|
|
108
|
+
} catch {
|
|
109
|
+
return undefined
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
function flattenEffectLabels(effects) {
|
|
114
|
+
const labels = []
|
|
115
|
+
const visit = (rows) => {
|
|
116
|
+
if (!Array.isArray(rows)) return
|
|
117
|
+
for (const row of rows) {
|
|
118
|
+
if (!row || typeof row !== 'object') continue
|
|
119
|
+
if (typeof row.label === 'string') labels.push(row.label)
|
|
120
|
+
visit(row.children)
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
visit(effects)
|
|
124
|
+
return labels
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
function slowMutationSnapshot(root) {
|
|
128
|
+
const loader = serviceOf(root, 'loader')
|
|
129
|
+
let entries = []
|
|
130
|
+
try { entries = typeof loader?.entries === 'function' ? [...loader.entries()] : [] } catch {}
|
|
131
|
+
return entries.flatMap((entry) => {
|
|
132
|
+
const fiber = entry?.fiber
|
|
133
|
+
if (!fiber || (entry?.options?.id !== 'vision-router' && !fiber.inertia)) return []
|
|
134
|
+
let effects = []
|
|
135
|
+
try { effects = typeof fiber.getEffects === 'function' ? flattenEffectLabels(fiber.getEffects()) : [] } catch {}
|
|
136
|
+
return [{
|
|
137
|
+
id: entry?.options?.id,
|
|
138
|
+
state: fiber.state,
|
|
139
|
+
inertia: Boolean(fiber.inertia),
|
|
140
|
+
effects,
|
|
141
|
+
}]
|
|
142
|
+
})
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
function beginSlowMutationWatch(root, requestId) {
|
|
146
|
+
return setTimeout(() => {
|
|
147
|
+
const snapshot = slowMutationSnapshot(root)
|
|
148
|
+
const message = `vision-router: DSH 0.1.7 settings mutation still reconciling after ${SLOW_MUTATION_MS}ms request=${requestId} ${JSON.stringify(snapshot)}`
|
|
149
|
+
try {
|
|
150
|
+
if (root?.logger && typeof root.logger.warn === 'function') root.logger.warn(message)
|
|
151
|
+
else console.warn(message)
|
|
152
|
+
} catch {
|
|
153
|
+
try { console.warn(message) } catch {}
|
|
154
|
+
}
|
|
155
|
+
}, SLOW_MUTATION_MS)
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
function routeFor(root, state) {
|
|
159
|
+
return {
|
|
160
|
+
kind: 'exact',
|
|
161
|
+
path: LOCAL_SETTINGS_PATH,
|
|
162
|
+
async handler(req, res) {
|
|
163
|
+
const requestId = ++state.requestSequence
|
|
164
|
+
trace('handler-enter', { requestId, method: req.method, generation: state.current?.id })
|
|
165
|
+
const settings = currentSettings(state)
|
|
166
|
+
if (!settings) {
|
|
167
|
+
trace('settings-unavailable', { requestId })
|
|
168
|
+
sendJson(res, 404, { ok: false, error: { code: 'settings-unavailable', message: 'Vision Router settings are unavailable' } })
|
|
169
|
+
return
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
if (req.method === 'GET') {
|
|
173
|
+
const descriptor = namespaceDescriptor(settings)
|
|
174
|
+
if (!descriptor) {
|
|
175
|
+
sendJson(res, 404, { ok: false, error: { code: 'settings-unavailable', message: 'Vision Router settings are unavailable' } })
|
|
176
|
+
return
|
|
177
|
+
}
|
|
178
|
+
sendJson(res, 200, { ok: true, value: { ...descriptor, writable: settings.writable === true } })
|
|
179
|
+
trace('response', { requestId, status: 200, revision: descriptor.revision })
|
|
180
|
+
return
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
if (req.method !== 'POST') {
|
|
184
|
+
res.setHeader('Allow', 'GET, POST')
|
|
185
|
+
sendJson(res, 405, { ok: false, error: { code: 'method-not-allowed', message: 'method not allowed' } })
|
|
186
|
+
return
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
let slowTimer
|
|
190
|
+
try {
|
|
191
|
+
trace('body-read-start', { requestId })
|
|
192
|
+
const payload = await readJson(req)
|
|
193
|
+
trace('body-read-done', { requestId, expectedRevision: payload?.expectedRevision, ops: payload?.ops?.length })
|
|
194
|
+
slowTimer = beginSlowMutationWatch(root, requestId)
|
|
195
|
+
trace('mutate-start', { requestId, generation: state.current?.id })
|
|
196
|
+
await settings.mutate('vision-router', payload?.ops, payload?.expectedRevision)
|
|
197
|
+
trace('mutate-done', { requestId, generation: state.current?.id })
|
|
198
|
+
|
|
199
|
+
// ConfigEditor.edit() reconciles the profile and can dispose the plugin
|
|
200
|
+
// generation that accepted this request. The route itself is owned by a
|
|
201
|
+
// root child fiber that depends only on WebServer, so finish the response
|
|
202
|
+
// from the newly-mounted DVR generation when present.
|
|
203
|
+
const afterSettings = currentSettings(state) ?? settings
|
|
204
|
+
const descriptor = namespaceDescriptor(afterSettings)
|
|
205
|
+
if (!descriptor) throw new Error('Vision Router settings disappeared after the write')
|
|
206
|
+
sendJson(res, 200, { ok: true, value: { ...descriptor, writable: afterSettings.writable === true } })
|
|
207
|
+
trace('response', { requestId, status: 200, revision: descriptor.revision })
|
|
208
|
+
} catch (error) {
|
|
209
|
+
const conflict = error?.code === 'SETTINGS_CONFLICT'
|
|
210
|
+
const status = error?.statusCode ?? (conflict ? 409 : 400)
|
|
211
|
+
trace('response-error', { requestId, status, code: error?.code, message: error?.message ?? String(error) })
|
|
212
|
+
sendJson(res, status, {
|
|
213
|
+
ok: false,
|
|
214
|
+
error: {
|
|
215
|
+
code: conflict ? 'settings-conflict' : 'settings-rejected',
|
|
216
|
+
message: error?.message ?? String(error),
|
|
217
|
+
...(conflict ? { details: { expected: error.expected, actual: error.actual } } : {}),
|
|
218
|
+
},
|
|
219
|
+
})
|
|
220
|
+
} finally {
|
|
221
|
+
if (slowTimer) clearTimeout(slowTimer)
|
|
222
|
+
}
|
|
223
|
+
},
|
|
224
|
+
}
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
function ensureRootRoute(root, state) {
|
|
228
|
+
if (state.routeOwner || state.routeInstalling) return
|
|
229
|
+
if (typeof root?.inject !== 'function') {
|
|
230
|
+
throw new Error('Vision Router requires root dependency injection for the DSH 0.1.7 local settings route')
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
state.routeInstalling = true
|
|
234
|
+
try {
|
|
235
|
+
const owner = root.inject(['webServer'], (webCtx) => {
|
|
236
|
+
if (!webCtx?.webServer || typeof webCtx.webServer.register !== 'function' || typeof webCtx.effect !== 'function') {
|
|
237
|
+
throw new Error('Vision Router requires WebServer route registration on DSH 0.1.7')
|
|
238
|
+
}
|
|
239
|
+
trace('route-register', {})
|
|
240
|
+
webCtx.effect(
|
|
241
|
+
() => {
|
|
242
|
+
const dispose = webCtx.webServer.register(routeFor(root, state))
|
|
243
|
+
return () => {
|
|
244
|
+
trace('route-dispose', {})
|
|
245
|
+
dispose()
|
|
246
|
+
}
|
|
247
|
+
},
|
|
248
|
+
'vision-router: DSH 0.1.7 root local settings transport',
|
|
249
|
+
)
|
|
250
|
+
})
|
|
251
|
+
// `root.inject()` creates a root child fiber. It is independent from the
|
|
252
|
+
// DVR plugin fiber and therefore survives ConfigEditor reconciliation of
|
|
253
|
+
// the `vision-router` entry. Keep the owner reachable for the process life.
|
|
254
|
+
state.routeOwner = owner ?? true
|
|
255
|
+
} catch (error) {
|
|
256
|
+
state.routeOwner = undefined
|
|
257
|
+
throw error
|
|
258
|
+
} finally {
|
|
259
|
+
state.routeInstalling = false
|
|
260
|
+
}
|
|
261
|
+
}
|
|
262
|
+
|
|
263
|
+
/**
|
|
264
|
+
* ConfigEditor.edit() reconciles the plugin configuration and therefore disposes
|
|
265
|
+
* the DVR plugin fiber while a Settings POST is still in flight. A dedicated
|
|
266
|
+
* root child fiber owns the HTTP route and depends only on WebServer; individual
|
|
267
|
+
* DVR generations only publish a resolver for their current settings facade.
|
|
268
|
+
*/
|
|
269
|
+
export function installDsh017RootLocalSettingsTransport(ctx) {
|
|
270
|
+
if (!ctx || typeof ctx.inject !== 'function') return
|
|
271
|
+
const root = rootOf(ctx)
|
|
272
|
+
if (!objectLike(root)) return
|
|
273
|
+
const state = stateFor(root)
|
|
274
|
+
|
|
275
|
+
ctx.inject(['settings'], (settingsCtx) => {
|
|
276
|
+
const mounted = compatibleSettings(settingsCtx)
|
|
277
|
+
if (!mounted) return
|
|
278
|
+
|
|
279
|
+
const generation = {
|
|
280
|
+
id: ++state.generationSequence,
|
|
281
|
+
active: true,
|
|
282
|
+
resolve: () => compatibleSettings(ctx) ?? mounted,
|
|
283
|
+
}
|
|
284
|
+
state.current = generation
|
|
285
|
+
trace('generation-mount', { generation: generation.id })
|
|
286
|
+
ensureRootRoute(root, state)
|
|
287
|
+
|
|
288
|
+
if (typeof settingsCtx?.effect === 'function') {
|
|
289
|
+
settingsCtx.effect(
|
|
290
|
+
() => () => {
|
|
291
|
+
generation.active = false
|
|
292
|
+
if (state.current === generation) state.current = undefined
|
|
293
|
+
trace('generation-dispose', { generation: generation.id })
|
|
294
|
+
},
|
|
295
|
+
'vision-router: DSH 0.1.7 settings generation',
|
|
296
|
+
)
|
|
297
|
+
}
|
|
298
|
+
})
|
|
299
|
+
}
|
|
@@ -1,8 +1,208 @@
|
|
|
1
|
+
import { htmlHasScriptMarker } from '../html-script-marker.js'
|
|
1
2
|
import { installVisionRouterRemoteSettingsBridge } from '../remote-settings-bridge.js'
|
|
2
3
|
import { installSettingsRc8ClientLifecycle } from '../settings-client-rc8-lifecycle.js'
|
|
3
4
|
|
|
5
|
+
const SETTINGS_CONFIG_FORMS_MARK = 'data-vision-router-settings-configforms-compat'
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* DSH 0.1.7 replaces the browser `settingsScope` service with `configForms`.
|
|
9
|
+
* Vision Router still supports older Hosts, so the browser bundle cannot hard
|
|
10
|
+
* depend on either service name. This prelude removes the legacy hard service
|
|
11
|
+
* edge at module-factory time and presents the old `settingsScope.bind()` face
|
|
12
|
+
* over whichever official settings service the active Host provides.
|
|
13
|
+
*/
|
|
14
|
+
export const SETTINGS_CONFIG_FORMS_CLIENT_PRELUDE = String.raw`(function(){
|
|
15
|
+
'use strict';
|
|
16
|
+
var TARGET = 'dsh-vision-router';
|
|
17
|
+
var FLAG = '__visionRouterSettingsConfigFormsCompat';
|
|
18
|
+
var contextCache = typeof WeakMap === 'function' ? new WeakMap() : undefined;
|
|
19
|
+
var binderCache = typeof WeakMap === 'function' ? new WeakMap() : undefined;
|
|
20
|
+
var connectionCache = typeof WeakMap === 'function' ? new WeakMap() : undefined;
|
|
21
|
+
|
|
22
|
+
function safeGet(ctx, name) {
|
|
23
|
+
if (!ctx) return undefined;
|
|
24
|
+
try {
|
|
25
|
+
if (typeof ctx.get === 'function') {
|
|
26
|
+
var value = ctx.get(name);
|
|
27
|
+
if (value !== undefined && value !== null) return value;
|
|
28
|
+
}
|
|
29
|
+
} catch (_) {}
|
|
30
|
+
try { return ctx[name]; } catch (_) { return undefined; }
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
function isLoopbackLocation(locationLike) {
|
|
34
|
+
var hostname = locationLike && typeof locationLike.hostname === 'string'
|
|
35
|
+
? locationLike.hostname.toLowerCase().replace(/^\[|\]$/g, '')
|
|
36
|
+
: '';
|
|
37
|
+
if (hostname === 'localhost' || hostname.endsWith('.localhost') || hostname === '::1') return true;
|
|
38
|
+
return /^127(?:\.\d{1,3}){3}$/.test(hostname);
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
function normalizeConnection(connection) {
|
|
42
|
+
if (!connection || typeof connection !== 'object') return connection;
|
|
43
|
+
var locationLike;
|
|
44
|
+
try { locationLike = window && window.location; } catch (_) { locationLike = undefined; }
|
|
45
|
+
if (!isLoopbackLocation(locationLike) || connection.isLoopback !== false) return connection;
|
|
46
|
+
if (connectionCache && connectionCache.has(connection)) return connectionCache.get(connection);
|
|
47
|
+
var wrapped = new Proxy(connection, {
|
|
48
|
+
get: function(target, property) {
|
|
49
|
+
if (property === 'isLoopback') return true;
|
|
50
|
+
var value = Reflect.get(target, property, target);
|
|
51
|
+
return typeof value === 'function' ? value.bind(target) : value;
|
|
52
|
+
}
|
|
53
|
+
});
|
|
54
|
+
if (connectionCache) connectionCache.set(connection, wrapped);
|
|
55
|
+
return wrapped;
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
function legacyBinder(ctx) {
|
|
59
|
+
var binder = safeGet(ctx, 'settingsScope');
|
|
60
|
+
return binder && typeof binder.bind === 'function' ? binder : undefined;
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
function configFormsBinder(ctx) {
|
|
64
|
+
var forms = safeGet(ctx, 'configForms');
|
|
65
|
+
if (!forms || typeof forms.get !== 'function') return undefined;
|
|
66
|
+
if (binderCache && binderCache.has(forms)) return binderCache.get(forms);
|
|
67
|
+
var binder = {
|
|
68
|
+
bind: function(spec) {
|
|
69
|
+
var namespace = spec && spec.namespace;
|
|
70
|
+
if (typeof namespace !== 'string' || namespace.length === 0) {
|
|
71
|
+
throw new TypeError('settings namespace must be a non-empty string');
|
|
72
|
+
}
|
|
73
|
+
return forms.get(namespace);
|
|
74
|
+
}
|
|
75
|
+
};
|
|
76
|
+
if (binderCache) binderCache.set(forms, binder);
|
|
77
|
+
return binder;
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
function resolveBinder(ctx) {
|
|
81
|
+
return legacyBinder(ctx) || configFormsBinder(ctx);
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
function wrapContext(ctx) {
|
|
85
|
+
if (!ctx || typeof ctx !== 'object') return ctx;
|
|
86
|
+
if (contextCache && contextCache.has(ctx)) return contextCache.get(ctx);
|
|
87
|
+
var wrapped = new Proxy(ctx, {
|
|
88
|
+
get: function(target, property) {
|
|
89
|
+
if (property === 'settingsScope') {
|
|
90
|
+
var binder = resolveBinder(target);
|
|
91
|
+
if (!binder) {
|
|
92
|
+
throw new Error('Vision Router requires DSH settingsScope or configForms');
|
|
93
|
+
}
|
|
94
|
+
return binder;
|
|
95
|
+
}
|
|
96
|
+
if (property === 'get') {
|
|
97
|
+
var getter = Reflect.get(target, property, target);
|
|
98
|
+
if (typeof getter !== 'function') return getter;
|
|
99
|
+
return function(name) {
|
|
100
|
+
var value = getter.call(target, name);
|
|
101
|
+
return name === 'connection' ? normalizeConnection(value) : value;
|
|
102
|
+
};
|
|
103
|
+
}
|
|
104
|
+
var value = Reflect.get(target, property, target);
|
|
105
|
+
return typeof value === 'function' ? value.bind(target) : value;
|
|
106
|
+
}
|
|
107
|
+
});
|
|
108
|
+
if (contextCache) contextCache.set(ctx, wrapped);
|
|
109
|
+
return wrapped;
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
function rewriteInject(exports) {
|
|
113
|
+
if (!exports || !Array.isArray(exports.inject) || exports.inject.indexOf('settingsScope') === -1) return;
|
|
114
|
+
exports.inject = exports.inject.filter(function(name){ return name !== 'settingsScope'; });
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
function patchLiveLoader(loader) {
|
|
118
|
+
if (!loader || typeof loader.load !== 'function' || loader.load[FLAG]) return;
|
|
119
|
+
var original = loader.load;
|
|
120
|
+
function load(spec) {
|
|
121
|
+
if (spec && spec.id === TARGET && typeof spec.factory === 'function') {
|
|
122
|
+
var factory = spec.factory;
|
|
123
|
+
spec = Object.assign({}, spec, {
|
|
124
|
+
factory: function(require) {
|
|
125
|
+
var exports = factory(require);
|
|
126
|
+
rewriteInject(exports);
|
|
127
|
+
if (exports && typeof exports.apply === 'function' && !exports.apply[FLAG]) {
|
|
128
|
+
var apply = exports.apply;
|
|
129
|
+
var wrappedApply = function(ctx) {
|
|
130
|
+
var rest = Array.prototype.slice.call(arguments, 1);
|
|
131
|
+
return apply.apply(exports, [wrapContext(ctx)].concat(rest));
|
|
132
|
+
};
|
|
133
|
+
Object.defineProperty(wrappedApply, FLAG, { value: true });
|
|
134
|
+
exports.apply = wrappedApply;
|
|
135
|
+
}
|
|
136
|
+
return exports;
|
|
137
|
+
}
|
|
138
|
+
});
|
|
139
|
+
}
|
|
140
|
+
return original.call(this, spec);
|
|
141
|
+
}
|
|
142
|
+
Object.defineProperty(load, FLAG, { value: true });
|
|
143
|
+
loader.load = load;
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
function patchCreate(loader) {
|
|
147
|
+
if (!loader) return;
|
|
148
|
+
patchLiveLoader(loader);
|
|
149
|
+
if (typeof loader.create !== 'function' || loader.create[FLAG]) return;
|
|
150
|
+
var originalCreate = loader.create;
|
|
151
|
+
function create() {
|
|
152
|
+
var result = originalCreate.apply(this, arguments);
|
|
153
|
+
patchLiveLoader(loader);
|
|
154
|
+
return result;
|
|
155
|
+
}
|
|
156
|
+
Object.defineProperty(create, FLAG, { value: true });
|
|
157
|
+
loader.create = create;
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
function install() {
|
|
161
|
+
if (window.__ModuleLoader__) {
|
|
162
|
+
patchCreate(window.__ModuleLoader__);
|
|
163
|
+
return;
|
|
164
|
+
}
|
|
165
|
+
var descriptor = Object.getOwnPropertyDescriptor(window, '__ModuleLoader__');
|
|
166
|
+
if (descriptor && descriptor.configurable === false) return;
|
|
167
|
+
var previousGet = descriptor && descriptor.get;
|
|
168
|
+
var previousSet = descriptor && descriptor.set;
|
|
169
|
+
var stored = descriptor && Object.prototype.hasOwnProperty.call(descriptor, 'value') ? descriptor.value : undefined;
|
|
170
|
+
Object.defineProperty(window, '__ModuleLoader__', {
|
|
171
|
+
configurable: true,
|
|
172
|
+
enumerable: !descriptor || descriptor.enumerable !== false,
|
|
173
|
+
get: function(){ return previousGet ? previousGet.call(window) : stored; },
|
|
174
|
+
set: function(value) {
|
|
175
|
+
if (previousSet) previousSet.call(window, value); else stored = value;
|
|
176
|
+
try { patchCreate(previousGet ? previousGet.call(window) : value); } catch (_) {}
|
|
177
|
+
}
|
|
178
|
+
});
|
|
179
|
+
if (stored) patchCreate(stored);
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
try { install(); } catch (_) {}
|
|
183
|
+
})();`
|
|
184
|
+
|
|
185
|
+
export function injectSettingsConfigFormsCompatPrelude(html) {
|
|
186
|
+
if (typeof html !== 'string' || htmlHasScriptMarker(html, SETTINGS_CONFIG_FORMS_MARK)) return html
|
|
187
|
+
const safe = SETTINGS_CONFIG_FORMS_CLIENT_PRELUDE.replace(/<\/script/gi, '<\\/script')
|
|
188
|
+
const script = `<script ${SETTINGS_CONFIG_FORMS_MARK}>${safe}</script>`
|
|
189
|
+
const closeHead = html.indexOf('</head>')
|
|
190
|
+
return closeHead === -1 ? `${html}${script}` : `${html.slice(0, closeHead)}${script}${html.slice(closeHead)}`
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
export function installSettingsConfigFormsCompat(ctx) {
|
|
194
|
+
if (!ctx || typeof ctx.inject !== 'function') return
|
|
195
|
+
ctx.inject(['webServer'], (webCtx) => {
|
|
196
|
+
webCtx.effect(
|
|
197
|
+
() => webCtx.webServer.tapIndex(injectSettingsConfigFormsCompatPrelude),
|
|
198
|
+
'vision-router: settings configForms compatibility',
|
|
199
|
+
)
|
|
200
|
+
})
|
|
201
|
+
}
|
|
202
|
+
|
|
4
203
|
export function installVisionRemoteSettingsClient(ctx, logger) {
|
|
5
204
|
installVisionRouterRemoteSettingsBridge(ctx, logger)
|
|
205
|
+
installSettingsConfigFormsCompat(ctx)
|
|
6
206
|
installSettingsRc8ClientLifecycle(ctx)
|
|
7
207
|
return ctx
|
|
8
208
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "dsh-vision-router",
|
|
3
|
-
"version": "2.2.
|
|
3
|
+
"version": "2.2.2",
|
|
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": {
|
|
@@ -70,12 +70,12 @@
|
|
|
70
70
|
"sharp": "^0.35.4"
|
|
71
71
|
},
|
|
72
72
|
"scripts": {
|
|
73
|
-
"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/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/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/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/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/issue-512-tool-restriction.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/security-resource-boundaries.test.js tests/security-property-fuzz.test.js tests/security-adversarial-fuzz-contract.test.js tests/runtime-i18n.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/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
|
+
"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/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/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/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/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/issue-512-tool-restriction.test.js tests/pi-ai-bridge-wire-compat.test.js tests/fetch-wrapper-lifecycle.test.js tests/fetch-wrapper-composition.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/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/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",
|
|
74
74
|
"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",
|
|
75
75
|
"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",
|
|
76
76
|
"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",
|
|
77
77
|
"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",
|
|
78
|
-
"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: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/fetch-wrapper-lifecycle.test.js tests/fetch-wrapper-composition.test.js tests/native-image-coexistence.test.js tests/tesseract-node24-boot.test.js tests/dsh-host-capabilities.test.js",
|
|
79
79
|
"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",
|
|
80
80
|
"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/security-adversarial-fuzz-contract.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",
|
|
81
81
|
"test:grounding": "node --test tests/grounding-coordinate-frame.test.js tests/grounding-coordinate-runtime.test.js tests/vision-capability-grounding-proof-contract.test.js",
|