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.
Files changed (132) hide show
  1. package/README.md +16 -7
  2. package/README.zh.md +16 -7
  3. package/docs/architecture/2.3.0-compat-retirement.md +147 -0
  4. package/docs/architecture/2.3.0-execution-plan.md +410 -0
  5. package/docs/architecture/2.3.0-final-audit.md +279 -0
  6. package/docs/architecture/2.3.0-reopened-plan.md +720 -0
  7. package/docs/architecture/3.0-architecture-plan.md +252 -0
  8. package/docs/architecture/3.0-r0-contract-ledger.md +137 -0
  9. package/docs/architecture/3.0-r1-source-artifact-boundary.md +98 -0
  10. package/docs/architecture/3.0-r10-final-correctness-closure.md +207 -0
  11. package/docs/architecture/3.0-r11-adversarial-review.md +441 -0
  12. package/docs/architecture/3.0-r5-browser-go-no-go.md +165 -0
  13. package/docs/architecture/3.0-r5-browser-typescript-gate.md +105 -0
  14. package/docs/architecture/3.0-r6-compat-retirement.md +203 -0
  15. package/docs/architecture/3.0-r7-public-api.md +151 -0
  16. package/docs/architecture/3.0-r8-ci-release.md +248 -0
  17. package/docs/architecture/3.0-r9-final-audit.md +370 -0
  18. package/docs/architecture/compat-inventory.md +26 -34
  19. package/docs/architecture/dsh-compatibility-matrix.md +65 -54
  20. package/docs/architecture/dsh-support-window.md +30 -22
  21. package/docs/architecture/final-closure-audit.md +2 -0
  22. package/docs/releases/v2.3.0.md +53 -0
  23. package/docs/releases/v3.0.0.md +85 -0
  24. package/entry.js +9 -57
  25. package/index.js +268 -761
  26. package/lib/agent-request-route-authority.js +1 -20
  27. package/lib/catalog-corrections.js +1 -2
  28. package/lib/client-maintenance-actions.js +145 -0
  29. package/lib/client-presentation-boundary-main.js +75 -17
  30. package/lib/client-presentation-boundary.js +53 -28
  31. package/lib/client.js +246 -144
  32. package/lib/core-apply-lifecycle.js +41 -0
  33. package/lib/core-cache.js +87 -0
  34. package/lib/core-primitives.js +91 -278
  35. package/lib/core-vision-surface-domain.js +63 -0
  36. package/lib/core-vision-surface.js +11 -77
  37. package/lib/delegated-call-config.js +15 -24
  38. package/lib/desktop-screenshot-tool.js +262 -0
  39. package/lib/doctor-cli.js +0 -0
  40. package/lib/doctor-runtime.js +4 -1
  41. package/lib/dsh-contract-compat.js +37 -256
  42. package/lib/dsh-host-capabilities.js +3 -9
  43. package/lib/dsh-settings-017-compat.js +17 -10
  44. package/lib/dsh-support-window.js +3 -3
  45. package/lib/file-logger.js +8 -3
  46. package/lib/grounding-coordinate-runtime.js +1 -2
  47. package/lib/http-body-limit.js +97 -79
  48. package/lib/http-compat.js +1 -2
  49. package/lib/image-offload-compat.js +30 -21
  50. package/lib/image-resource-governor.js +210 -228
  51. package/lib/legacy-global-proxy-boundary.js +46 -3
  52. package/lib/live-model-client-prelude.js +12 -4
  53. package/lib/live-model-discovery.js +134 -43
  54. package/lib/local-remote-settings-permission.js +33 -7
  55. package/lib/local-vision-stabilizer.js +31 -17
  56. package/lib/maintenance-routes.js +134 -0
  57. package/lib/native-image-coexistence.js +15 -3
  58. package/lib/ollama-cold-start.js +87 -23
  59. package/lib/pi-ai-bridge-wire-compat.js +3 -2
  60. package/lib/proxy-dispatcher-pool.js +151 -92
  61. package/lib/proxy-routing.js +75 -51
  62. package/lib/proxy-url-compat.js +3 -10
  63. package/lib/public-config.js +36 -0
  64. package/lib/public-entry.d.ts +148 -0
  65. package/lib/public-entry.js +19 -11
  66. package/lib/remote-settings-bridge.js +11 -0
  67. package/lib/remote-settings-risk-client-boundary.js +155 -0
  68. package/lib/remote-settings-risk-confirmation.js +17 -105
  69. package/lib/replay-delegation.js +12 -7
  70. package/lib/replay-envelope-v2-compat.js +12 -7
  71. package/lib/runtime-composition.js +127 -34
  72. package/lib/runtime-i18n-boundary.js +10 -11
  73. package/lib/screenshot-source-boundary.js +2 -2
  74. package/lib/session-affinity-runtime.js +113 -97
  75. package/lib/session-affinity.js +89 -76
  76. package/lib/session-event-history-compat.js +30 -0
  77. package/lib/session-message-source-compat.js +7 -3
  78. package/lib/session-query-readers.js +263 -0
  79. package/lib/session-surface-compat.js +30 -31
  80. package/lib/session-surface-policy-domain.js +85 -0
  81. package/lib/session-surface-policy.js +2 -111
  82. package/lib/session-turn-resolver.js +2 -6
  83. package/lib/session-vision-attachment-index.js +143 -0
  84. package/lib/session-vision-delegation-boundary.js +34 -0
  85. package/lib/session-vision-delegation.js +22 -0
  86. package/lib/session-vision-index.js +179 -116
  87. package/lib/session-vision-mode-authority.js +229 -192
  88. package/lib/session-vision-mode-boundary.js +65 -22
  89. package/lib/session-vision-policy-domain.js +304 -0
  90. package/lib/session-vision-policy.js +104 -0
  91. package/lib/session-vision-state.js +368 -378
  92. package/lib/settings-client-017-compat.js +9 -7
  93. package/lib/settings-client-loader-lifecycle.js +129 -0
  94. package/lib/sharp-runtime.js +0 -7
  95. package/lib/structured-flow-hardening.js +25 -6
  96. package/lib/tesseract-exec-compat.js +9 -1
  97. package/lib/turn-budget-context.js +47 -41
  98. package/lib/update-check.js +11 -2
  99. package/lib/vision-attachment-handle-runtime.js +176 -156
  100. package/lib/vision-backend-runtime-policy.js +19 -2
  101. package/lib/vision-background-benchmark.js +41 -4
  102. package/lib/vision-background-failure-policy.js +41 -41
  103. package/lib/vision-background-stop-store-core.js +217 -0
  104. package/lib/vision-background-stop-store.js +8 -179
  105. package/lib/vision-capability-benchmark-service.js +1 -0
  106. package/lib/vision-capability-benchmark.js +11 -61
  107. package/lib/vision-capability-evidence.js +35 -0
  108. package/lib/vision-capability-fingerprint.js +72 -0
  109. package/lib/vision-capability-identity.js +116 -61
  110. package/lib/vision-capability-router.js +13 -33
  111. package/lib/vision-chunk-assembler.js +95 -0
  112. package/lib/vision-diagnostics-routes.js +246 -0
  113. package/lib/vision-evidence-guidance.js +3 -1
  114. package/lib/vision-execution-order-apply.js +45 -55
  115. package/lib/vision-execution-order.js +19 -19
  116. package/lib/vision-execution-policy.js +12 -7
  117. package/lib/vision-model-registry.js +30 -17
  118. package/lib/vision-provider-transport.js +137 -176
  119. package/lib/vision-routing-authority.js +47 -48
  120. package/lib/vision-routing-product.js +26 -29
  121. package/lib/vision-routing-settings-prelude.js +8 -3
  122. package/lib/vision-runtime-performance-store.js +166 -0
  123. package/lib/vision-runtime-performance.js +13 -142
  124. package/lib/vision-toggle-root-hardening.js +22 -5
  125. package/lib/vision-tool-runtime-boundary.js +104 -31
  126. package/lib/web/dsh-settings-017-root-transport.js +27 -12
  127. package/lib/web/remote-settings-client.js +6 -9
  128. package/lib/web-capability-boundary.js +17 -7
  129. package/lib/wrapper-directory.js +14 -23
  130. package/package.json +56 -20
  131. package/lib/android-attachment-compat.js +0 -244
  132. package/lib/settings-client-rc8-lifecycle.js +0 -397
