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
|
@@ -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
|
-
|
|
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
|
-
|
|
3
|
-
|
|
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
|
+
}
|