@neuroverseos/governance 0.4.0 → 0.5.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/dist/adapters/index.cjs +66 -1
- package/dist/adapters/index.d.cts +40 -2
- package/dist/adapters/index.d.ts +40 -2
- package/dist/adapters/index.js +1 -1
- package/dist/{build-THUEYMVT.js → build-SCAWPA7E.js} +2 -2
- package/dist/{chunk-APU4OZIP.js → chunk-U6FRAEQJ.js} +66 -1
- package/dist/cli/neuroverse.cjs +66 -1
- package/dist/cli/neuroverse.js +4 -4
- package/dist/{derive-5LOMN7GO.js → derive-AUQE3L3P.js} +2 -2
- package/dist/{doctor-WIO4FLA3.js → doctor-EY7LKSYY.js} +1 -1
- package/dist/index.js +29 -29
- package/dist/{mentraos-YFS7FMJH.js → mentraos-LLH7KEV4.js} +1 -1
- package/dist/spatial/index.cjs +682 -0
- package/dist/spatial/index.d.cts +517 -0
- package/dist/spatial/index.d.ts +517 -0
- package/dist/spatial/index.js +633 -0
- package/dist/worlds/mentraos-spatial.nv-world.md +68 -0
- package/package.json +16 -3
- package/dist/{chunk-QZ666FCV.js → chunk-4G6WHPLI.js} +3 -3
- package/dist/{configure-ai-5MP5DWTT.js → configure-ai-LL3VAPQW.js} +3 -3
|
@@ -0,0 +1,633 @@
|
|
|
1
|
+
import "../chunk-QWGCMQQD.js";
|
|
2
|
+
|
|
3
|
+
// src/spatial/types.ts
|
|
4
|
+
var DEFAULT_ZONE_RULES = {
|
|
5
|
+
camera: "allowed",
|
|
6
|
+
microphone: "allowed",
|
|
7
|
+
aiDataSend: "allowed",
|
|
8
|
+
aiActions: "allowed",
|
|
9
|
+
aiRecommendations: "allowed",
|
|
10
|
+
locationSharing: "allowed",
|
|
11
|
+
aiOverlay: "allowed",
|
|
12
|
+
dataRetention: "allowed",
|
|
13
|
+
commercialAI: "allowed",
|
|
14
|
+
bystanderProtection: "standard"
|
|
15
|
+
};
|
|
16
|
+
var DEFAULT_PARTICIPANT_CONSTRAINTS = {
|
|
17
|
+
cameraMinimum: "allowed",
|
|
18
|
+
microphoneMinimum: "allowed",
|
|
19
|
+
aiDataSendMinimum: "allowed",
|
|
20
|
+
aiRecommendationsMinimum: "allowed",
|
|
21
|
+
dataRetentionMinimum: "allowed",
|
|
22
|
+
bystanderProtectionMinimum: "standard"
|
|
23
|
+
};
|
|
24
|
+
|
|
25
|
+
// src/spatial/engine.ts
|
|
26
|
+
var CAMERA_ORDER = ["allowed", "confirm_each", "blocked"];
|
|
27
|
+
var MIC_ORDER = ["allowed", "confirm_each", "blocked"];
|
|
28
|
+
var AI_DATA_ORDER = ["allowed", "declared_only", "confirm_each", "blocked"];
|
|
29
|
+
var AI_ACTION_ORDER = ["allowed", "confirm_all", "blocked"];
|
|
30
|
+
var AI_REC_ORDER = ["allowed", "non_commercial", "blocked"];
|
|
31
|
+
var LOCATION_ORDER = ["allowed", "confirm_each", "blocked"];
|
|
32
|
+
var OVERLAY_ORDER = ["allowed", "non_obstructive", "blocked"];
|
|
33
|
+
var RETENTION_ORDER = ["allowed", "session_only", "blocked"];
|
|
34
|
+
var COMMERCIAL_ORDER = ["allowed", "blocked"];
|
|
35
|
+
var BYSTANDER_ORDER = ["standard", "elevated", "strict"];
|
|
36
|
+
function mostRestrictive(a, b, order) {
|
|
37
|
+
const idxA = order.indexOf(a);
|
|
38
|
+
const idxB = order.indexOf(b);
|
|
39
|
+
return idxA >= idxB ? a : b;
|
|
40
|
+
}
|
|
41
|
+
function mergeRules(a, b) {
|
|
42
|
+
return {
|
|
43
|
+
camera: mostRestrictive(a.camera, b.camera, CAMERA_ORDER),
|
|
44
|
+
microphone: mostRestrictive(a.microphone, b.microphone, MIC_ORDER),
|
|
45
|
+
aiDataSend: mostRestrictive(a.aiDataSend, b.aiDataSend, AI_DATA_ORDER),
|
|
46
|
+
aiActions: mostRestrictive(a.aiActions, b.aiActions, AI_ACTION_ORDER),
|
|
47
|
+
aiRecommendations: mostRestrictive(a.aiRecommendations, b.aiRecommendations, AI_REC_ORDER),
|
|
48
|
+
locationSharing: mostRestrictive(a.locationSharing, b.locationSharing, LOCATION_ORDER),
|
|
49
|
+
aiOverlay: mostRestrictive(a.aiOverlay, b.aiOverlay, OVERLAY_ORDER),
|
|
50
|
+
dataRetention: mostRestrictive(a.dataRetention, b.dataRetention, RETENTION_ORDER),
|
|
51
|
+
commercialAI: mostRestrictive(a.commercialAI, b.commercialAI, COMMERCIAL_ORDER),
|
|
52
|
+
bystanderProtection: mostRestrictive(a.bystanderProtection, b.bystanderProtection, BYSTANDER_ORDER),
|
|
53
|
+
custom: [...a.custom ?? [], ...b.custom ?? []]
|
|
54
|
+
};
|
|
55
|
+
}
|
|
56
|
+
function constraintsToRules(c) {
|
|
57
|
+
return {
|
|
58
|
+
...DEFAULT_ZONE_RULES,
|
|
59
|
+
camera: c.cameraMinimum,
|
|
60
|
+
microphone: c.microphoneMinimum,
|
|
61
|
+
aiDataSend: c.aiDataSendMinimum,
|
|
62
|
+
aiRecommendations: c.aiRecommendationsMinimum,
|
|
63
|
+
dataRetention: c.dataRetentionMinimum,
|
|
64
|
+
bystanderProtection: c.bystanderProtectionMinimum
|
|
65
|
+
};
|
|
66
|
+
}
|
|
67
|
+
function createOptIn(zone, userOverrides = {}, method = "tap_confirm") {
|
|
68
|
+
const sessionId = `spatial-${zone.zoneId}-${Date.now()}`;
|
|
69
|
+
const overrideRules = {
|
|
70
|
+
...DEFAULT_ZONE_RULES,
|
|
71
|
+
...userOverrides
|
|
72
|
+
};
|
|
73
|
+
const effectiveRules = mergeRules(zone.rules, overrideRules);
|
|
74
|
+
return {
|
|
75
|
+
zoneId: zone.zoneId,
|
|
76
|
+
optedInAt: Date.now(),
|
|
77
|
+
method,
|
|
78
|
+
acceptedRules: zone.rules,
|
|
79
|
+
userOverrides,
|
|
80
|
+
effectiveRules,
|
|
81
|
+
sessionId
|
|
82
|
+
};
|
|
83
|
+
}
|
|
84
|
+
function startHandshake(initiator, initiatorId, zoneId) {
|
|
85
|
+
const handshakeId = `handshake-${Date.now()}-${Math.random().toString(36).slice(2, 8)}`;
|
|
86
|
+
const now = Date.now();
|
|
87
|
+
const participant = {
|
|
88
|
+
participantId: initiatorId,
|
|
89
|
+
publishedConstraints: initiator,
|
|
90
|
+
joinedAt: now,
|
|
91
|
+
acknowledged: true
|
|
92
|
+
};
|
|
93
|
+
return {
|
|
94
|
+
handshakeId,
|
|
95
|
+
participants: [participant],
|
|
96
|
+
negotiatedRules: constraintsToRules(initiator),
|
|
97
|
+
zoneId,
|
|
98
|
+
createdAt: now,
|
|
99
|
+
lastNegotiatedAt: now,
|
|
100
|
+
state: "active"
|
|
101
|
+
};
|
|
102
|
+
}
|
|
103
|
+
function joinHandshake(handshake, constraints, participantId) {
|
|
104
|
+
const now = Date.now();
|
|
105
|
+
const newParticipant = {
|
|
106
|
+
participantId,
|
|
107
|
+
publishedConstraints: constraints,
|
|
108
|
+
joinedAt: now,
|
|
109
|
+
acknowledged: false
|
|
110
|
+
};
|
|
111
|
+
const participants = [...handshake.participants, newParticipant];
|
|
112
|
+
const negotiatedRules = negotiateHandshake(participants);
|
|
113
|
+
return {
|
|
114
|
+
...handshake,
|
|
115
|
+
participants,
|
|
116
|
+
negotiatedRules,
|
|
117
|
+
lastNegotiatedAt: now
|
|
118
|
+
};
|
|
119
|
+
}
|
|
120
|
+
function leaveHandshake(handshake, participantId) {
|
|
121
|
+
const participants = handshake.participants.filter(
|
|
122
|
+
(p) => p.participantId !== participantId
|
|
123
|
+
);
|
|
124
|
+
if (participants.length < 2) {
|
|
125
|
+
return {
|
|
126
|
+
...handshake,
|
|
127
|
+
participants,
|
|
128
|
+
state: "dissolved",
|
|
129
|
+
lastNegotiatedAt: Date.now(),
|
|
130
|
+
negotiatedRules: participants.length === 1 ? constraintsToRules(participants[0].publishedConstraints) : DEFAULT_ZONE_RULES
|
|
131
|
+
};
|
|
132
|
+
}
|
|
133
|
+
return {
|
|
134
|
+
...handshake,
|
|
135
|
+
participants,
|
|
136
|
+
negotiatedRules: negotiateHandshake(participants),
|
|
137
|
+
lastNegotiatedAt: Date.now()
|
|
138
|
+
};
|
|
139
|
+
}
|
|
140
|
+
function negotiateHandshake(participants) {
|
|
141
|
+
if (participants.length === 0) return DEFAULT_ZONE_RULES;
|
|
142
|
+
let result = constraintsToRules(participants[0].publishedConstraints);
|
|
143
|
+
for (let i = 1; i < participants.length; i++) {
|
|
144
|
+
const participantRules = constraintsToRules(participants[i].publishedConstraints);
|
|
145
|
+
result = mergeRules(result, participantRules);
|
|
146
|
+
}
|
|
147
|
+
return result;
|
|
148
|
+
}
|
|
149
|
+
function createSession() {
|
|
150
|
+
return {
|
|
151
|
+
sessionId: `session-${Date.now()}-${Math.random().toString(36).slice(2, 8)}`,
|
|
152
|
+
effectiveRules: DEFAULT_ZONE_RULES,
|
|
153
|
+
state: "discovering",
|
|
154
|
+
startedAt: Date.now(),
|
|
155
|
+
endedAt: null,
|
|
156
|
+
events: []
|
|
157
|
+
};
|
|
158
|
+
}
|
|
159
|
+
function applyZoneToSession(session, zone, optIn) {
|
|
160
|
+
const event = {
|
|
161
|
+
type: "zone_opted_in",
|
|
162
|
+
zoneId: zone.zoneId,
|
|
163
|
+
timestamp: Date.now()
|
|
164
|
+
};
|
|
165
|
+
const baseRules = session.handshake ? session.handshake.negotiatedRules : DEFAULT_ZONE_RULES;
|
|
166
|
+
const effectiveRules = mergeRules(baseRules, optIn.effectiveRules);
|
|
167
|
+
return {
|
|
168
|
+
...session,
|
|
169
|
+
zone,
|
|
170
|
+
optIn,
|
|
171
|
+
effectiveRules,
|
|
172
|
+
state: session.handshake ? "handshake_active" : "opted_in",
|
|
173
|
+
events: [...session.events, event]
|
|
174
|
+
};
|
|
175
|
+
}
|
|
176
|
+
function applyHandshakeToSession(session, handshake) {
|
|
177
|
+
const event = {
|
|
178
|
+
type: "handshake_negotiated",
|
|
179
|
+
handshakeId: handshake.handshakeId,
|
|
180
|
+
timestamp: Date.now()
|
|
181
|
+
};
|
|
182
|
+
const baseRules = session.optIn ? session.optIn.effectiveRules : DEFAULT_ZONE_RULES;
|
|
183
|
+
const effectiveRules = mergeRules(baseRules, handshake.negotiatedRules);
|
|
184
|
+
return {
|
|
185
|
+
...session,
|
|
186
|
+
handshake,
|
|
187
|
+
effectiveRules,
|
|
188
|
+
state: "handshake_active",
|
|
189
|
+
events: [...session.events, event]
|
|
190
|
+
};
|
|
191
|
+
}
|
|
192
|
+
function exitZone(session) {
|
|
193
|
+
if (!session.zone) return session;
|
|
194
|
+
const event = {
|
|
195
|
+
type: "zone_exited",
|
|
196
|
+
zoneId: session.zone.zoneId,
|
|
197
|
+
timestamp: Date.now()
|
|
198
|
+
};
|
|
199
|
+
const effectiveRules = session.handshake ? session.handshake.negotiatedRules : DEFAULT_ZONE_RULES;
|
|
200
|
+
return {
|
|
201
|
+
...session,
|
|
202
|
+
zone: void 0,
|
|
203
|
+
optIn: void 0,
|
|
204
|
+
effectiveRules,
|
|
205
|
+
state: session.handshake ? "handshake_active" : "discovering",
|
|
206
|
+
events: [...session.events, event]
|
|
207
|
+
};
|
|
208
|
+
}
|
|
209
|
+
function endSession(session) {
|
|
210
|
+
return {
|
|
211
|
+
...session,
|
|
212
|
+
state: "ended",
|
|
213
|
+
endedAt: Date.now(),
|
|
214
|
+
effectiveRules: DEFAULT_ZONE_RULES
|
|
215
|
+
};
|
|
216
|
+
}
|
|
217
|
+
var INTENT_TO_RULE = {
|
|
218
|
+
// Camera intents
|
|
219
|
+
"photo_capture": "camera",
|
|
220
|
+
"stream_start": "camera",
|
|
221
|
+
"restream_start": "camera",
|
|
222
|
+
// Microphone intents
|
|
223
|
+
"transcription_start": "microphone",
|
|
224
|
+
"translation_start": "microphone",
|
|
225
|
+
"phone_passthrough_start": "microphone",
|
|
226
|
+
// AI data intents
|
|
227
|
+
"ai_send_transcription": "aiDataSend",
|
|
228
|
+
"ai_send_image": "aiDataSend",
|
|
229
|
+
"ai_send_location": "aiDataSend",
|
|
230
|
+
// AI action intents
|
|
231
|
+
"ai_auto_action": "aiActions",
|
|
232
|
+
"ai_auto_purchase": "aiActions",
|
|
233
|
+
"ai_auto_respond_message": "aiActions",
|
|
234
|
+
// AI overlay intents
|
|
235
|
+
"display_text_wall": "aiOverlay",
|
|
236
|
+
"display_double_text_wall": "aiOverlay",
|
|
237
|
+
"display_image": "aiOverlay",
|
|
238
|
+
// Data retention
|
|
239
|
+
"ai_retain_session_data": "dataRetention",
|
|
240
|
+
// Location
|
|
241
|
+
"location_get_current": "locationSharing",
|
|
242
|
+
"location_start_tracking": "locationSharing",
|
|
243
|
+
// Third-party sharing
|
|
244
|
+
"ai_share_with_third_party": "aiDataSend"
|
|
245
|
+
};
|
|
246
|
+
function evaluateSpatial(intent, session) {
|
|
247
|
+
const ruleKey = INTENT_TO_RULE[intent];
|
|
248
|
+
if (!ruleKey) {
|
|
249
|
+
return {
|
|
250
|
+
allowed: true,
|
|
251
|
+
requiresConfirmation: false,
|
|
252
|
+
blockedBy: "none",
|
|
253
|
+
reason: "Intent not governed by spatial rules",
|
|
254
|
+
rule: "none"
|
|
255
|
+
};
|
|
256
|
+
}
|
|
257
|
+
const effectiveValue = session.effectiveRules[ruleKey];
|
|
258
|
+
if (effectiveValue === "blocked") {
|
|
259
|
+
const zoneBlocks = session.zone && session.optIn?.effectiveRules[ruleKey] === "blocked";
|
|
260
|
+
const handshakeBlocks = session.handshake && session.handshake.negotiatedRules[ruleKey] === "blocked";
|
|
261
|
+
let blockedBy = "none";
|
|
262
|
+
if (zoneBlocks && handshakeBlocks) blockedBy = "zone+handshake";
|
|
263
|
+
else if (zoneBlocks) blockedBy = "zone";
|
|
264
|
+
else if (handshakeBlocks) blockedBy = "handshake";
|
|
265
|
+
const source = blockedBy === "zone" ? `zone "${session.zone?.name}"` : blockedBy === "handshake" ? `handshake (${session.handshake?.participants.length} participants)` : blockedBy === "zone+handshake" ? `zone "${session.zone?.name}" + handshake` : "spatial rules";
|
|
266
|
+
return {
|
|
267
|
+
allowed: false,
|
|
268
|
+
requiresConfirmation: false,
|
|
269
|
+
blockedBy,
|
|
270
|
+
reason: `${intent} blocked by ${source}: ${ruleKey} = blocked`,
|
|
271
|
+
rule: ruleKey
|
|
272
|
+
};
|
|
273
|
+
}
|
|
274
|
+
if (effectiveValue === "confirm_each" || effectiveValue === "confirm_all" || effectiveValue === "non_obstructive" || effectiveValue === "non_commercial" || effectiveValue === "session_only" || effectiveValue === "declared_only") {
|
|
275
|
+
return {
|
|
276
|
+
allowed: false,
|
|
277
|
+
requiresConfirmation: true,
|
|
278
|
+
blockedBy: "none",
|
|
279
|
+
reason: `${intent} requires confirmation: ${ruleKey} = ${effectiveValue}`,
|
|
280
|
+
rule: ruleKey
|
|
281
|
+
};
|
|
282
|
+
}
|
|
283
|
+
return {
|
|
284
|
+
allowed: true,
|
|
285
|
+
requiresConfirmation: false,
|
|
286
|
+
blockedBy: "none",
|
|
287
|
+
reason: `${intent} allowed by spatial rules`,
|
|
288
|
+
rule: ruleKey
|
|
289
|
+
};
|
|
290
|
+
}
|
|
291
|
+
function activateEmergencyOverride(session) {
|
|
292
|
+
const event = {
|
|
293
|
+
type: "zone_exited",
|
|
294
|
+
zoneId: session.zone?.zoneId ?? "emergency",
|
|
295
|
+
timestamp: Date.now()
|
|
296
|
+
};
|
|
297
|
+
return {
|
|
298
|
+
...session,
|
|
299
|
+
effectiveRules: DEFAULT_ZONE_RULES,
|
|
300
|
+
// Everything allowed
|
|
301
|
+
state: "active",
|
|
302
|
+
events: [...session.events, event]
|
|
303
|
+
};
|
|
304
|
+
}
|
|
305
|
+
function evaluateSpatialEmergency(_intent) {
|
|
306
|
+
return {
|
|
307
|
+
allowed: true,
|
|
308
|
+
requiresConfirmation: false,
|
|
309
|
+
blockedBy: "none",
|
|
310
|
+
reason: "Emergency override active \u2014 all spatial rules bypassed",
|
|
311
|
+
rule: "emergency"
|
|
312
|
+
};
|
|
313
|
+
}
|
|
314
|
+
var ZONE_TEMPLATES = {
|
|
315
|
+
/** Coffee shop — relaxed, but no recording without asking */
|
|
316
|
+
cafe: {
|
|
317
|
+
...DEFAULT_ZONE_RULES,
|
|
318
|
+
camera: "confirm_each",
|
|
319
|
+
microphone: "allowed",
|
|
320
|
+
aiRecommendations: "non_commercial",
|
|
321
|
+
bystanderProtection: "elevated"
|
|
322
|
+
},
|
|
323
|
+
/** Hospital — strict, PHI-aware, no unauthorized recording */
|
|
324
|
+
healthcare: {
|
|
325
|
+
...DEFAULT_ZONE_RULES,
|
|
326
|
+
camera: "blocked",
|
|
327
|
+
microphone: "confirm_each",
|
|
328
|
+
aiDataSend: "confirm_each",
|
|
329
|
+
aiActions: "blocked",
|
|
330
|
+
aiRecommendations: "blocked",
|
|
331
|
+
locationSharing: "blocked",
|
|
332
|
+
dataRetention: "blocked",
|
|
333
|
+
commercialAI: "blocked",
|
|
334
|
+
bystanderProtection: "strict"
|
|
335
|
+
},
|
|
336
|
+
/** Retail store — allow browsing AI, block price surveillance */
|
|
337
|
+
retail: {
|
|
338
|
+
...DEFAULT_ZONE_RULES,
|
|
339
|
+
camera: "confirm_each",
|
|
340
|
+
aiRecommendations: "non_commercial",
|
|
341
|
+
commercialAI: "blocked",
|
|
342
|
+
bystanderProtection: "elevated"
|
|
343
|
+
},
|
|
344
|
+
/** Office / workplace — allow productivity AI, restrict recording */
|
|
345
|
+
workplace: {
|
|
346
|
+
...DEFAULT_ZONE_RULES,
|
|
347
|
+
camera: "blocked",
|
|
348
|
+
microphone: "confirm_each",
|
|
349
|
+
aiDataSend: "declared_only",
|
|
350
|
+
dataRetention: "session_only",
|
|
351
|
+
bystanderProtection: "elevated"
|
|
352
|
+
},
|
|
353
|
+
/** Concert / live event — no recording, no streaming */
|
|
354
|
+
entertainment: {
|
|
355
|
+
...DEFAULT_ZONE_RULES,
|
|
356
|
+
camera: "blocked",
|
|
357
|
+
microphone: "blocked",
|
|
358
|
+
aiOverlay: "non_obstructive",
|
|
359
|
+
dataRetention: "blocked",
|
|
360
|
+
bystanderProtection: "strict"
|
|
361
|
+
},
|
|
362
|
+
/** Library / museum — quiet, no recording, non-obstructive only */
|
|
363
|
+
education: {
|
|
364
|
+
...DEFAULT_ZONE_RULES,
|
|
365
|
+
camera: "blocked",
|
|
366
|
+
microphone: "blocked",
|
|
367
|
+
aiOverlay: "non_obstructive",
|
|
368
|
+
aiRecommendations: "non_commercial",
|
|
369
|
+
commercialAI: "blocked",
|
|
370
|
+
bystanderProtection: "elevated"
|
|
371
|
+
},
|
|
372
|
+
/** Religious space — absolute privacy and respect */
|
|
373
|
+
religious: {
|
|
374
|
+
...DEFAULT_ZONE_RULES,
|
|
375
|
+
camera: "blocked",
|
|
376
|
+
microphone: "blocked",
|
|
377
|
+
aiDataSend: "blocked",
|
|
378
|
+
aiActions: "blocked",
|
|
379
|
+
aiRecommendations: "blocked",
|
|
380
|
+
aiOverlay: "blocked",
|
|
381
|
+
dataRetention: "blocked",
|
|
382
|
+
commercialAI: "blocked",
|
|
383
|
+
bystanderProtection: "strict"
|
|
384
|
+
},
|
|
385
|
+
/** Home — fully permissive (user's own space) */
|
|
386
|
+
home: {
|
|
387
|
+
...DEFAULT_ZONE_RULES
|
|
388
|
+
},
|
|
389
|
+
/** Public transit — moderate restrictions */
|
|
390
|
+
transit: {
|
|
391
|
+
...DEFAULT_ZONE_RULES,
|
|
392
|
+
camera: "confirm_each",
|
|
393
|
+
microphone: "allowed",
|
|
394
|
+
bystanderProtection: "elevated"
|
|
395
|
+
}
|
|
396
|
+
};
|
|
397
|
+
|
|
398
|
+
// src/spatial/zones/example-zones.ts
|
|
399
|
+
var BLUE_BOTTLE_HAYES = {
|
|
400
|
+
zoneId: "zone-bluebottle-hayes-001",
|
|
401
|
+
name: "Blue Bottle Coffee \u2014 Hayes Valley",
|
|
402
|
+
publisher: {
|
|
403
|
+
name: "Blue Bottle Coffee",
|
|
404
|
+
type: "business",
|
|
405
|
+
verified: true
|
|
406
|
+
},
|
|
407
|
+
rules: {
|
|
408
|
+
...ZONE_TEMPLATES.cafe,
|
|
409
|
+
// Blue Bottle addition: no AI product recommendations (respect the craft)
|
|
410
|
+
custom: [{
|
|
411
|
+
id: "no-product-recs",
|
|
412
|
+
description: "No AI-generated product recommendations in our space",
|
|
413
|
+
targetIntent: "ai_recommend_product",
|
|
414
|
+
effect: "block",
|
|
415
|
+
rationale: "We curate our own experience. Ask our baristas, not your AI."
|
|
416
|
+
}]
|
|
417
|
+
},
|
|
418
|
+
discovery: { method: "auki_anchor", anchorId: "auki-bb-hayes-001", confidence: 0.95 },
|
|
419
|
+
type: "hospitality",
|
|
420
|
+
requiresOptIn: false,
|
|
421
|
+
rationale: "A coffee shop where people work, talk, and think. We protect the vibe and the people in it.",
|
|
422
|
+
rulesUpdatedAt: Date.now(),
|
|
423
|
+
version: "1.0.0"
|
|
424
|
+
};
|
|
425
|
+
var UCSF_MEDICAL = {
|
|
426
|
+
zoneId: "zone-ucsf-medical-001",
|
|
427
|
+
name: "UCSF Medical Center \u2014 Main Campus",
|
|
428
|
+
publisher: {
|
|
429
|
+
name: "UCSF Health",
|
|
430
|
+
type: "institution",
|
|
431
|
+
verified: true
|
|
432
|
+
},
|
|
433
|
+
rules: {
|
|
434
|
+
...ZONE_TEMPLATES.healthcare,
|
|
435
|
+
custom: [
|
|
436
|
+
{
|
|
437
|
+
id: "phi-in-network",
|
|
438
|
+
description: "No patient health information may be sent to AI providers outside the UCSF network",
|
|
439
|
+
targetIntent: "ai_send_transcription",
|
|
440
|
+
effect: "block",
|
|
441
|
+
rationale: "HIPAA compliance. PHI stays in-network."
|
|
442
|
+
},
|
|
443
|
+
{
|
|
444
|
+
id: "no-clinical-ai-action",
|
|
445
|
+
description: "AI cannot take autonomous clinical actions",
|
|
446
|
+
targetIntent: "ai_auto_action",
|
|
447
|
+
effect: "block",
|
|
448
|
+
rationale: "Clinical decisions require licensed physician confirmation."
|
|
449
|
+
}
|
|
450
|
+
]
|
|
451
|
+
},
|
|
452
|
+
discovery: { method: "ble_beacon", beaconId: "ucsf-main-lobby-001", rssi: -65 },
|
|
453
|
+
type: "healthcare",
|
|
454
|
+
requiresOptIn: true,
|
|
455
|
+
rationale: "A hospital where patients, families, and staff deserve absolute privacy. Recording is prohibited. AI actions require clinical oversight. Patient data never leaves our network.",
|
|
456
|
+
rulesUpdatedAt: Date.now(),
|
|
457
|
+
version: "2.0.0"
|
|
458
|
+
};
|
|
459
|
+
var APPLE_UNION_SQUARE = {
|
|
460
|
+
zoneId: "zone-apple-unisonq-001",
|
|
461
|
+
name: "Apple Union Square",
|
|
462
|
+
publisher: {
|
|
463
|
+
name: "Apple Inc.",
|
|
464
|
+
type: "business",
|
|
465
|
+
verified: true
|
|
466
|
+
},
|
|
467
|
+
rules: {
|
|
468
|
+
...ZONE_TEMPLATES.retail,
|
|
469
|
+
camera: "blocked",
|
|
470
|
+
// No photos of displays / unreleased products
|
|
471
|
+
custom: [
|
|
472
|
+
{
|
|
473
|
+
id: "no-price-comparison",
|
|
474
|
+
description: "No AI-powered real-time price comparison while in store",
|
|
475
|
+
targetIntent: "ai_compare_prices",
|
|
476
|
+
effect: "block",
|
|
477
|
+
rationale: "Respect our retail experience. Compare prices on your own time."
|
|
478
|
+
},
|
|
479
|
+
{
|
|
480
|
+
id: "no-product-scanning",
|
|
481
|
+
description: "No AI scanning of unreleased or display products",
|
|
482
|
+
targetIntent: "ai_send_image",
|
|
483
|
+
effect: "block",
|
|
484
|
+
rationale: "Display products may include unreleased items under NDA."
|
|
485
|
+
}
|
|
486
|
+
]
|
|
487
|
+
},
|
|
488
|
+
discovery: { method: "ble_beacon", beaconId: "apple-usq-main-001", rssi: -60 },
|
|
489
|
+
type: "retail",
|
|
490
|
+
requiresOptIn: false,
|
|
491
|
+
rationale: "A retail experience designed to be explored in person. No AI product scanning, no real-time price comparison, no unauthorized recording of displays.",
|
|
492
|
+
rulesUpdatedAt: Date.now(),
|
|
493
|
+
version: "1.0.0"
|
|
494
|
+
};
|
|
495
|
+
var CHASE_CENTER = {
|
|
496
|
+
zoneId: "zone-chase-center-001",
|
|
497
|
+
name: "Chase Center",
|
|
498
|
+
publisher: {
|
|
499
|
+
name: "Chase Center / GSW",
|
|
500
|
+
type: "business",
|
|
501
|
+
verified: true
|
|
502
|
+
},
|
|
503
|
+
rules: {
|
|
504
|
+
...ZONE_TEMPLATES.entertainment,
|
|
505
|
+
custom: [{
|
|
506
|
+
id: "no-event-recording",
|
|
507
|
+
description: "No audio or video recording of live performances",
|
|
508
|
+
targetIntent: "stream_start",
|
|
509
|
+
effect: "block",
|
|
510
|
+
rationale: "Live performance rights. Recording violates artist/league agreements."
|
|
511
|
+
}]
|
|
512
|
+
},
|
|
513
|
+
discovery: { method: "geofence", lat: 37.768, lng: -122.3877, radiusMeters: 200 },
|
|
514
|
+
type: "entertainment",
|
|
515
|
+
requiresOptIn: true,
|
|
516
|
+
rationale: "A live event venue. No recording of performances. No streaming. AR overlays must not obstruct the live experience. Strict bystander protection for 18,000+ attendees.",
|
|
517
|
+
rulesUpdatedAt: Date.now(),
|
|
518
|
+
version: "1.0.0"
|
|
519
|
+
};
|
|
520
|
+
var WEWORK_SOMA = {
|
|
521
|
+
zoneId: "zone-wework-soma-001",
|
|
522
|
+
name: "WeWork \u2014 SoMa",
|
|
523
|
+
publisher: {
|
|
524
|
+
name: "WeWork",
|
|
525
|
+
type: "business",
|
|
526
|
+
verified: true
|
|
527
|
+
},
|
|
528
|
+
rules: {
|
|
529
|
+
...ZONE_TEMPLATES.workplace,
|
|
530
|
+
aiDataSend: "allowed",
|
|
531
|
+
// Productivity AI is the whole point
|
|
532
|
+
aiOverlay: "allowed",
|
|
533
|
+
// AR workspace tools welcome
|
|
534
|
+
custom: [{
|
|
535
|
+
id: "no-competitor-intel",
|
|
536
|
+
description: "No AI analysis of other tenants' visible screens or conversations",
|
|
537
|
+
targetIntent: "ai_send_image",
|
|
538
|
+
effect: "confirm",
|
|
539
|
+
rationale: "Shared workspace. Respect other tenants' IP and privacy."
|
|
540
|
+
}]
|
|
541
|
+
},
|
|
542
|
+
discovery: { method: "auki_anchor", anchorId: "auki-wework-soma-floor3", confidence: 0.92 },
|
|
543
|
+
type: "workplace",
|
|
544
|
+
requiresOptIn: false,
|
|
545
|
+
rationale: "A shared workspace where productivity AI is welcome but recording is restricted. Other tenants' work is their own \u2014 no AI should analyze it.",
|
|
546
|
+
rulesUpdatedAt: Date.now(),
|
|
547
|
+
version: "1.0.0"
|
|
548
|
+
};
|
|
549
|
+
var SFMOMA = {
|
|
550
|
+
zoneId: "zone-sfmoma-001",
|
|
551
|
+
name: "SFMOMA",
|
|
552
|
+
publisher: {
|
|
553
|
+
name: "San Francisco Museum of Modern Art",
|
|
554
|
+
type: "institution",
|
|
555
|
+
verified: true
|
|
556
|
+
},
|
|
557
|
+
rules: {
|
|
558
|
+
...ZONE_TEMPLATES.education,
|
|
559
|
+
aiRecommendations: "allowed",
|
|
560
|
+
// "Tell me about this painting" is great
|
|
561
|
+
aiDataSend: "declared_only",
|
|
562
|
+
// AI art analysis is part of the experience
|
|
563
|
+
custom: [{
|
|
564
|
+
id: "no-flash-no-stream",
|
|
565
|
+
description: "No flash photography or live streaming of exhibitions",
|
|
566
|
+
targetIntent: "stream_start",
|
|
567
|
+
effect: "block",
|
|
568
|
+
rationale: "Protect the art and the experience of other visitors."
|
|
569
|
+
}]
|
|
570
|
+
},
|
|
571
|
+
discovery: { method: "nfc_tap", tagId: "sfmoma-entrance-nfc-001" },
|
|
572
|
+
type: "education",
|
|
573
|
+
requiresOptIn: false,
|
|
574
|
+
rationale: "A museum where AI can enhance the art experience (tell me about this piece) but recording and streaming are prohibited. No commercial AI recommendations.",
|
|
575
|
+
rulesUpdatedAt: Date.now(),
|
|
576
|
+
version: "1.0.0"
|
|
577
|
+
};
|
|
578
|
+
var MY_HOME = {
|
|
579
|
+
zoneId: "zone-home-user-001",
|
|
580
|
+
name: "Home",
|
|
581
|
+
publisher: {
|
|
582
|
+
name: "You",
|
|
583
|
+
type: "individual",
|
|
584
|
+
verified: false
|
|
585
|
+
},
|
|
586
|
+
rules: {
|
|
587
|
+
...ZONE_TEMPLATES.home
|
|
588
|
+
// Home is your space — fully permissive by default
|
|
589
|
+
// You can tighten it if you want (e.g., "no AI recording when guests are over")
|
|
590
|
+
},
|
|
591
|
+
discovery: { method: "geofence", lat: 37.7749, lng: -122.4194, radiusMeters: 50 },
|
|
592
|
+
type: "residential",
|
|
593
|
+
requiresOptIn: false,
|
|
594
|
+
rationale: "Your home. Your rules. Everything is allowed by default. Tighten as needed.",
|
|
595
|
+
rulesUpdatedAt: Date.now(),
|
|
596
|
+
version: "1.0.0"
|
|
597
|
+
};
|
|
598
|
+
var EXAMPLE_ZONES = [
|
|
599
|
+
BLUE_BOTTLE_HAYES,
|
|
600
|
+
UCSF_MEDICAL,
|
|
601
|
+
APPLE_UNION_SQUARE,
|
|
602
|
+
CHASE_CENTER,
|
|
603
|
+
WEWORK_SOMA,
|
|
604
|
+
SFMOMA,
|
|
605
|
+
MY_HOME
|
|
606
|
+
];
|
|
607
|
+
export {
|
|
608
|
+
APPLE_UNION_SQUARE,
|
|
609
|
+
BLUE_BOTTLE_HAYES,
|
|
610
|
+
CHASE_CENTER,
|
|
611
|
+
DEFAULT_PARTICIPANT_CONSTRAINTS,
|
|
612
|
+
DEFAULT_ZONE_RULES,
|
|
613
|
+
EXAMPLE_ZONES,
|
|
614
|
+
MY_HOME,
|
|
615
|
+
SFMOMA,
|
|
616
|
+
UCSF_MEDICAL,
|
|
617
|
+
WEWORK_SOMA,
|
|
618
|
+
ZONE_TEMPLATES,
|
|
619
|
+
activateEmergencyOverride,
|
|
620
|
+
applyHandshakeToSession,
|
|
621
|
+
applyZoneToSession,
|
|
622
|
+
constraintsToRules,
|
|
623
|
+
createOptIn,
|
|
624
|
+
createSession,
|
|
625
|
+
endSession,
|
|
626
|
+
evaluateSpatial,
|
|
627
|
+
evaluateSpatialEmergency,
|
|
628
|
+
exitZone,
|
|
629
|
+
joinHandshake,
|
|
630
|
+
leaveHandshake,
|
|
631
|
+
mergeRules,
|
|
632
|
+
startHandshake
|
|
633
|
+
};
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
---
|
|
2
|
+
world_id: mentraos-spatial
|
|
3
|
+
name: "MentraOS Spatial Governance"
|
|
4
|
+
version: "1.0.0"
|
|
5
|
+
runtime_mode: COMPLIANCE
|
|
6
|
+
description: >
|
|
7
|
+
Spatial governance layer for MentraOS smart glasses. Activates when
|
|
8
|
+
glasses detect zones (via Auki anchors, BLE, geofence) or nearby
|
|
9
|
+
participants (multi-user handshake). Rules are temporary — they apply
|
|
10
|
+
while you're in the space and dissolve when you leave.
|
|
11
|
+
---
|
|
12
|
+
|
|
13
|
+
# Thesis
|
|
14
|
+
|
|
15
|
+
Smart glasses live in the real world. The rules that apply at home don't apply at a hospital. The rules that apply when you're alone don't apply when you're standing next to someone who doesn't want to be recorded. Spatial governance makes AI respect the physical context it operates in.
|
|
16
|
+
|
|
17
|
+
This world governs the spatial layer — the bridge between physical location and AI behavior. It enforces three principles: (1) zones publish rules and users opt in explicitly, (2) when multiple AR users share a space their governance composes via handshake with "most restrictive wins," and (3) all spatial rules are temporary — they dissolve when you leave.
|
|
18
|
+
|
|
19
|
+
This layer sits between user rules (which always win) and the platform world. A zone can tighten your rules, never relax them. A handshake participant can tighten the group, never relax it.
|
|
20
|
+
|
|
21
|
+
# Invariants
|
|
22
|
+
|
|
23
|
+
- `opt_in_required` — Users must explicitly accept a zone's rules before they apply. No zone can force governance on a user without consent. Discovery is passive; acceptance is active. (structural, immutable)
|
|
24
|
+
- `most_restrictive_wins` — When zone rules, handshake rules, and user rules overlap, the most restrictive value for each field wins. A zone cannot relax your personal rules. A single handshake participant blocking recording means nobody records. (structural, immutable)
|
|
25
|
+
- `rules_are_temporary` — Spatial rules apply only during the spatial session. When you leave a zone or a handshake dissolves, the rules dissolve with it. No spatial rule persists beyond the session. (structural, immutable)
|
|
26
|
+
- `no_identity_leak` — Handshake negotiation shares governance constraints, not identity. "I require no recording" is a constraint, not a name. Participants are anonymous by default. (structural, immutable)
|
|
27
|
+
- `zone_transparency` — When a zone's rules are active, the user must be able to see which zone they're in, what rules apply, and how to exit. No invisible governance. (structural, immutable)
|
|
28
|
+
- `user_can_always_leave` — A user can exit any zone or leave any handshake at any time. Governance never traps the user. If you don't like the rules, you leave. Your personal rules remain. (structural, immutable)
|
|
29
|
+
- `physics_over_policy` — Hardware constraints override spatial rules. If glasses don't have a camera, a zone rule allowing cameras is irrelevant. Physics always wins. (structural, immutable)
|
|
30
|
+
- `bystander_default_protection` — In any space with non-consenting bystanders, the default is elevated bystander protection. Zones can tighten this to strict. No zone can lower it below standard. (structural, immutable)
|
|
31
|
+
|
|
32
|
+
# State
|
|
33
|
+
|
|
34
|
+
- `active_zone` — string, initial "none". The currently active zone ID.
|
|
35
|
+
- `zone_opt_ins` — counter, initial 0. Number of zone opt-ins this session.
|
|
36
|
+
- `zone_declines` — counter, initial 0. Number of zone opt-in declines.
|
|
37
|
+
- `handshake_participants` — counter, initial 0. Current handshake participant count.
|
|
38
|
+
- `handshake_negotiations` — counter, initial 0. Number of handshake re-negotiations.
|
|
39
|
+
- `spatial_blocks` — counter, initial 0. Number of intents blocked by spatial rules.
|
|
40
|
+
- `spatial_confirms` — counter, initial 0. Number of intents requiring spatial confirmation.
|
|
41
|
+
- `spatial_trust` — score, initial 1.0, range [0.0, 1.0]. Spatial governance health score.
|
|
42
|
+
|
|
43
|
+
# Assumptions
|
|
44
|
+
|
|
45
|
+
- `standard` — Default spatial behavior. zone_opt_in_required=true, handshake_auto_join=false, bystander_protection=elevated, max_handshake_participants=10
|
|
46
|
+
- `strict` — Privacy-first spatial. zone_opt_in_required=true, handshake_auto_join=false, bystander_protection=strict, max_handshake_participants=5
|
|
47
|
+
- `open` — For controlled spaces (home, private office). zone_opt_in_required=false, handshake_auto_join=true, bystander_protection=standard, max_handshake_participants=20
|
|
48
|
+
|
|
49
|
+
# Rules
|
|
50
|
+
|
|
51
|
+
- `rule-001` — Zone rules applied without opt-in. trigger: zone_active AND NOT zone_opted_in → spatial_trust *= 0.10, BLOCK: "Zone rules cannot apply without your explicit opt-in."
|
|
52
|
+
- `rule-002` — Handshake identity leaked. trigger: handshake_active AND identity_shared → spatial_trust *= 0.05, BLOCK: "Handshake negotiation must be anonymous."
|
|
53
|
+
- `rule-003` — Bystander protection violated. trigger: bystander_protection == "strict" AND camera == "allowed" → spatial_trust *= 0.20, BLOCK: "Camera blocked in strict bystander protection zone."
|
|
54
|
+
- `rule-004` — Zone exit blocked. trigger: user_exit_requested AND exit_denied → spatial_trust *= 0.01, BLOCK: "Users must always be able to exit a zone."
|
|
55
|
+
- `rule-005` — Spatial rules persisted beyond session. trigger: session_ended AND spatial_rules_active → spatial_trust *= 0.10, BLOCK: "Spatial rules must dissolve when session ends."
|
|
56
|
+
- `rule-006` — Clean spatial session (advantage). trigger: zone_opt_ins > 0 AND spatial_blocks == 0 → spatial_trust *= 1.08
|
|
57
|
+
- `rule-007` — Zone relaxing user rules. trigger: zone_rule_less_restrictive_than_user_rule → spatial_trust *= 0.30, BLOCK: "Zones can only tighten rules, never relax them."
|
|
58
|
+
|
|
59
|
+
# Gates
|
|
60
|
+
|
|
61
|
+
- ACTIVE: spatial_trust >= 0.7
|
|
62
|
+
- CAUTIOUS: spatial_trust >= 0.3
|
|
63
|
+
- SUSPENDED: spatial_trust < 0.3
|
|
64
|
+
|
|
65
|
+
# Outcomes
|
|
66
|
+
|
|
67
|
+
- `clean_spatial` — Desired. User navigated zones and handshakes with no governance violations. Tracked by: zone_opt_ins > 0 AND spatial_blocks == 0.
|
|
68
|
+
- `forced_governance` — Undesired. A zone or handshake tried to apply rules without consent. Tracked by: spatial_trust < 0.5.
|