@@ -1,117 +1,8 @@
1
1
  import { currentSessionVisionPolicy } from './native-image-coexistence.js'
2
2
  import { currentSessionVisionModeAuthority } from './session-vision-mode-authority.js'
3
+ import { resolveSessionSurfacePolicy } from './session-surface-policy-domain.js'
3
4
 
4
- const OWNERSHIP = Object.freeze({
5
- PLUGIN_OWNED: 'vision-router-owned',
6
- NATIVE: 'native-image',
7
- TEXT_ONLY: 'text-only',
8
- UNKNOWN: 'unknown',
9
- })
10
-
11
- const KNOWN_OWNERSHIP = new Set(Object.values(OWNERSHIP))
12
-
13
- function isObject(value) {
14
- return value !== null && typeof value === 'object' && !Array.isArray(value)
15
- }
16
-
17
- function normalizedOwnership(policy) {
18
- if (!isObject(policy)) return undefined
19
- return KNOWN_OWNERSHIP.has(policy.ownership) ? policy.ownership : OWNERSHIP.UNKNOWN
20
- }
21
-
22
- /**
23
- * Resolve one immutable, read-only snapshot of the Vision Router capability
24
- * surface for the current session.
25
- *
26
- * Image ownership and Vision-mode authority are intentionally distinct. The
27
- * native-image policy answers who can consume raw pixels; the mode authority
28
- * answers whether this Session explicitly selected a Vision Router-owned
29
- * wrapper/twin for the current step. Only the latter can grant plugin tools or
30
- * automatic visual work. This prevents a historical image, a native multimodal
31
- * source model, or a stale request header from silently turning Vision back on.
32
- *
33
- * Durable transcript invariant: once a real session policy exists, Core may
34
- * observe/index image blocks but may never replace a user-owned image message
35
- * before the Agent loop appends it to the Session log. Image projection belongs
36
- * at the adapter/request boundary. The historical `rewriteCurrentImages` bit is
37
- * therefore intentionally ignored even when an older policy producer still
38
- * exposes it; the compatibility field below is permanently false.
39
- */
40
- export function resolveSessionSurfacePolicy({
41
- visionPolicy,
42
- visionModeAuthority,
43
- config = {},
44
- schemaBootstrapping = false,
45
- } = {}) {
46
- const source = isObject(visionPolicy) ? visionPolicy : undefined
47
- const authority = isObject(visionModeAuthority) ? visionModeAuthority : undefined
48
- const value = isObject(config) ? config : {}
49
- const ownership = normalizedOwnership(source)
50
- const pluginOwned = ownership === OWNERSHIP.PLUGIN_OWNED
51
-
52
- // During schema bootstrap there is no real Session policy, so global Settings
53
- // still decide which definitions Core registers. During a real step, an
54
- // explicit authority snapshot wins; direct helper callers fall back to the
55
- // already-classified plugin ownership instead of inventing an enabled mode.
56
- const visionModeEnabled = authority !== undefined
57
- ? authority.enabled === true
58
- : source === undefined
59
- ? true
60
- : pluginOwned
61
-
62
- // No active session policy means no session-specific preservation authority.
63
- // During a real pre-step, every ownership result — including explicit
64
- // text-only and unknown — preserves the durable user message unchanged.
65
- const preserveRawImages = source !== undefined
66
- const rewriteCurrentImages = false
67
- const allowStructuredBootstrap = visionModeEnabled
68
- const allowGenericAutoMount = visionModeEnabled
69
-
70
- const surface = Object.freeze({
71
- preserveRawImages,
72
- rewriteCurrentImages,
73
- visionTools: value.tool !== false && visionModeEnabled,
74
- structuredBootstrap:
75
- value.structuredVisionBootstrap === true && visionModeEnabled,
76
- genericAutoMount:
77
- value.autoActivateOnImage !== false && visionModeEnabled,
78
- instantDescribe:
79
- value.instantDescribe !== false && visionModeEnabled,
80
- })
81
-
82
- const overrides = {}
83
- if (schemaBootstrapping === true && source === undefined && value.tool === false) {
84
- overrides.tool = true
85
- }
86
- // Core's historical rewriteImages switch controls an agent/pre-step message
87
- // transform. Disable it for every real session policy so neither current nor
88
- // historical user image blocks can be persisted as internal attachment text.
89
- // Vision Router-owned adapters still perform their private request projection.
90
- if (source !== undefined && value.rewriteImages !== false) overrides.rewriteImages = false
91
-
92
- // The composer/model selection is the Session authority. When it is OFF,
93
- // suppress every automatic/plugin-owned visual surface while preserving the
94
- // durable image itself. Settings remain unchanged; these are turn-local Core
95
- // projections only.
96
- if (source !== undefined && !visionModeEnabled) {
97
- if (value.tool !== false) overrides.tool = false
98
- if (value.instantDescribe !== false) overrides.instantDescribe = false
99
- if (value.autoActivateOnImage !== false) overrides.autoActivateOnImage = false
100
- if (value.structuredVisionBootstrap !== false) overrides.structuredVisionBootstrap = false
101
- }
102
-
103
- return Object.freeze({
104
- ownership,
105
- visionModeEnabled,
106
- participates: source !== undefined,
107
- preserveRawImages,
108
- rewriteCurrentImages,
109
- allowStructuredBootstrap,
110
- allowGenericAutoMount,
111
- surface,
112
- legacyConfigOverrides: Object.freeze(overrides),
113
- })
114
- }
5
+ export { resolveSessionSurfacePolicy } from './session-surface-policy-domain.js'
115
6
 
