dsh-vision-router 2.2.8 → 3.0.0
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 +16 -7
- package/README.zh.md +16 -7
- package/docs/architecture/2.3.0-compat-retirement.md +147 -0
- package/docs/architecture/2.3.0-execution-plan.md +410 -0
- package/docs/architecture/2.3.0-final-audit.md +279 -0
- package/docs/architecture/2.3.0-reopened-plan.md +720 -0
- package/docs/architecture/3.0-architecture-plan.md +252 -0
- package/docs/architecture/3.0-r0-contract-ledger.md +137 -0
- package/docs/architecture/3.0-r1-source-artifact-boundary.md +98 -0
- package/docs/architecture/3.0-r10-final-correctness-closure.md +207 -0
- package/docs/architecture/3.0-r11-adversarial-review.md +441 -0
- package/docs/architecture/3.0-r5-browser-go-no-go.md +165 -0
- package/docs/architecture/3.0-r5-browser-typescript-gate.md +105 -0
- package/docs/architecture/3.0-r6-compat-retirement.md +203 -0
- package/docs/architecture/3.0-r7-public-api.md +151 -0
- package/docs/architecture/3.0-r8-ci-release.md +248 -0
- package/docs/architecture/3.0-r9-final-audit.md +370 -0
- package/docs/architecture/compat-inventory.md +26 -34
- package/docs/architecture/dsh-compatibility-matrix.md +65 -54
- package/docs/architecture/dsh-support-window.md +30 -22
- package/docs/architecture/final-closure-audit.md +2 -0
- package/docs/releases/v2.3.0.md +53 -0
- package/docs/releases/v3.0.0.md +85 -0
- package/entry.js +9 -57
- package/index.js +268 -761
- package/lib/agent-request-route-authority.js +1 -20
- package/lib/catalog-corrections.js +1 -2
- package/lib/client-maintenance-actions.js +145 -0
- package/lib/client-presentation-boundary-main.js +75 -17
- package/lib/client-presentation-boundary.js +53 -28
- package/lib/client.js +246 -144
- package/lib/core-apply-lifecycle.js +41 -0
- package/lib/core-cache.js +87 -0
- package/lib/core-primitives.js +91 -278
- package/lib/core-vision-surface-domain.js +63 -0
- package/lib/core-vision-surface.js +11 -77
- package/lib/delegated-call-config.js +15 -24
- package/lib/desktop-screenshot-tool.js +262 -0
- package/lib/doctor-cli.js +0 -0
- package/lib/doctor-runtime.js +4 -1
- package/lib/dsh-contract-compat.js +37 -256
- package/lib/dsh-host-capabilities.js +3 -9
- package/lib/dsh-settings-017-compat.js +17 -10
- package/lib/dsh-support-window.js +3 -3
- package/lib/file-logger.js +8 -3
- package/lib/grounding-coordinate-runtime.js +1 -2
- package/lib/http-body-limit.js +97 -79
- package/lib/http-compat.js +1 -2
- package/lib/image-offload-compat.js +30 -21
- package/lib/image-resource-governor.js +210 -228
- package/lib/legacy-global-proxy-boundary.js +46 -3
- package/lib/live-model-client-prelude.js +12 -4
- package/lib/live-model-discovery.js +134 -43
- package/lib/local-remote-settings-permission.js +33 -7
- package/lib/local-vision-stabilizer.js +31 -17
- package/lib/maintenance-routes.js +134 -0
- package/lib/native-image-coexistence.js +15 -3
- package/lib/ollama-cold-start.js +87 -23
- package/lib/pi-ai-bridge-wire-compat.js +3 -2
- package/lib/proxy-dispatcher-pool.js +151 -92
- package/lib/proxy-routing.js +75 -51
- package/lib/proxy-url-compat.js +3 -10
- package/lib/public-config.js +36 -0
- package/lib/public-entry.d.ts +148 -0
- package/lib/public-entry.js +19 -11
- package/lib/remote-settings-bridge.js +11 -0
- package/lib/remote-settings-risk-client-boundary.js +155 -0
- package/lib/remote-settings-risk-confirmation.js +17 -105
- package/lib/replay-delegation.js +12 -7
- package/lib/replay-envelope-v2-compat.js +12 -7
- package/lib/runtime-composition.js +127 -34
- package/lib/runtime-i18n-boundary.js +10 -11
- package/lib/screenshot-source-boundary.js +2 -2
- package/lib/session-affinity-runtime.js +113 -97
- package/lib/session-affinity.js +89 -76
- package/lib/session-event-history-compat.js +30 -0
- package/lib/session-message-source-compat.js +7 -3
- package/lib/session-query-readers.js +263 -0
- package/lib/session-surface-compat.js +30 -31
- package/lib/session-surface-policy-domain.js +85 -0
- package/lib/session-surface-policy.js +2 -111
- package/lib/session-turn-resolver.js +2 -6
- package/lib/session-vision-attachment-index.js +143 -0
- package/lib/session-vision-delegation-boundary.js +34 -0
- package/lib/session-vision-delegation.js +22 -0
- package/lib/session-vision-index.js +179 -116
- package/lib/session-vision-mode-authority.js +229 -192
- package/lib/session-vision-mode-boundary.js +65 -22
- package/lib/session-vision-policy-domain.js +304 -0
- package/lib/session-vision-policy.js +104 -0
- package/lib/session-vision-state.js +368 -378
- package/lib/settings-client-017-compat.js +9 -7
- package/lib/settings-client-loader-lifecycle.js +129 -0
- package/lib/sharp-runtime.js +0 -7
- package/lib/structured-flow-hardening.js +25 -6
- package/lib/tesseract-exec-compat.js +9 -1
- package/lib/turn-budget-context.js +47 -41
- package/lib/update-check.js +11 -2
- package/lib/vision-attachment-handle-runtime.js +176 -156
- package/lib/vision-backend-runtime-policy.js +19 -2
- package/lib/vision-background-benchmark.js +41 -4
- package/lib/vision-background-failure-policy.js +41 -41
- package/lib/vision-background-stop-store-core.js +217 -0
- package/lib/vision-background-stop-store.js +8 -179
- package/lib/vision-capability-benchmark-service.js +1 -0
- package/lib/vision-capability-benchmark.js +11 -61
- package/lib/vision-capability-evidence.js +35 -0
- package/lib/vision-capability-fingerprint.js +72 -0
- package/lib/vision-capability-identity.js +116 -61
- package/lib/vision-capability-router.js +13 -33
- package/lib/vision-chunk-assembler.js +95 -0
- package/lib/vision-diagnostics-routes.js +246 -0
- package/lib/vision-evidence-guidance.js +3 -1
- package/lib/vision-execution-order-apply.js +45 -55
- package/lib/vision-execution-order.js +19 -19
- package/lib/vision-execution-policy.js +12 -7
- package/lib/vision-model-registry.js +30 -17
- package/lib/vision-provider-transport.js +137 -176
- package/lib/vision-routing-authority.js +47 -48
- package/lib/vision-routing-product.js +26 -29
- package/lib/vision-routing-settings-prelude.js +8 -3
- package/lib/vision-runtime-performance-store.js +166 -0
- package/lib/vision-runtime-performance.js +13 -142
- package/lib/vision-toggle-root-hardening.js +22 -5
- package/lib/vision-tool-runtime-boundary.js +104 -31
- package/lib/web/dsh-settings-017-root-transport.js +27 -12
- package/lib/web/remote-settings-client.js +6 -9
- package/lib/web-capability-boundary.js +17 -7
- package/lib/wrapper-directory.js +14 -23
- package/package.json +56 -20
- package/lib/android-attachment-compat.js +0 -244
- package/lib/settings-client-rc8-lifecycle.js +0 -397
|
@@ -14,8 +14,12 @@ import { contextWithNativeImageCoexistence } from './native-image-coexistence.js
|
|
|
14
14
|
import { createCoreVisionSurfaceRuntime } from './core-vision-surface.js'
|
|
15
15
|
import { installSessionVisionIndexBoundary } from './session-vision-index.js'
|
|
16
16
|
import { createSessionVisionRuntime } from './session-vision-runtime.js'
|
|
17
|
+
import { runCoreApplyLifecycle } from './core-apply-lifecycle.js'
|
|
17
18
|
import { installLegacyCoreVisionPolicyBridge } from './legacy-core-vision-policy-bridge.js'
|
|
18
19
|
import { installSessionVisionModeBoundary } from './session-vision-mode-boundary.js'
|
|
20
|
+
import { resolveSessionVisionModeAuthority } from './session-vision-mode-authority.js'
|
|
21
|
+
import { createSessionVisionPolicyRuntimeStore } from './session-vision-policy-domain.js'
|
|
22
|
+
import { installSessionVisionDelegationBoundary } from './session-vision-delegation-boundary.js'
|
|
19
23
|
import { installPiAiBridgeWireCompat } from './pi-ai-bridge-wire-compat.js'
|
|
20
24
|
import { installLiveModelDiscovery } from './live-model-discovery.js'
|
|
21
25
|
import { installVisionModelRegistry } from './vision-model-registry.js'
|
|
@@ -23,14 +27,12 @@ import { installAdversarialHardening } from './adversarial-hardening.js'
|
|
|
23
27
|
import { installOllamaColdStartGuard } from './ollama-cold-start.js'
|
|
24
28
|
import { installLocalVisionStabilizer } from './local-vision-stabilizer.js'
|
|
25
29
|
import { installWrapperDirectoryAlias } from './wrapper-directory.js'
|
|
26
|
-
import { installAndroidAttachmentCompat } from './android-attachment-compat.js'
|
|
27
30
|
import { contextWithCoalescedAdapterUpdates } from './adapter-update-coalescer.js'
|
|
28
31
|
import { installTesseractExecFileCompat } from './tesseract-exec-compat.js'
|
|
29
32
|
import { installLocalMutationRouteBoundary } from './web-capability-boundary.js'
|
|
30
33
|
import { installScreenshotSourceBoundary } from './screenshot-source-boundary.js'
|
|
31
34
|
import { installVisionToolRuntimeBoundary } from './vision-tool-runtime-boundary.js'
|
|
32
35
|
import {
|
|
33
|
-
configureAgentRequestRouteAuthority,
|
|
34
36
|
contextWithAgentRequestRouteAuthority,
|
|
35
37
|
} from './agent-request-route-authority.js'
|
|
36
38
|
import { contextWithGroundingCoordinateFrame } from './grounding-coordinate-runtime.js'
|
|
@@ -57,7 +59,7 @@ import {
|
|
|
57
59
|
} from './structured-flow-hardening.js'
|
|
58
60
|
import { installVisionLimitDiagnostics } from './vision-limit-diagnostics.js'
|
|
59
61
|
import {
|
|
60
|
-
|
|
62
|
+
installDshHostCapabilityDiagnostics,
|
|
61
63
|
createSessionEventReader,
|
|
62
64
|
createSessionEventTailReader,
|
|
63
65
|
createSessionLogReader,
|
|
@@ -73,6 +75,7 @@ import {
|
|
|
73
75
|
} from './web/index.js'
|
|
74
76
|
import { createRuntimeI18nCoreFacade } from './runtime-i18n-core.js'
|
|
75
77
|
import { createRuntimeI18nCoreScope } from './runtime-i18n-core-scope.js'
|
|
78
|
+
import { installVisionMaintenanceRoutes } from './maintenance-routes.js'
|
|
76
79
|
|
|
77
80
|
function liveVisionSettings(ctx, fallback) {
|
|
78
81
|
try {
|
|
@@ -95,7 +98,7 @@ function legacyCoreVisionSettings(ctx, fallback) {
|
|
|
95
98
|
return { ...current, autoActivateOnImage: false }
|
|
96
99
|
}
|
|
97
100
|
|
|
98
|
-
|
|
101
|
+
function installHostAndSecurityBoundaries(ctx, config, core) {
|
|
99
102
|
// SessionVisionIndex calls planGuardStopShadows before Core.apply. Keep that
|
|
100
103
|
// one helper behind the live-locale facade; the final Core itself still gets
|
|
101
104
|
// the ordinary composition identity and is localized by the scoped context.
|
|
@@ -134,7 +137,9 @@ export function applyVisionRuntimeComposition(ctx, config = {}, core) {
|
|
|
134
137
|
config,
|
|
135
138
|
core,
|
|
136
139
|
)
|
|
137
|
-
const ollamaColdStartCtx = installOllamaColdStartGuard(hardenedCtx, hardenedConfig, core
|
|
140
|
+
const ollamaColdStartCtx = installOllamaColdStartGuard(hardenedCtx, hardenedConfig, core, {
|
|
141
|
+
logger: logging.logger,
|
|
142
|
+
})
|
|
138
143
|
const { ctx: stabilizedCtx, bootConfig } = installLocalVisionStabilizer(
|
|
139
144
|
ollamaColdStartCtx,
|
|
140
145
|
hardenedConfig,
|
|
@@ -174,10 +179,29 @@ export function applyVisionRuntimeComposition(ctx, config = {}, core) {
|
|
|
174
179
|
Config: core.Config,
|
|
175
180
|
})
|
|
176
181
|
: ownershipCtx
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
182
|
+
// DVR 2.3 supports DSH from the 0.1.5 train, whose attachment
|
|
183
|
+
// contract already owns batch persistence. The old no-batch Android wrapper
|
|
184
|
+
// is therefore unreachable; keep capability diagnostics as an explicit
|
|
185
|
+
// composition responsibility instead of hiding that side effect in a shim.
|
|
186
|
+
installDshHostCapabilityDiagnostics(settingsCtx)
|
|
187
|
+
const attachmentCompatCtx = settingsCtx
|
|
180
188
|
|
|
189
|
+
return {
|
|
190
|
+
attachmentCompatCtx,
|
|
191
|
+
batchAttachmentHost,
|
|
192
|
+
capabilityStore,
|
|
193
|
+
hardenedConfig,
|
|
194
|
+
hostOwnsOfficialDeepSeek,
|
|
195
|
+
logging,
|
|
196
|
+
runtimeConfig,
|
|
197
|
+
runtimeI18nCore,
|
|
198
|
+
runtimeI18nCoreScope,
|
|
199
|
+
runtimePerformanceStore,
|
|
200
|
+
}
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
function createRuntimeOwners(host, core) {
|
|
204
|
+
const { attachmentCompatCtx, logging, runtimeConfig, runtimeI18nCore } = host
|
|
181
205
|
const toolRuntimeCtx = installVisionToolRuntimeBoundary(attachmentCompatCtx, runtimeConfig)
|
|
182
206
|
const nativeImageCompat = contextWithNativeImageCoexistence(toolRuntimeCtx, runtimeConfig)
|
|
183
207
|
const coreVisionSurfaceRuntime = createCoreVisionSurfaceRuntime({
|
|
@@ -208,11 +232,37 @@ export function applyVisionRuntimeComposition(ctx, config = {}, core) {
|
|
|
208
232
|
nativeImageCompat.config,
|
|
209
233
|
{ rewriteHistoryImages: core.rewriteHistoryImages },
|
|
210
234
|
)
|
|
235
|
+
const sessionVisionPolicyStore = createSessionVisionPolicyRuntimeStore(legacyCoreCompat.ctx)
|
|
236
|
+
legacyCoreCompat.ctx.inject?.(['agents'], (agentsCtx) => {
|
|
237
|
+
installSessionVisionDelegationBoundary(
|
|
238
|
+
agentsCtx,
|
|
239
|
+
sessionVisionPolicyStore,
|
|
240
|
+
(parent) => resolveSessionVisionModeAuthority(
|
|
241
|
+
agentsCtx,
|
|
242
|
+
parent,
|
|
243
|
+
legacyCoreCompat.config,
|
|
244
|
+
{ sessionPolicy: sessionVisionPolicyStore.get(String(parent.id)) },
|
|
245
|
+
),
|
|
246
|
+
)
|
|
247
|
+
})
|
|
211
248
|
const sessionVisionModeCompat = installSessionVisionModeBoundary(
|
|
212
249
|
legacyCoreCompat.ctx,
|
|
213
250
|
legacyCoreCompat.config,
|
|
251
|
+
{ sessionVisionPolicyStore },
|
|
214
252
|
)
|
|
215
253
|
|
|
254
|
+
return {
|
|
255
|
+
coreVisionSurfaceRuntime,
|
|
256
|
+
nativeImageCompat,
|
|
257
|
+
sessionVisionModeCompat,
|
|
258
|
+
sessionVisionPolicyStore,
|
|
259
|
+
sessionVisionRuntime,
|
|
260
|
+
}
|
|
261
|
+
}
|
|
262
|
+
|
|
263
|
+
function installRoutingAndHostProducts(host, owners, core) {
|
|
264
|
+
const { capabilityStore, logging, runtimeConfig, runtimePerformanceStore } = host
|
|
265
|
+
const { sessionVisionModeCompat } = owners
|
|
216
266
|
// Final structured-flow guard sits closest to core.apply so it sees the
|
|
217
267
|
// actual tool registrations and pre-step listener. The diagnostic observer
|
|
218
268
|
// remains immediately inside it so existing timeout/budget semantics stay
|
|
@@ -266,6 +316,19 @@ export function applyVisionRuntimeComposition(ctx, config = {}, core) {
|
|
|
266
316
|
})
|
|
267
317
|
installPiAiBridgeWireCompat(reconciledCtx, logging.logger)
|
|
268
318
|
|
|
319
|
+
return {
|
|
320
|
+
breakerShadowHealth,
|
|
321
|
+
liveDiscovery,
|
|
322
|
+
reconciledCtx,
|
|
323
|
+
sessionEventTailReader,
|
|
324
|
+
sessionTurnResolver,
|
|
325
|
+
}
|
|
326
|
+
}
|
|
327
|
+
|
|
328
|
+
function installExecutionBoundaries(host, owners, routing, core, providerTransport) {
|
|
329
|
+
const { capabilityStore, logging, runtimeConfig, runtimeI18nCoreScope, runtimePerformanceStore } = host
|
|
330
|
+
const { sessionVisionRuntime } = owners
|
|
331
|
+
const { breakerShadowHealth, liveDiscovery, reconciledCtx } = routing
|
|
269
332
|
const executionCtx = contextWithVisionExecutionPolicy(reconciledCtx, {
|
|
270
333
|
isBridgeEvidence: (provider, model) => liveDiscovery.hasModel(provider, model),
|
|
271
334
|
evidenceSource: (provider, model) => liveDiscovery.evidenceSource?.(provider, model),
|
|
@@ -314,11 +377,13 @@ export function applyVisionRuntimeComposition(ctx, config = {}, core) {
|
|
|
314
377
|
core,
|
|
315
378
|
evidenceSource: (provider, model) => liveDiscovery.evidenceSource?.(provider, model),
|
|
316
379
|
logger: logging.logger,
|
|
380
|
+
providerTransport,
|
|
317
381
|
finalContextDecorator: runtimeI18nCoreScope.decorate,
|
|
318
382
|
})
|
|
319
383
|
const capabilityBenchmark = installCapabilityBenchmarkService(backendRuntimeCtx, runtimeConfig, core, {
|
|
320
384
|
logger: logging.logger,
|
|
321
385
|
store: capabilityStore,
|
|
386
|
+
providerTransport,
|
|
322
387
|
})
|
|
323
388
|
attachCapabilityBenchmarkPresentation(capabilityBenchmark, {
|
|
324
389
|
ctx: backendRuntimeCtx,
|
|
@@ -331,13 +396,28 @@ export function applyVisionRuntimeComposition(ctx, config = {}, core) {
|
|
|
331
396
|
// Core owns exactly one agent/request routing hook. Protect the completed
|
|
332
397
|
// provider/model handoff at that event boundary so future route-switch logic
|
|
333
398
|
// cannot accidentally carry source-model call defaults into the target.
|
|
334
|
-
|
|
399
|
+
const coreRequestAuthorityCtx = contextWithAgentRequestRouteAuthority(backendRuntimeCtx, {
|
|
335
400
|
wrapperRoute: runtimeConfig.wrapperRoute,
|
|
336
401
|
chainRoute: runtimeConfig.chainRoute,
|
|
337
402
|
logger: logging.logger,
|
|
338
403
|
})
|
|
339
|
-
const coreRequestAuthorityCtx = contextWithAgentRequestRouteAuthority(backendRuntimeCtx)
|
|
340
404
|
|
|
405
|
+
return { coreRequestAuthorityCtx }
|
|
406
|
+
}
|
|
407
|
+
|
|
408
|
+
function applyMatureCoreBridge(host, owners, routing, execution, core, runtime) {
|
|
409
|
+
const {
|
|
410
|
+
attachmentCompatCtx,
|
|
411
|
+
batchAttachmentHost,
|
|
412
|
+
hardenedConfig,
|
|
413
|
+
hostOwnsOfficialDeepSeek,
|
|
414
|
+
logging,
|
|
415
|
+
runtimeConfig,
|
|
416
|
+
runtimeI18nCoreScope,
|
|
417
|
+
} = host
|
|
418
|
+
const { coreVisionSurfaceRuntime, sessionVisionModeCompat, sessionVisionRuntime } = owners
|
|
419
|
+
const { breakerShadowHealth, sessionEventTailReader, sessionTurnResolver } = routing
|
|
420
|
+
const { coreRequestAuthorityCtx } = execution
|
|
341
421
|
try {
|
|
342
422
|
const c = hardenedConfig && typeof hardenedConfig === 'object' ? hardenedConfig : {}
|
|
343
423
|
const local = c.localOllama && typeof c.localOllama === 'object' ? c.localOllama : {}
|
|
@@ -354,8 +434,27 @@ export function applyVisionRuntimeComposition(ctx, config = {}, core) {
|
|
|
354
434
|
/* diagnostics must never break apply */
|
|
355
435
|
}
|
|
356
436
|
|
|
357
|
-
|
|
358
|
-
|
|
437
|
+
let schemaBootstrapFinished = false
|
|
438
|
+
const finishSchemaBootstrapOnce = () => {
|
|
439
|
+
if (schemaBootstrapFinished) return
|
|
440
|
+
schemaBootstrapFinished = true
|
|
441
|
+
coreVisionSurfaceRuntime.finishSchemaBootstrap()
|
|
442
|
+
}
|
|
443
|
+
const installPostCoreRuntime = () => {
|
|
444
|
+
installVisionMaintenanceRoutes(coreRequestAuthorityCtx, { logger: logging.logger })
|
|
445
|
+
finishSchemaBootstrapOnce()
|
|
446
|
+
installWrapperDirectoryAlias(attachmentCompatCtx, runtimeConfig, logging.logger)
|
|
447
|
+
}
|
|
448
|
+
const failCoreApply = (error) => {
|
|
449
|
+
finishSchemaBootstrapOnce()
|
|
450
|
+
logging.logger.error(
|
|
451
|
+
'vision-router: plugin apply failed: %s',
|
|
452
|
+
error && error.stack ? error.stack : error && error.message ? error.message : String(error),
|
|
453
|
+
)
|
|
454
|
+
}
|
|
455
|
+
|
|
456
|
+
return runCoreApplyLifecycle(
|
|
457
|
+
() => runtimeI18nCoreScope.run(() =>
|
|
359
458
|
withVisionCircuitBreakerObserver(
|
|
360
459
|
breakerShadowHealth.capture,
|
|
361
460
|
() => core.apply(
|
|
@@ -367,28 +466,22 @@ export function applyVisionRuntimeComposition(ctx, config = {}, core) {
|
|
|
367
466
|
sessionTurnResolver,
|
|
368
467
|
sessionEventTailReader,
|
|
369
468
|
hostOwnsOfficialDeepSeek,
|
|
469
|
+
providerTransport: runtime?.providerTransport,
|
|
370
470
|
},
|
|
371
471
|
),
|
|
372
472
|
),
|
|
373
|
-
)
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
logging.logger.error(
|
|
389
|
-
'vision-router: plugin apply failed: %s',
|
|
390
|
-
error && error.stack ? error.stack : error && error.message ? error.message : String(error),
|
|
391
|
-
)
|
|
392
|
-
throw error
|
|
393
|
-
}
|
|
394
|
-
}
|
|
473
|
+
),
|
|
474
|
+
{
|
|
475
|
+
onSuccess: installPostCoreRuntime,
|
|
476
|
+
onFailure: failCoreApply,
|
|
477
|
+
},
|
|
478
|
+
)
|
|
479
|
+
}
|
|
480
|
+
|
|
481
|
+
export function applyVisionRuntimeComposition(ctx, config = {}, core, runtime = {}) {
|
|
482
|
+
const host = installHostAndSecurityBoundaries(ctx, config, core)
|
|
483
|
+
const owners = createRuntimeOwners(host, core)
|
|
484
|
+
const routing = installRoutingAndHostProducts(host, owners, core)
|
|
485
|
+
const execution = installExecutionBoundaries(host, owners, routing, core, runtime?.providerTransport)
|
|
486
|
+
return applyMatureCoreBridge(host, owners, routing, execution, core, runtime)
|
|
487
|
+
}
|
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
import { createRuntimeI18n, DEEP_TOOL_MOUNT_STATE } from './runtime-i18n.js'
|
|
2
2
|
import { depthCopyFor } from './depth-guidance.js'
|
|
3
3
|
import { blocksHaveRetainedImage } from './image-offload-compat.js'
|
|
4
|
+
import { captureFetchDelegate, installFetchWrapper } from './fetch-wrapper-lifecycle.js'
|
|
5
|
+
import { visionRouterMessageSource } from './session-message-source-compat.js'
|
|
4
6
|
|
|
5
7
|
const PLUGIN_NAME = 'dsh-vision-router'
|
|
6
8
|
const DEEP_TOOL_NAMES = new Set([
|
|
@@ -432,7 +434,7 @@ function addAutoMountReminder(decision, payload, i18n) {
|
|
|
432
434
|
: `${Date.now()}-${Math.floor(Math.random() * 1e9)}`
|
|
433
435
|
}`,
|
|
434
436
|
content: [{ type: 'text', text: i18n.t('autoMountReminder') }],
|
|
435
|
-
source:
|
|
437
|
+
source: visionRouterMessageSource(payload?.agent?.session),
|
|
436
438
|
}
|
|
437
439
|
const structuredIndex = base.findIndex((message) =>
|
|
438
440
|
typeof message?.id === 'string' && message.id.startsWith('vision-router-structured-'),
|
|
@@ -520,9 +522,9 @@ function localizeRequestBody(value, i18n) {
|
|
|
520
522
|
function installPromptFetchBoundary(ctx, i18n) {
|
|
521
523
|
if (typeof ctx?.effect !== 'function' || typeof globalThis.fetch !== 'function') return
|
|
522
524
|
ctx.effect(() => {
|
|
523
|
-
const
|
|
525
|
+
const delegateFetch = captureFetchDelegate()
|
|
524
526
|
const localizedFetch = (input, init) => {
|
|
525
|
-
if (i18n.language() !== 'en' || typeof init?.body !== 'string') return
|
|
527
|
+
if (i18n.language() !== 'en' || typeof init?.body !== 'string') return delegateFetch(input, init)
|
|
526
528
|
const body = init.body
|
|
527
529
|
if (
|
|
528
530
|
!body.includes('请按以下结构识别这张图片') &&
|
|
@@ -530,21 +532,18 @@ function installPromptFetchBoundary(ctx, i18n) {
|
|
|
530
532
|
!body.includes(OCR_FALLBACK_PROMPT_ZH) &&
|
|
531
533
|
!body.includes(LONG_SCREENSHOT_OCR_PROMPT_ZH)
|
|
532
534
|
) {
|
|
533
|
-
return
|
|
535
|
+
return delegateFetch(input, init)
|
|
534
536
|
}
|
|
535
537
|
try {
|
|
536
538
|
const parsed = JSON.parse(body)
|
|
537
539
|
const localized = localizeRequestBody(parsed, i18n)
|
|
538
|
-
if (localized === parsed) return
|
|
539
|
-
return
|
|
540
|
+
if (localized === parsed) return delegateFetch(input, init)
|
|
541
|
+
return delegateFetch(input, { ...init, body: JSON.stringify(localized) })
|
|
540
542
|
} catch {
|
|
541
|
-
return
|
|
543
|
+
return delegateFetch(input, init)
|
|
542
544
|
}
|
|
543
545
|
}
|
|
544
|
-
|
|
545
|
-
return () => {
|
|
546
|
-
if (globalThis.fetch === localizedFetch) globalThis.fetch = originalFetch
|
|
547
|
-
}
|
|
546
|
+
return installFetchWrapper(localizedFetch)
|
|
548
547
|
}, 'vision-router: runtime i18n prompt boundary')
|
|
549
548
|
}
|
|
550
549
|
|
|
@@ -110,7 +110,7 @@ function wrapTools(tools, ctx, core) {
|
|
|
110
110
|
export function installScreenshotSourceBoundary(ctx, core) {
|
|
111
111
|
if (!ctx || (typeof ctx !== 'object' && typeof ctx !== 'function')) return ctx
|
|
112
112
|
const cached = wrappedContexts.get(ctx)
|
|
113
|
-
if (cached) return cached
|
|
113
|
+
if (cached?.core === core) return cached.wrapped
|
|
114
114
|
let wrapped
|
|
115
115
|
wrapped = new Proxy(ctx, {
|
|
116
116
|
get(target, property) {
|
|
@@ -119,6 +119,6 @@ export function installScreenshotSourceBoundary(ctx, core) {
|
|
|
119
119
|
return typeof value === 'function' ? value.bind(target) : value
|
|
120
120
|
},
|
|
121
121
|
})
|
|
122
|
-
wrappedContexts.set(ctx, wrapped)
|
|
122
|
+
wrappedContexts.set(ctx, { core, wrapped })
|
|
123
123
|
return wrapped
|
|
124
124
|
}
|
|
@@ -1,104 +1,120 @@
|
|
|
1
|
-
import { AsyncLocalStorage } from 'node:async_hooks'
|
|
2
|
-
import { rawSessionIdentity } from './session-affinity.js'
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
1
|
+
import { AsyncLocalStorage } from 'node:async_hooks';
|
|
2
|
+
import { rawSessionIdentity } from './session-affinity.js';
|
|
3
|
+
const affinityRuntime = new AsyncLocalStorage();
|
|
4
|
+
function propertyBag(value) {
|
|
5
|
+
return value !== null && (typeof value === 'object' || typeof value === 'function')
|
|
6
|
+
? value
|
|
7
|
+
: undefined;
|
|
8
|
+
}
|
|
9
|
+
function isPromiseLike(value) {
|
|
10
|
+
return typeof propertyBag(value)?.then === 'function';
|
|
11
|
+
}
|
|
6
12
|
export function currentVisionSessionAffinityId() {
|
|
7
|
-
|
|
8
|
-
|
|
13
|
+
const state = affinityRuntime.getStore();
|
|
14
|
+
return state?.active === true ? state.affinityId : undefined;
|
|
9
15
|
}
|
|
10
|
-
|
|
11
16
|
export function runWithVisionSessionAffinity(affinityId, callback) {
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
17
|
+
const raw = rawSessionIdentity(affinityId);
|
|
18
|
+
if (raw === undefined)
|
|
19
|
+
return callback();
|
|
20
|
+
const state = {
|
|
21
|
+
affinityId: raw,
|
|
22
|
+
active: true,
|
|
23
|
+
};
|
|
24
|
+
let result;
|
|
25
|
+
try {
|
|
26
|
+
result = affinityRuntime.run(state, callback);
|
|
27
|
+
}
|
|
28
|
+
catch (error) {
|
|
29
|
+
state.active = false;
|
|
30
|
+
throw error;
|
|
31
|
+
}
|
|
32
|
+
if (isPromiseLike(result)) {
|
|
33
|
+
return Promise.resolve(result).finally(() => {
|
|
34
|
+
state.active = false;
|
|
35
|
+
});
|
|
36
|
+
}
|
|
37
|
+
state.active = false;
|
|
38
|
+
return result;
|
|
27
39
|
}
|
|
28
|
-
|
|
29
|
-
/**
|
|
30
|
-
* Keep AsyncLocalStorage active across lazy AsyncIterable creation and every
|
|
31
|
-
* iterator operation. Adapter/network work often begins on next(), not when
|
|
32
|
-
* ctx.llm.stream() returns the iterable. Promise-returning test/compat streams
|
|
33
|
-
* are supported too because some older seams resolve the iterable lazily.
|
|
34
|
-
* One mutable state object is retired at stream completion so async work that
|
|
35
|
-
* outlives the model request cannot keep projecting the conversation id.
|
|
36
|
-
*/
|
|
37
40
|
export function streamWithVisionSessionAffinity(affinityId, streamFactory) {
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
const
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
}
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
41
|
+
const raw = rawSessionIdentity(affinityId);
|
|
42
|
+
if (raw === undefined)
|
|
43
|
+
return streamFactory();
|
|
44
|
+
return {
|
|
45
|
+
[Symbol.asyncIterator]() {
|
|
46
|
+
const state = {
|
|
47
|
+
affinityId: raw,
|
|
48
|
+
active: true,
|
|
49
|
+
};
|
|
50
|
+
let iteratorPromise;
|
|
51
|
+
const retire = () => {
|
|
52
|
+
state.active = false;
|
|
53
|
+
};
|
|
54
|
+
const ensure = () => {
|
|
55
|
+
if (iteratorPromise === undefined) {
|
|
56
|
+
iteratorPromise = affinityRuntime.run(state, async () => {
|
|
57
|
+
const stream = await streamFactory();
|
|
58
|
+
if (stream === null
|
|
59
|
+
|| (typeof stream !== 'object' && typeof stream !== 'function')
|
|
60
|
+
|| typeof stream[Symbol.asyncIterator] !== 'function') {
|
|
61
|
+
throw new TypeError('scoped vision stream is not async iterable');
|
|
62
|
+
}
|
|
63
|
+
return stream[Symbol.asyncIterator]();
|
|
64
|
+
});
|
|
65
|
+
}
|
|
66
|
+
return iteratorPromise;
|
|
67
|
+
};
|
|
68
|
+
return {
|
|
69
|
+
next(value) {
|
|
70
|
+
return affinityRuntime.run(state, async () => {
|
|
71
|
+
try {
|
|
72
|
+
const result = await (await ensure()).next(value);
|
|
73
|
+
if (result?.done === true)
|
|
74
|
+
retire();
|
|
75
|
+
return result;
|
|
76
|
+
}
|
|
77
|
+
catch (error) {
|
|
78
|
+
retire();
|
|
79
|
+
throw error;
|
|
80
|
+
}
|
|
81
|
+
});
|
|
82
|
+
},
|
|
83
|
+
return(value) {
|
|
84
|
+
if (iteratorPromise === undefined) {
|
|
85
|
+
retire();
|
|
86
|
+
return Promise.resolve({ done: true, value });
|
|
87
|
+
}
|
|
88
|
+
return affinityRuntime.run(state, async () => {
|
|
89
|
+
try {
|
|
90
|
+
const active = await ensure();
|
|
91
|
+
return typeof active.return === 'function'
|
|
92
|
+
? await active.return(value)
|
|
93
|
+
: { done: true, value };
|
|
94
|
+
}
|
|
95
|
+
finally {
|
|
96
|
+
retire();
|
|
97
|
+
}
|
|
98
|
+
});
|
|
99
|
+
},
|
|
100
|
+
throw(error) {
|
|
101
|
+
if (iteratorPromise === undefined) {
|
|
102
|
+
retire();
|
|
103
|
+
return Promise.reject(error);
|
|
104
|
+
}
|
|
105
|
+
return affinityRuntime.run(state, async () => {
|
|
106
|
+
try {
|
|
107
|
+
const active = await ensure();
|
|
108
|
+
if (typeof active.throw === 'function')
|
|
109
|
+
return await active.throw(error);
|
|
110
|
+
throw error;
|
|
111
|
+
}
|
|
112
|
+
finally {
|
|
113
|
+
retire();
|
|
114
|
+
}
|
|
115
|
+
});
|
|
116
|
+
},
|
|
117
|
+
};
|
|
100
118
|
},
|
|
101
|
-
|
|
102
|
-
},
|
|
103
|
-
}
|
|
119
|
+
};
|
|
104
120
|
}
|