116
7
  /** Read the turn-local ownership + mode snapshots and resolve the Core surface. */
117
8
  export function currentSessionSurfacePolicy(config = {}, options = {}) {
@@ -1,10 +1,6 @@
1
1
  function projectionServiceOf(ctx) {
2
- try {
3
- const service = ctx?.sessionProjections
4
- if (service && typeof service.stateOf === 'function') return service
5
- } catch {
6
- // Some compatibility contexts expose services only through get().
7
- }
2
+ // sessionProjections is optional here; probing the Cordis service property
3
+ // outside inject() would turn this reader into a hard pending dependency.
8
4
  try {
9
5
  const service = ctx?.get?.('sessionProjections')
10
6
  return service && typeof service.stateOf === 'function' ? service : undefined
@@ -0,0 +1,143 @@
1
+ import { legacySessionEvents } from './session-event-history-compat.js';
2
+ function propertyBag(value) {
3
+ return value !== null && (typeof value === 'object' || typeof value === 'function')
4
+ ? value
5
+ : undefined;
6
+ }
7
+ function weakKey(value) {
8
+ return value !== null && (typeof value === 'object' || typeof value === 'function')
9
+ ? value
10
+ : undefined;
11
+ }
12
+ function attachmentRef(value) {
13
+ return propertyBag(value) ? value : undefined;
14
+ }
15
+ function attachmentIdOf(ref) {
16
+ const record = propertyBag(ref);
17
+ return String(record?.attachmentId ?? record?.id);
18
+ }
19
+ function iterableValues(value) {
20
+ const iterator = propertyBag(value)?.[Symbol.iterator];
21
+ if (typeof iterator !== 'function') {
22
+ throw new TypeError('Session attachment collector returned a non-iterable result');
23
+ }
24
+ return value;
25
+ }
26
+ function boundedMessage(error) {
27
+ const record = propertyBag(error);
28
+ const text = record?.message ?? error;
29
+ return String(text ?? '').slice(0, 400);
30
+ }
31
+ /**
32
+ * Attachment access boundary for one SessionVisionStateStore.
33
+ *
34
+ * The state store remains the sole bounded storage owner. This boundary owns
35
+ * only cache access plus target-only cold recovery from the durable Session
36
+ * event source. Synchronous lookup never performs historical I/O.
37
+ */
38
+ export function createSessionVisionAttachmentIndex({ stateStore, collectEventAttachmentRefs, readSessionLog, logger, }) {
39
+ const runtimeStore = stateStore;
40
+ const lookup = runtimeStore.lookupAttachment;
41
+ const primitiveLookup = typeof lookup === 'function'
42
+ ? (session, attachmentId) => lookup.call(stateStore, session, attachmentId)
43
+ : undefined;
44
+ const attachmentRecoveryWarnings = new WeakMap();
45
+ const recordAttachments = (session, refs) => {
46
+ if (!session || !Array.isArray(refs) || refs.length === 0)
47
+ return;
48
+ stateStore.recordAttachments(session, refs);
49
+ };
50
+ const lookupAttachment = (session, attachmentId) => {
51
+ if (primitiveLookup === undefined)
52
+ return undefined;
53
+ return primitiveLookup(session, attachmentId);
54
+ };
55
+ const recoverAttachmentsFromEvents = (session, ids, events) => {
56
+ const found = new Map();
57
+ if (!Array.isArray(events)
58
+ || events.length === 0
59
+ || typeof collectEventAttachmentRefs !== 'function') {
60
+ return found;
61
+ }
62
+ const wanted = new Set(ids);
63
+ const collected = collectEventAttachmentRefs(events);
64
+ for (const value of iterableValues(collected)) {
65
+ if (!value)
66
+ continue;
67
+ const ref = attachmentRef(value);
68
+ if (ref === undefined)
69
+ continue;
70
+ const id = attachmentIdOf(ref);
71
+ if (wanted.has(id) && !found.has(id))
72
+ found.set(id, ref);
73
+ }
74
+ if (found.size > 0)
75
+ stateStore.recordAttachments(session, [...found.values()]);
76
+ return found;
77
+ };
78
+ const warnAttachmentRecoveryFailure = (session, attachmentId, error) => {
79
+ const message = boundedMessage(error);
80
+ const warningKey = `${String(attachmentId)}:${message}`;
81
+ const key = weakKey(session);
82
+ if (key !== undefined && attachmentRecoveryWarnings.get(key) === warningKey)
83
+ return;
84
+ if (key !== undefined)
85
+ attachmentRecoveryWarnings.set(key, warningKey);
86
+ logger?.warn?.('vision-router: session attachment recovery failed id=%s error=%s', String(attachmentId), message);
87
+ };
88
+ const resolveAttachments = async (session, attachmentIds) => {
89
+ const requested = [
90
+ ...new Set((Array.isArray(attachmentIds) ? attachmentIds : []).map((id) => String(id))),
91
+ ];
92
+ const resolved = new Map();
93
+ const missing = [];
94
+ for (const id of requested) {
95
+ const cached = lookupAttachment(session, id);
96
+ if (cached !== undefined)
97
+ resolved.set(id, cached);
98
+ else
99
+ missing.push(id);
100
+ }
101
+ if (missing.length === 0 || session === undefined)
102
+ return resolved;
103
+ let events;
104
+ if (typeof readSessionLog === 'function') {
105
+ let result;
106
+ try {
107
+ result = await readSessionLog(session);
108
+ }
109
+ catch (error) {
110
+ warnAttachmentRecoveryFailure(session, missing[0], error);
111
+ return resolved;
112
+ }
113
+ const resultRecord = propertyBag(result);
114
+ if (resultRecord?.supported === true) {
115
+ const key = weakKey(session);
116
+ if (key !== undefined)
117
+ attachmentRecoveryWarnings.delete(key);
118
+ events = resultRecord.events;
119
+ }
120
+ else if (resultRecord?.supported !== false) {
121
+ warnAttachmentRecoveryFailure(session, missing[0], new Error('Session log reader returned an invalid capability result'));
122
+ return resolved;
123
+ }
124
+ }
125
+ // Explicitly unsupported SessionQuery keeps the released Session-local
126
+ // compatibility fallback. Advertised reader failures above never fall back.
127
+ if (events === undefined)
128
+ events = legacySessionEvents(session);
129
+ const recovered = recoverAttachmentsFromEvents(session, missing, events);
130
+ for (const [id, ref] of recovered)
131
+ resolved.set(id, ref);
132
+ return resolved;
133
+ };
134
+ const resolveAttachment = async (session, attachmentId) => {
135
+ return (await resolveAttachments(session, [attachmentId])).get(String(attachmentId));
136
+ };
137
+ return Object.freeze({
138
+ recordAttachments,
139
+ lookupAttachment,
140
+ resolveAttachment,
141
+ resolveAttachments,
142
+ });
143
+ }
@@ -0,0 +1,34 @@
1
+ import { materializeDelegatedSessionVisionPolicy, } from './session-vision-delegation.js';
2
+ /**
3
+ * Materialize inherited DVR authority for exact runtime-owned child Agents.
4
+ *
5
+ * DSH awaits serial `agent/created` listeners before releasing queued work,
6
+ * so the snapshot is durable before the child can assemble its first prompt.
7
+ * Runtime ownership is revalidated with `agents.isOwnedBy`; ambient initiator
8
+ * presence alone is never treated as delegation authority.
9
+ */
10
+ export function installSessionVisionDelegationBoundary(ctx, store, resolveParentAuthority) {
11
+ const currentInitiator = ctx.agents?.currentInitiator;
12
+ const isOwnedBy = ctx.agents?.isOwnedBy;
13
+ const on = ctx.on;
14
+ if (typeof currentInitiator !== 'function'
15
+ || typeof isOwnedBy !== 'function'
16
+ || typeof on !== 'function') {
17
+ return false;
18
+ }
19
+ on.call(ctx, 'agent/created', async ({ agent: child }) => {
20
+ if (typeof store.hydrate === 'function') {
21
+ await store.hydrate(child.id);
22
+ }
23
+ const parent = currentInitiator.call(ctx.agents);
24
+ if (parent === undefined || parent.id === child.id)
25
+ return;
26
+ if (!isOwnedBy.call(ctx.agents, child.id, parent))
27
+ return;
28
+ if (typeof store.hydrate === 'function') {
29
+ await store.hydrate(parent.id);
30
+ }
31
+ await materializeDelegatedSessionVisionPolicy(store, parent, child, resolveParentAuthority);
32
+ });
33
+ return true;
34
+ }
@@ -0,0 +1,22 @@
1
+ import { delegatedSessionVisionPolicy, } from './session-vision-policy.js';
2
+ /**
3
+ * Materialize one child-local DVR policy snapshot at the delegation boundary.
4
+ *
5
+ * Child-local durable truth wins when it already exists. Otherwise the direct
6
+ * parent's durable policy wins over route-derived compatibility authority, and
7
+ * the resulting enabled bit is copied into the child as a delegation snapshot.
8
+ * Later parent changes therefore cannot retroactively mutate an existing child.
9
+ */
10
+ export async function materializeDelegatedSessionVisionPolicy(store, parent, child, resolveParentAuthority) {
11
+ if (parent.id === child.id) {
12
+ throw new TypeError('Session Vision delegation requires distinct parent and child ids');
13
+ }
14
+ const existing = store.get(child.id);
15
+ if (existing !== undefined) {
16
+ return Object.freeze({ status: 'existing', policy: existing });
17
+ }
18
+ const parentPolicy = store.get(parent.id);
19
+ const enabled = parentPolicy?.enabled ?? resolveParentAuthority(parent).enabled;
20
+ const policy = await store.set(child.id, delegatedSessionVisionPolicy(enabled, parent.id));
21
+ return Object.freeze({ status: 'materialized', policy });
22
+ }