@neuroverseos/governance 0.5.0 → 0.6.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 (133) hide show
  1. package/README.md +244 -0
  2. package/dist/adapters/autoresearch.d.cts +2 -1
  3. package/dist/adapters/autoresearch.d.ts +2 -1
  4. package/dist/adapters/autoresearch.js +2 -2
  5. package/dist/adapters/deep-agents.d.cts +3 -2
  6. package/dist/adapters/deep-agents.d.ts +3 -2
  7. package/dist/adapters/deep-agents.js +2 -2
  8. package/dist/adapters/express.d.cts +2 -1
  9. package/dist/adapters/express.d.ts +2 -1
  10. package/dist/adapters/express.js +2 -2
  11. package/dist/adapters/github.cjs +1697 -0
  12. package/dist/adapters/github.d.cts +225 -0
  13. package/dist/adapters/github.d.ts +225 -0
  14. package/dist/adapters/github.js +27 -0
  15. package/dist/adapters/index.d.cts +4 -316
  16. package/dist/adapters/index.d.ts +4 -316
  17. package/dist/adapters/index.js +23 -21
  18. package/dist/adapters/langchain.d.cts +3 -2
  19. package/dist/adapters/langchain.d.ts +3 -2
  20. package/dist/adapters/langchain.js +2 -2
  21. package/dist/adapters/mentraos.cjs +2181 -0
  22. package/dist/adapters/mentraos.d.cts +319 -0
  23. package/dist/adapters/mentraos.d.ts +319 -0
  24. package/dist/{mentraos-LLH7KEV4.js → adapters/mentraos.js} +12 -10
  25. package/dist/adapters/openai.d.cts +3 -2
  26. package/dist/adapters/openai.d.ts +3 -2
  27. package/dist/adapters/openai.js +2 -2
  28. package/dist/adapters/openclaw.d.cts +3 -2
  29. package/dist/adapters/openclaw.d.ts +3 -2
  30. package/dist/adapters/openclaw.js +2 -2
  31. package/dist/{add-LYHDZ5RL.js → add-XSANI3FK.js} +1 -1
  32. package/dist/admin/index.cjs +2214 -0
  33. package/dist/admin/index.d.cts +362 -0
  34. package/dist/admin/index.d.ts +362 -0
  35. package/dist/admin/index.js +703 -0
  36. package/dist/bootstrap-contract-DcV6t-8M.d.cts +216 -0
  37. package/dist/bootstrap-contract-DcV6t-8M.d.ts +216 -0
  38. package/dist/{build-SCAWPA7E.js → build-UTVDGHB3.js} +5 -5
  39. package/dist/{chunk-JKGPSFGH.js → chunk-7FL3U7Z5.js} +3 -3
  40. package/dist/chunk-A2UZTLRV.js +421 -0
  41. package/dist/{chunk-TD5GKIHP.js → chunk-B3IIPTY3.js} +3 -3
  42. package/dist/chunk-EQR7BGFN.js +337 -0
  43. package/dist/{chunk-5JUZ4HL7.js → chunk-FDPPZLSQ.js} +3 -3
  44. package/dist/{chunk-MFKHTE5R.js → chunk-FKQCPRKI.js} +3 -3
  45. package/dist/{chunk-7D7PZLB7.js → chunk-FS2UUJJO.js} +3 -3
  46. package/dist/{chunk-U6FRAEQJ.js → chunk-GJ6LM4JZ.js} +1 -441
  47. package/dist/chunk-H3REGQRI.js +107 -0
  48. package/dist/{chunk-25XHSTPT.js → chunk-HDNDL6D5.js} +3 -3
  49. package/dist/{chunk-BXLTEUS4.js → chunk-I4RTIMLX.js} +2 -2
  50. package/dist/chunk-IOVXB6QN.js +447 -0
  51. package/dist/{chunk-Y6WXAPKY.js → chunk-NTHXZAW4.js} +3 -3
  52. package/dist/{chunk-UTH7OXTM.js → chunk-OTZU76DH.js} +22 -4
  53. package/dist/{chunk-DWHUZUEY.js → chunk-T6GMRZWC.js} +3 -3
  54. package/dist/{chunk-V4FZHJQX.js → chunk-TIXVEPS2.js} +3 -3
  55. package/dist/{chunk-YNYCQECH.js → chunk-TJ5L2UTE.js} +3 -3
  56. package/dist/chunk-UGTNKTHS.js +542 -0
  57. package/dist/cli/neuroverse.cjs +3372 -523
  58. package/dist/cli/neuroverse.js +53 -21
  59. package/dist/cli/plan.js +2 -2
  60. package/dist/cli/run.cjs +242 -139
  61. package/dist/cli/run.js +23 -3
  62. package/dist/cli/worldmodel.cjs +1624 -0
  63. package/dist/cli/worldmodel.d.cts +24 -0
  64. package/dist/cli/worldmodel.d.ts +24 -0
  65. package/dist/cli/worldmodel.js +742 -0
  66. package/dist/{demo-66MMJTEH.js → demo-6W3YXLAX.js} +4 -4
  67. package/dist/{derive-AUQE3L3P.js → derive-42IJW7JI.js} +4 -4
  68. package/dist/{doctor-EY7LKSYY.js → doctor-XEMLO6UA.js} +3 -2
  69. package/dist/engine/bootstrap-emitter.cjs +241 -0
  70. package/dist/engine/bootstrap-emitter.d.cts +27 -0
  71. package/dist/engine/bootstrap-emitter.d.ts +27 -0
  72. package/dist/{bootstrap-emitter-GIMOJFOC.js → engine/bootstrap-emitter.js} +2 -2
  73. package/dist/engine/bootstrap-parser.cjs +560 -0
  74. package/dist/engine/bootstrap-parser.d.cts +96 -0
  75. package/dist/engine/bootstrap-parser.d.ts +96 -0
  76. package/dist/{bootstrap-parser-LBLGVEMU.js → engine/bootstrap-parser.js} +2 -2
  77. package/dist/engine/guard-engine.cjs +1116 -0
  78. package/dist/engine/guard-engine.d.cts +60 -0
  79. package/dist/engine/guard-engine.d.ts +60 -0
  80. package/dist/{guard-engine-N7TUIUU7.js → engine/guard-engine.js} +3 -3
  81. package/dist/engine/simulate-engine.cjs +390 -0
  82. package/dist/engine/simulate-engine.d.cts +105 -0
  83. package/dist/engine/simulate-engine.d.ts +105 -0
  84. package/dist/engine/simulate-engine.js +9 -0
  85. package/dist/engine/worldmodel-compiler.cjs +366 -0
  86. package/dist/engine/worldmodel-compiler.d.cts +46 -0
  87. package/dist/engine/worldmodel-compiler.d.ts +46 -0
  88. package/dist/engine/worldmodel-compiler.js +17 -0
  89. package/dist/engine/worldmodel-parser.cjs +566 -0
  90. package/dist/engine/worldmodel-parser.d.cts +22 -0
  91. package/dist/engine/worldmodel-parser.d.ts +22 -0
  92. package/dist/engine/worldmodel-parser.js +7 -0
  93. package/dist/{equity-penalties-WWC7UDQD.js → equity-penalties-CCO3GVHS.js} +6 -6
  94. package/dist/{explain-MUSGDT67.js → explain-HDFN4ION.js} +1 -1
  95. package/dist/{guard-W3BMQPBJ.js → guard-IHJEKHL2.js} +16 -4
  96. package/dist/{guard-contract-CLBbTGK_.d.ts → guard-contract-ddiIPlOg.d.cts} +2 -369
  97. package/dist/{guard-contract-CLBbTGK_.d.cts → guard-contract-q6HJAq3Q.d.ts} +2 -369
  98. package/dist/{improve-PJDAWW4Q.js → improve-LRORRYEX.js} +3 -3
  99. package/dist/index.cjs +471 -1
  100. package/dist/index.d.cts +14 -492
  101. package/dist/index.d.ts +14 -492
  102. package/dist/index.js +63 -42
  103. package/dist/keygen-BSZH3NM2.js +77 -0
  104. package/dist/{lens-IP6GIZ2Q.js → lens-TLDZQXBI.js} +152 -26
  105. package/dist/{mcp-server-OG3PPVD2.js → mcp-server-CKYBHXWK.js} +2 -2
  106. package/dist/migrate-NH5PVMX4.js +221 -0
  107. package/dist/{playground-4BK2XQ47.js → playground-3TTBN7XD.js} +5 -5
  108. package/dist/{redteam-BRZALBPP.js → redteam-W644UMWN.js} +3 -3
  109. package/dist/{session-SGRUT2UH.js → session-FMAROEIE.js} +2 -2
  110. package/dist/{shared-CwGpPheR.d.ts → shared-DAzdfWtU.d.ts} +1 -1
  111. package/dist/{shared-BGzmYP5g.d.cts → shared-PpalGKxc.d.cts} +1 -1
  112. package/dist/sign-RRELHKWM.js +11 -0
  113. package/dist/{simulate-FGXKIH7V.js → simulate-VT437EEL.js} +2 -2
  114. package/dist/{test-PT44BSYG.js → test-XDB2DH3L.js} +3 -3
  115. package/dist/types.cjs +18 -0
  116. package/dist/types.d.cts +370 -0
  117. package/dist/types.d.ts +370 -0
  118. package/dist/types.js +0 -0
  119. package/dist/{validate-Q5O5TGLT.js → validate-M52DX22Y.js} +1 -1
  120. package/dist/verify-6AVTWX75.js +151 -0
  121. package/dist/{world-V52ZMH26.js → world-O4HTQPDP.js} +1 -1
  122. package/dist/{world-loader-C4D3VPP3.js → world-loader-YTYFOP7D.js} +1 -1
  123. package/dist/worldmodel-contract-BPGhiuW5.d.cts +221 -0
  124. package/dist/worldmodel-contract-BPGhiuW5.d.ts +221 -0
  125. package/dist/worlds/auki-vanguard.worldmodel.md +116 -0
  126. package/dist/worlds/behavioral-demo.nv-world.md +130 -0
  127. package/dist/worlds/neuroverse-governance.worldmodel.md +115 -0
  128. package/package.json +44 -3
  129. package/dist/{bootstrap-IP5QMC3Q.js → bootstrap-2OW5ZLBL.js} +3 -3
  130. package/dist/{chunk-4G6WHPLI.js → chunk-735Z3HA4.js} +6 -6
  131. package/dist/{chunk-7QIAF377.js → chunk-CYDMUJVZ.js} +0 -0
  132. package/dist/{configure-ai-LL3VAPQW.js → configure-ai-5MP5DWTT.js} +3 -3
  133. package/dist/{decision-flow-3K4D72G4.js → decision-flow-IJPNMVQK.js} +3 -3
@@ -0,0 +1,2214 @@
1
+ "use strict";
2
+ var __defProp = Object.defineProperty;
3
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
4
+ var __getOwnPropNames = Object.getOwnPropertyNames;
5
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
6
+ var __export = (target, all) => {
7
+ for (var name in all)
8
+ __defProp(target, name, { get: all[name], enumerable: true });
9
+ };
10
+ var __copyProps = (to, from, except, desc) => {
11
+ if (from && typeof from === "object" || typeof from === "function") {
12
+ for (let key of __getOwnPropNames(from))
13
+ if (!__hasOwnProp.call(to, key) && key !== except)
14
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
15
+ }
16
+ return to;
17
+ };
18
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
19
+
20
+ // src/admin/index.ts
21
+ var admin_exports = {};
22
+ __export(admin_exports, {
23
+ GovernanceAdmin: () => GovernanceAdmin,
24
+ GovernanceAdminError: () => GovernanceAdminError,
25
+ InMemoryStorage: () => InMemoryStorage,
26
+ simulateFullMatrix: () => simulateFullMatrix,
27
+ simulatePolicy: () => simulatePolicy
28
+ });
29
+ module.exports = __toCommonJS(admin_exports);
30
+
31
+ // src/engine/text-utils.ts
32
+ function normalizeEventText(event) {
33
+ return [
34
+ event.intent,
35
+ event.tool ?? "",
36
+ event.scope ?? ""
37
+ ].join(" ").toLowerCase();
38
+ }
39
+ function extractKeywords(text, minLength = 3) {
40
+ return text.toLowerCase().split(/\s+/).filter((w) => w.length > minLength);
41
+ }
42
+ function matchesAllKeywords(eventText, ruleText) {
43
+ const keywords = extractKeywords(ruleText);
44
+ if (keywords.length === 0) return false;
45
+ return keywords.every((kw) => eventText.includes(kw));
46
+ }
47
+ function matchesKeywordThreshold(eventText, ruleText, threshold = 0.5) {
48
+ const keywords = extractKeywords(ruleText);
49
+ if (keywords.length === 0) return false;
50
+ const matched = keywords.filter((kw) => eventText.includes(kw));
51
+ return matched.length >= Math.ceil(keywords.length * threshold);
52
+ }
53
+ function tokenSimilarity(a, b) {
54
+ const tokensA = new Set(a.toLowerCase().split(/\s+/).filter((w) => w.length > 2));
55
+ const tokensB = new Set(b.toLowerCase().split(/\s+/).filter((w) => w.length > 2));
56
+ if (tokensA.size === 0 || tokensB.size === 0) return 0;
57
+ let intersection = 0;
58
+ for (const t of tokensA) {
59
+ if (tokensB.has(t)) intersection++;
60
+ }
61
+ const union = (/* @__PURE__ */ new Set([...tokensA, ...tokensB])).size;
62
+ return union > 0 ? intersection / union : 0;
63
+ }
64
+
65
+ // src/engine/plan-engine.ts
66
+ function keywordMatch(eventText, step) {
67
+ const stepText = [
68
+ step.label,
69
+ step.description ?? "",
70
+ ...step.tags ?? []
71
+ ].join(" ");
72
+ return matchesKeywordThreshold(eventText, stepText, 0.5);
73
+ }
74
+ function tokenSimilarity2(a, b) {
75
+ return tokenSimilarity(a, b);
76
+ }
77
+ function findMatchingStep(eventText, event, steps) {
78
+ const pendingOrActive = steps.filter((s) => s.status === "pending" || s.status === "active");
79
+ if (pendingOrActive.length === 0) {
80
+ return { matched: null, closest: null, closestScore: 0 };
81
+ }
82
+ for (const step of pendingOrActive) {
83
+ if (keywordMatch(eventText, step)) {
84
+ if (step.tools && event.tool && !step.tools.includes(event.tool)) {
85
+ continue;
86
+ }
87
+ return { matched: step, closest: step, closestScore: 1 };
88
+ }
89
+ }
90
+ const intentText = [event.intent, event.tool ?? "", event.scope ?? ""].join(" ");
91
+ let bestStep = null;
92
+ let bestScore = 0;
93
+ for (const step of pendingOrActive) {
94
+ const stepText = [step.label, step.description ?? "", ...step.tags ?? []].join(" ");
95
+ const score = tokenSimilarity2(intentText, stepText);
96
+ if (score > bestScore) {
97
+ bestScore = score;
98
+ bestStep = step;
99
+ }
100
+ }
101
+ const SIMILARITY_THRESHOLD = 0.35;
102
+ if (bestScore >= SIMILARITY_THRESHOLD && bestStep) {
103
+ if (bestStep.tools && event.tool && !bestStep.tools.includes(event.tool)) {
104
+ return { matched: null, closest: bestStep, closestScore: bestScore };
105
+ }
106
+ return { matched: bestStep, closest: bestStep, closestScore: bestScore };
107
+ }
108
+ return { matched: null, closest: bestStep, closestScore: bestScore };
109
+ }
110
+ function isSequenceValid(step, plan) {
111
+ if (!plan.sequential) return true;
112
+ if (!step.requires || step.requires.length === 0) return true;
113
+ return step.requires.every((reqId) => {
114
+ const reqStep = plan.steps.find((s) => s.id === reqId);
115
+ return reqStep?.status === "completed";
116
+ });
117
+ }
118
+ function checkConstraints(event, eventText, constraints) {
119
+ const checks = [];
120
+ for (const constraint of constraints) {
121
+ if (constraint.type === "approval") {
122
+ if (constraint.trigger && eventText.includes(constraint.trigger.substring(0, 10).toLowerCase())) {
123
+ checks.push({ constraintId: constraint.id, passed: false, reason: constraint.description });
124
+ return { violated: constraint, checks };
125
+ }
126
+ const keywords = constraint.description.toLowerCase().split(/\s+/).filter((w) => w.length > 3);
127
+ const relevant = keywords.some((kw) => eventText.includes(kw));
128
+ if (relevant) {
129
+ checks.push({ constraintId: constraint.id, passed: false, reason: constraint.description });
130
+ return { violated: constraint, checks };
131
+ }
132
+ checks.push({ constraintId: constraint.id, passed: true });
133
+ continue;
134
+ }
135
+ if (constraint.type === "scope" && constraint.trigger) {
136
+ const keywords = extractKeywords(constraint.trigger);
137
+ const violated = keywords.length > 0 && keywords.every((kw) => eventText.includes(kw));
138
+ checks.push({
139
+ constraintId: constraint.id,
140
+ passed: !violated,
141
+ reason: violated ? constraint.description : void 0
142
+ });
143
+ if (violated) {
144
+ return { violated: constraint, checks };
145
+ }
146
+ continue;
147
+ }
148
+ checks.push({ constraintId: constraint.id, passed: true });
149
+ }
150
+ return { violated: null, checks };
151
+ }
152
+ function getPlanProgress(plan) {
153
+ const completed = plan.steps.filter((s) => s.status === "completed").length;
154
+ const total = plan.steps.length;
155
+ return {
156
+ completed,
157
+ total,
158
+ percentage: total > 0 ? Math.round(completed / total * 100) : 0
159
+ };
160
+ }
161
+ function evaluatePlan(event, plan) {
162
+ const progress = getPlanProgress(plan);
163
+ if (plan.expires_at) {
164
+ const expiresAt = new Date(plan.expires_at).getTime();
165
+ if (Date.now() > expiresAt) {
166
+ return {
167
+ allowed: true,
168
+ status: "PLAN_COMPLETE",
169
+ reason: "Plan has expired.",
170
+ progress
171
+ };
172
+ }
173
+ }
174
+ if (progress.completed === progress.total) {
175
+ return {
176
+ allowed: true,
177
+ status: "PLAN_COMPLETE",
178
+ reason: "All plan steps are completed.",
179
+ progress
180
+ };
181
+ }
182
+ const eventText = normalizeEventText(event);
183
+ const { matched, closest, closestScore } = findMatchingStep(eventText, event, plan.steps);
184
+ if (!matched) {
185
+ return {
186
+ allowed: false,
187
+ status: "OFF_PLAN",
188
+ reason: "Action does not match any plan step.",
189
+ closestStep: closest?.label,
190
+ similarityScore: closestScore,
191
+ progress
192
+ };
193
+ }
194
+ if (!isSequenceValid(matched, plan)) {
195
+ const pendingDeps = (matched.requires ?? []).filter((reqId) => plan.steps.find((s) => s.id === reqId)?.status !== "completed").join(", ");
196
+ return {
197
+ allowed: false,
198
+ status: "OFF_PLAN",
199
+ reason: `Step "${matched.label}" requires completion of: ${pendingDeps}`,
200
+ matchedStep: matched.id,
201
+ progress
202
+ };
203
+ }
204
+ const { violated } = checkConstraints(event, eventText, plan.constraints);
205
+ if (violated) {
206
+ return {
207
+ allowed: false,
208
+ status: "CONSTRAINT_VIOLATED",
209
+ reason: violated.description,
210
+ matchedStep: matched.id,
211
+ progress
212
+ };
213
+ }
214
+ return {
215
+ allowed: true,
216
+ status: "ON_PLAN",
217
+ reason: `Matches step: ${matched.label}`,
218
+ matchedStep: matched.id,
219
+ progress
220
+ };
221
+ }
222
+ function buildPlanCheck(event, plan, verdict) {
223
+ const eventText = normalizeEventText(event);
224
+ const { matched, closest, closestScore } = findMatchingStep(eventText, event, plan.steps);
225
+ const { checks: constraintChecks } = checkConstraints(event, eventText, plan.constraints);
226
+ const progress = getPlanProgress(plan);
227
+ return {
228
+ planId: plan.plan_id,
229
+ matched: !!matched,
230
+ matchedStepId: matched?.id,
231
+ matchedStepLabel: matched?.label,
232
+ closestStepId: !matched ? closest?.id : void 0,
233
+ closestStepLabel: !matched ? closest?.label : void 0,
234
+ similarityScore: !matched ? closestScore : void 0,
235
+ sequenceValid: matched ? isSequenceValid(matched, plan) : void 0,
236
+ constraintsChecked: constraintChecks,
237
+ progress: { completed: progress.completed, total: progress.total }
238
+ };
239
+ }
240
+
241
+ // src/engine/guard-engine.ts
242
+ var PROMPT_INJECTION_PATTERNS = [
243
+ // Instruction override
244
+ { pattern: /ignore\s+(previous|all|prior|above)\s+(instructions?|rules?)/i, label: "ignore-instructions" },
245
+ { pattern: /disregard\s+(your|the)\s+(rules|constraints)/i, label: "disregard-rules" },
246
+ { pattern: /new\s+instructions?:/i, label: "new-instructions" },
247
+ // Identity manipulation
248
+ { pattern: /you\s+are\s+now/i, label: "identity-override" },
249
+ { pattern: /new\s+persona/i, label: "new-persona" },
250
+ { pattern: /act\s+as\s+if/i, label: "act-as-if" },
251
+ { pattern: /pretend\s+(you|to\s+be|you\s+are\s+unrestricted)/i, label: "pretend-to-be" },
252
+ // Context reset
253
+ { pattern: /forget\s+(everything|all|your)/i, label: "forget-context" },
254
+ { pattern: /system\s*:\s*override/i, label: "system-override" },
255
+ // Constraint bypass
256
+ { pattern: /override\s+(your|the)\s+(programming|constraints)/i, label: "override-constraints" },
257
+ { pattern: /bypass\s+(your|the)\s+(filters|constraints|rules)/i, label: "bypass-filters" },
258
+ // Prompt extraction
259
+ { pattern: /system\s+prompt/i, label: "system-prompt-probe" },
260
+ { pattern: /reveal\s+your\s+(instructions?|prompt|rules)/i, label: "reveal-instructions" },
261
+ // Known jailbreak terms
262
+ { pattern: /jailbreak/i, label: "jailbreak" },
263
+ { pattern: /DAN\s+mode/i, label: "dan-mode" },
264
+ { pattern: /developer\s+mode/i, label: "developer-mode" }
265
+ ];
266
+ var EXECUTION_CLAIM_PATTERNS = [
267
+ { pattern: /I have (executed|completed|performed|done|made|created|sent|deleted|modified|updated)/i, label: "claim-i-have" },
268
+ { pattern: /Successfully (created|deleted|modified|updated|sent|executed|performed)/i, label: "claim-successfully" },
269
+ { pattern: /The file has been/i, label: "claim-file-modified" },
270
+ { pattern: /I've made the changes/i, label: "claim-made-changes" },
271
+ { pattern: /I('ve| have) (sent|posted|submitted|uploaded|downloaded)/i, label: "claim-sent" },
272
+ { pattern: /Your (email|message|file|request) has been (sent|submitted)/i, label: "claim-your-sent" },
273
+ { pattern: /Transaction complete/i, label: "claim-transaction" },
274
+ { pattern: /Order placed/i, label: "claim-order" },
275
+ { pattern: /Payment processed/i, label: "claim-payment" }
276
+ ];
277
+ var EXECUTION_INTENT_PATTERNS = [
278
+ { pattern: /^(execute|run|perform|do this)/i, label: "intent-execute" },
279
+ { pattern: /^(create|write|delete|modify) (a |the )?(file|folder|document)/i, label: "intent-file-ops" },
280
+ { pattern: /^(send|post|submit) (a |an |the )?(email|message|tweet|post)/i, label: "intent-send" },
281
+ { pattern: /^(search|look up|browse) (the )?web/i, label: "intent-web-search" },
282
+ { pattern: /^(make|call|invoke) (a |an )?(api|http|rest) (call|request)/i, label: "intent-api-call" },
283
+ { pattern: /^(buy|purchase|order|pay|transfer|send money)/i, label: "intent-financial" },
284
+ { pattern: /^(book|schedule|reserve)/i, label: "intent-booking" },
285
+ { pattern: /^(download|upload|save to|export to)/i, label: "intent-transfer" }
286
+ ];
287
+ var SCOPE_ESCAPE_PATTERNS = [
288
+ { pattern: /\.\.\//, label: "parent-traversal" },
289
+ { pattern: /^\/(?!home|project|workspace)/i, label: "absolute-path-outside-safe" },
290
+ { pattern: /~\//, label: "home-directory" },
291
+ { pattern: /\/etc\//i, label: "system-config" },
292
+ { pattern: /\/usr\//i, label: "system-binaries" },
293
+ { pattern: /\/var\//i, label: "system-variable-data" }
294
+ ];
295
+ var NEUTRAL_MESSAGES = {
296
+ "prompt-injection": "This input contains patterns that could alter agent behavior.",
297
+ "scope-escape": "This action would affect resources outside the declared scope.",
298
+ "execution-claim": "This response claims to have performed an action.",
299
+ "execution-intent": "This input requests execution in a thinking-only environment.",
300
+ "delete": "This action would remove files. Confirmation needed.",
301
+ "write-external": "This action would write outside the project folder.",
302
+ "network-mutate": "This action would send data to an external service.",
303
+ "credential-access": "This action would access stored credentials."
304
+ };
305
+ function levelRequiresConfirmation(level, actionType) {
306
+ if (level === "strict") return true;
307
+ if (level === "standard") {
308
+ return actionType === "delete" || actionType === "credential-access";
309
+ }
310
+ return false;
311
+ }
312
+ function isExternalScope(scope) {
313
+ const internalPatterns = [
314
+ /^\.?\/?src\//i,
315
+ /^\.?\/?lib\//i,
316
+ /^\.?\/?app\//i,
317
+ /^\.?\/?components\//i,
318
+ /^\.?\/?pages\//i,
319
+ /^\.?\/?public\//i,
320
+ /^\.?\/?assets\//i,
321
+ /^\.\//
322
+ ];
323
+ return !internalPatterns.some((p) => p.test(scope));
324
+ }
325
+ var MAX_INPUT_LENGTH = 1e5;
326
+ function evaluateGuard(event, world, options = {}) {
327
+ const startTime = performance.now();
328
+ const level = options.level ?? "standard";
329
+ const includeTrace = options.trace ?? false;
330
+ if (!event.intent || typeof event.intent !== "string") {
331
+ return {
332
+ status: "BLOCK",
333
+ reason: "GuardEvent.intent is required and must be a string",
334
+ ruleId: "safety-input-validation",
335
+ evidence: {
336
+ worldId: world.world?.world_id ?? "",
337
+ worldName: world.world?.name ?? "",
338
+ worldVersion: world.world?.version ?? "",
339
+ evaluatedAt: Date.now(),
340
+ invariantsSatisfied: 0,
341
+ invariantsTotal: 0,
342
+ guardsMatched: [],
343
+ rulesMatched: [],
344
+ enforcementLevel: level
345
+ }
346
+ };
347
+ }
348
+ const inputLength = event.intent.length + (event.tool?.length ?? 0) + (event.scope?.length ?? 0) + (event.payload ? JSON.stringify(event.payload).length : 0);
349
+ if (inputLength > MAX_INPUT_LENGTH) {
350
+ return {
351
+ status: "BLOCK",
352
+ reason: `Input exceeds maximum allowed length (${MAX_INPUT_LENGTH} characters)`,
353
+ ruleId: "safety-input-length",
354
+ evidence: {
355
+ worldId: world.world?.world_id ?? "",
356
+ worldName: world.world?.name ?? "",
357
+ worldVersion: world.world?.version ?? "",
358
+ evaluatedAt: Date.now(),
359
+ invariantsSatisfied: 0,
360
+ invariantsTotal: 0,
361
+ guardsMatched: [],
362
+ rulesMatched: [],
363
+ enforcementLevel: level
364
+ }
365
+ };
366
+ }
367
+ const eventText = normalizeEventText(event);
368
+ const invariantChecks = [];
369
+ const safetyChecks = [];
370
+ let planCheckResult;
371
+ const roleChecks = [];
372
+ const guardChecks = [];
373
+ const kernelRuleChecks = [];
374
+ const levelChecks = [];
375
+ let decidingLayer = "default-allow";
376
+ let decidingId;
377
+ const guardsMatched = [];
378
+ const rulesMatched = [];
379
+ if (options.emergencyOverride) {
380
+ checkInvariantCoverage(world, invariantChecks);
381
+ return buildVerdict(
382
+ "ALLOW",
383
+ void 0,
384
+ "emergency-override",
385
+ "Emergency override active \u2014 all governance rules suspended. Platform constraints still apply.",
386
+ world,
387
+ level,
388
+ invariantChecks,
389
+ guardsMatched,
390
+ rulesMatched,
391
+ includeTrace ? buildTrace(
392
+ invariantChecks,
393
+ safetyChecks,
394
+ planCheckResult,
395
+ roleChecks,
396
+ guardChecks,
397
+ kernelRuleChecks,
398
+ levelChecks,
399
+ "session-allowlist",
400
+ "emergency-override",
401
+ startTime
402
+ ) : void 0,
403
+ event.intent
404
+ );
405
+ }
406
+ checkInvariantCoverage(world, invariantChecks);
407
+ if (event.roleId && options.agentStates) {
408
+ const agentState = options.agentStates.get(event.roleId);
409
+ if (agentState && agentState.cooldownRemaining > 0) {
410
+ decidingLayer = "safety";
411
+ decidingId = `penalize-cooldown-${event.roleId}`;
412
+ const verdict = buildVerdict(
413
+ "PENALIZE",
414
+ `Agent "${event.roleId}" is frozen for ${agentState.cooldownRemaining} more round(s) due to prior penalty.`,
415
+ `penalize-cooldown-${event.roleId}`,
416
+ void 0,
417
+ world,
418
+ level,
419
+ invariantChecks,
420
+ guardsMatched,
421
+ rulesMatched,
422
+ includeTrace ? buildTrace(
423
+ invariantChecks,
424
+ safetyChecks,
425
+ planCheckResult,
426
+ roleChecks,
427
+ guardChecks,
428
+ kernelRuleChecks,
429
+ levelChecks,
430
+ decidingLayer,
431
+ decidingId,
432
+ startTime
433
+ ) : void 0
434
+ );
435
+ verdict.intentRecord = {
436
+ originalIntent: event.intent,
437
+ finalAction: "blocked (agent frozen)",
438
+ enforcement: "PENALIZE",
439
+ consequence: { type: "freeze", rounds: agentState.cooldownRemaining, description: "Agent still in cooldown from prior penalty" }
440
+ };
441
+ return verdict;
442
+ }
443
+ }
444
+ if (options.sessionAllowlist) {
445
+ const key = eventToAllowlistKey(event);
446
+ if (options.sessionAllowlist.has(key)) {
447
+ decidingLayer = "session-allowlist";
448
+ decidingId = `allowlist:${key}`;
449
+ return buildVerdict(
450
+ "ALLOW",
451
+ void 0,
452
+ `allowlist:${key}`,
453
+ void 0,
454
+ world,
455
+ level,
456
+ invariantChecks,
457
+ guardsMatched,
458
+ rulesMatched,
459
+ includeTrace ? buildTrace(
460
+ invariantChecks,
461
+ safetyChecks,
462
+ planCheckResult,
463
+ roleChecks,
464
+ guardChecks,
465
+ kernelRuleChecks,
466
+ levelChecks,
467
+ decidingLayer,
468
+ decidingId,
469
+ startTime
470
+ ) : void 0,
471
+ event.intent
472
+ );
473
+ }
474
+ }
475
+ const safetyVerdict = checkSafety(event, eventText, safetyChecks);
476
+ if (safetyVerdict) {
477
+ decidingLayer = "safety";
478
+ decidingId = safetyVerdict.ruleId;
479
+ return buildVerdict(
480
+ safetyVerdict.status,
481
+ safetyVerdict.reason,
482
+ safetyVerdict.ruleId,
483
+ void 0,
484
+ world,
485
+ level,
486
+ invariantChecks,
487
+ guardsMatched,
488
+ rulesMatched,
489
+ includeTrace ? buildTrace(
490
+ invariantChecks,
491
+ safetyChecks,
492
+ planCheckResult,
493
+ roleChecks,
494
+ guardChecks,
495
+ kernelRuleChecks,
496
+ levelChecks,
497
+ decidingLayer,
498
+ decidingId,
499
+ startTime
500
+ ) : void 0,
501
+ event.intent
502
+ );
503
+ }
504
+ if (options.plan) {
505
+ const planVerdict = evaluatePlan(event, options.plan);
506
+ planCheckResult = buildPlanCheck(event, options.plan, planVerdict);
507
+ if (!planVerdict.allowed && planVerdict.status !== "PLAN_COMPLETE") {
508
+ decidingLayer = "plan-enforcement";
509
+ decidingId = `plan-${options.plan.plan_id}`;
510
+ const planStatus = planVerdict.status === "CONSTRAINT_VIOLATED" ? "PAUSE" : "BLOCK";
511
+ let reason = planVerdict.reason ?? "Action blocked by plan.";
512
+ if (planVerdict.status === "OFF_PLAN" && planVerdict.closestStep) {
513
+ reason += ` Closest step: "${planVerdict.closestStep}" (similarity: ${(planVerdict.similarityScore ?? 0).toFixed(2)})`;
514
+ }
515
+ return buildVerdict(
516
+ planStatus,
517
+ reason,
518
+ `plan-${options.plan.plan_id}`,
519
+ void 0,
520
+ world,
521
+ level,
522
+ invariantChecks,
523
+ guardsMatched,
524
+ rulesMatched,
525
+ includeTrace ? buildTrace(
526
+ invariantChecks,
527
+ safetyChecks,
528
+ planCheckResult,
529
+ roleChecks,
530
+ guardChecks,
531
+ kernelRuleChecks,
532
+ levelChecks,
533
+ decidingLayer,
534
+ decidingId,
535
+ startTime
536
+ ) : void 0,
537
+ event.intent
538
+ );
539
+ }
540
+ }
541
+ const roleVerdict = checkRoleRules(event, eventText, world, roleChecks);
542
+ if (roleVerdict) {
543
+ decidingLayer = "role";
544
+ decidingId = roleVerdict.ruleId;
545
+ return buildVerdict(
546
+ roleVerdict.status,
547
+ roleVerdict.reason,
548
+ roleVerdict.ruleId,
549
+ void 0,
550
+ world,
551
+ level,
552
+ invariantChecks,
553
+ guardsMatched,
554
+ rulesMatched,
555
+ includeTrace ? buildTrace(
556
+ invariantChecks,
557
+ safetyChecks,
558
+ planCheckResult,
559
+ roleChecks,
560
+ guardChecks,
561
+ kernelRuleChecks,
562
+ levelChecks,
563
+ decidingLayer,
564
+ decidingId,
565
+ startTime
566
+ ) : void 0,
567
+ event.intent
568
+ );
569
+ }
570
+ const guardVerdict = checkGuards(event, eventText, world, guardChecks, guardsMatched);
571
+ if (guardVerdict) {
572
+ if (guardVerdict.status !== "ALLOW") {
573
+ decidingLayer = "guard";
574
+ decidingId = guardVerdict.ruleId;
575
+ const intentRecord = {
576
+ originalIntent: event.intent,
577
+ finalAction: guardVerdict.status === "MODIFY" ? guardVerdict.modifiedTo ?? "modified" : guardVerdict.status === "PENALIZE" ? "blocked + penalized" : guardVerdict.status === "REWARD" ? event.intent : guardVerdict.status === "NEUTRAL" ? event.intent : guardVerdict.status === "BLOCK" ? "blocked" : "paused",
578
+ ruleApplied: guardVerdict.ruleId,
579
+ enforcement: guardVerdict.status,
580
+ modifiedTo: guardVerdict.modifiedTo,
581
+ consequence: guardVerdict.consequence,
582
+ reward: guardVerdict.reward
583
+ };
584
+ const verdict = buildVerdict(
585
+ guardVerdict.status,
586
+ guardVerdict.reason,
587
+ guardVerdict.ruleId,
588
+ void 0,
589
+ world,
590
+ level,
591
+ invariantChecks,
592
+ guardsMatched,
593
+ rulesMatched,
594
+ includeTrace ? buildTrace(
595
+ invariantChecks,
596
+ safetyChecks,
597
+ planCheckResult,
598
+ roleChecks,
599
+ guardChecks,
600
+ kernelRuleChecks,
601
+ levelChecks,
602
+ decidingLayer,
603
+ decidingId,
604
+ startTime
605
+ ) : void 0,
606
+ event.intent
607
+ );
608
+ verdict.intentRecord = intentRecord;
609
+ if (guardVerdict.consequence) verdict.consequence = guardVerdict.consequence;
610
+ if (guardVerdict.reward) verdict.reward = guardVerdict.reward;
611
+ return verdict;
612
+ }
613
+ }
614
+ const kernelVerdict = checkKernelRules(eventText, world, kernelRuleChecks, rulesMatched);
615
+ if (kernelVerdict) {
616
+ decidingLayer = "kernel-rule";
617
+ decidingId = kernelVerdict.ruleId;
618
+ return buildVerdict(
619
+ kernelVerdict.status,
620
+ kernelVerdict.reason,
621
+ kernelVerdict.ruleId,
622
+ void 0,
623
+ world,
624
+ level,
625
+ invariantChecks,
626
+ guardsMatched,
627
+ rulesMatched,
628
+ includeTrace ? buildTrace(
629
+ invariantChecks,
630
+ safetyChecks,
631
+ planCheckResult,
632
+ roleChecks,
633
+ guardChecks,
634
+ kernelRuleChecks,
635
+ levelChecks,
636
+ decidingLayer,
637
+ decidingId,
638
+ startTime
639
+ ) : void 0,
640
+ event.intent
641
+ );
642
+ }
643
+ const levelVerdict = checkLevelConstraints(event, level, levelChecks);
644
+ if (levelVerdict) {
645
+ decidingLayer = "level-constraint";
646
+ decidingId = levelVerdict.ruleId;
647
+ return buildVerdict(
648
+ levelVerdict.status,
649
+ levelVerdict.reason,
650
+ levelVerdict.ruleId,
651
+ void 0,
652
+ world,
653
+ level,
654
+ invariantChecks,
655
+ guardsMatched,
656
+ rulesMatched,
657
+ includeTrace ? buildTrace(
658
+ invariantChecks,
659
+ safetyChecks,
660
+ planCheckResult,
661
+ roleChecks,
662
+ guardChecks,
663
+ kernelRuleChecks,
664
+ levelChecks,
665
+ decidingLayer,
666
+ decidingId,
667
+ startTime
668
+ ) : void 0,
669
+ event.intent
670
+ );
671
+ }
672
+ const warning = guardVerdict?.warning;
673
+ return buildVerdict(
674
+ "ALLOW",
675
+ void 0,
676
+ void 0,
677
+ warning,
678
+ world,
679
+ level,
680
+ invariantChecks,
681
+ guardsMatched,
682
+ rulesMatched,
683
+ includeTrace ? buildTrace(
684
+ invariantChecks,
685
+ safetyChecks,
686
+ planCheckResult,
687
+ roleChecks,
688
+ guardChecks,
689
+ kernelRuleChecks,
690
+ levelChecks,
691
+ decidingLayer,
692
+ decidingId,
693
+ startTime
694
+ ) : void 0,
695
+ event.intent
696
+ );
697
+ }
698
+ function checkInvariantCoverage(world, checks) {
699
+ const invariants = world.invariants ?? [];
700
+ const guards = world.guards?.guards ?? [];
701
+ for (const invariant of invariants) {
702
+ const coveringGuard = guards.find(
703
+ (g) => g.invariant_ref === invariant.id && g.immutable
704
+ );
705
+ checks.push({
706
+ invariantId: invariant.id,
707
+ label: invariant.label,
708
+ hasGuardCoverage: !!coveringGuard,
709
+ coveringGuardId: coveringGuard?.id
710
+ });
711
+ }
712
+ }
713
+ function checkSafety(event, eventText, checks) {
714
+ const textToCheck = event.intent + (event.payload ? JSON.stringify(event.payload) : "");
715
+ for (const { pattern, label } of PROMPT_INJECTION_PATTERNS) {
716
+ const triggered = pattern.test(textToCheck);
717
+ checks.push({
718
+ checkType: "prompt-injection",
719
+ triggered,
720
+ matchedPattern: triggered ? label : void 0
721
+ });
722
+ if (triggered) {
723
+ for (const remaining of PROMPT_INJECTION_PATTERNS.filter((p) => p.label !== label)) {
724
+ checks.push({
725
+ checkType: "prompt-injection",
726
+ triggered: remaining.pattern.test(textToCheck),
727
+ matchedPattern: remaining.pattern.test(textToCheck) ? remaining.label : void 0
728
+ });
729
+ }
730
+ return {
731
+ status: "PAUSE",
732
+ reason: NEUTRAL_MESSAGES["prompt-injection"],
733
+ ruleId: `safety-injection-${label}`
734
+ };
735
+ }
736
+ }
737
+ const scopeToCheck = event.scope ?? event.intent;
738
+ for (const { pattern, label } of SCOPE_ESCAPE_PATTERNS) {
739
+ const triggered = pattern.test(scopeToCheck);
740
+ checks.push({
741
+ checkType: "scope-escape",
742
+ triggered,
743
+ matchedPattern: triggered ? label : void 0
744
+ });
745
+ if (triggered) {
746
+ for (const remaining of SCOPE_ESCAPE_PATTERNS.filter((p) => p.label !== label)) {
747
+ checks.push({
748
+ checkType: "scope-escape",
749
+ triggered: remaining.pattern.test(scopeToCheck),
750
+ matchedPattern: remaining.pattern.test(scopeToCheck) ? remaining.label : void 0
751
+ });
752
+ }
753
+ return {
754
+ status: "PAUSE",
755
+ reason: NEUTRAL_MESSAGES["scope-escape"],
756
+ ruleId: `safety-scope-${label}`
757
+ };
758
+ }
759
+ }
760
+ if (event.direction === "output") {
761
+ for (const { pattern, label } of EXECUTION_CLAIM_PATTERNS) {
762
+ const triggered = pattern.test(textToCheck);
763
+ checks.push({
764
+ checkType: "execution-claim",
765
+ triggered,
766
+ matchedPattern: triggered ? label : void 0
767
+ });
768
+ if (triggered) {
769
+ for (const remaining of EXECUTION_CLAIM_PATTERNS.filter((p) => p.label !== label)) {
770
+ checks.push({
771
+ checkType: "execution-claim",
772
+ triggered: remaining.pattern.test(textToCheck),
773
+ matchedPattern: remaining.pattern.test(textToCheck) ? remaining.label : void 0
774
+ });
775
+ }
776
+ return {
777
+ status: "PAUSE",
778
+ reason: NEUTRAL_MESSAGES["execution-claim"],
779
+ ruleId: `safety-execution-claim-${label}`
780
+ };
781
+ }
782
+ }
783
+ }
784
+ if (event.direction === "input") {
785
+ const intentTrimmed = event.intent.trim();
786
+ for (const { pattern, label } of EXECUTION_INTENT_PATTERNS) {
787
+ const triggered = pattern.test(intentTrimmed);
788
+ checks.push({
789
+ checkType: "execution-intent",
790
+ triggered,
791
+ matchedPattern: triggered ? label : void 0
792
+ });
793
+ if (triggered) {
794
+ for (const remaining of EXECUTION_INTENT_PATTERNS.filter((p) => p.label !== label)) {
795
+ checks.push({
796
+ checkType: "execution-intent",
797
+ triggered: remaining.pattern.test(intentTrimmed),
798
+ matchedPattern: remaining.pattern.test(intentTrimmed) ? remaining.label : void 0
799
+ });
800
+ }
801
+ return {
802
+ status: "PAUSE",
803
+ reason: NEUTRAL_MESSAGES["execution-intent"],
804
+ ruleId: `safety-execution-intent-${label}`
805
+ };
806
+ }
807
+ }
808
+ }
809
+ return null;
810
+ }
811
+ function checkRoleRules(event, eventText, world, checks) {
812
+ if (!event.roleId || !world.roles) return null;
813
+ const role = world.roles.roles.find((r) => r.id === event.roleId);
814
+ if (!role) return null;
815
+ if (role.requiresApproval) {
816
+ checks.push({
817
+ roleId: role.id,
818
+ roleName: role.name,
819
+ rule: "All actions require approval",
820
+ ruleType: "requiresApproval",
821
+ matched: true
822
+ });
823
+ return {
824
+ status: "PAUSE",
825
+ reason: `Role "${role.name}" requires approval for all actions.`,
826
+ ruleId: `role-${role.id}-requires-approval`
827
+ };
828
+ }
829
+ for (const rule of role.cannotDo) {
830
+ const matched = matchesKeywords(eventText, rule);
831
+ checks.push({
832
+ roleId: role.id,
833
+ roleName: role.name,
834
+ rule,
835
+ ruleType: "cannotDo",
836
+ matched
837
+ });
838
+ if (matched) {
839
+ return {
840
+ status: "BLOCK",
841
+ reason: `Role "${role.name}" cannot: ${rule}`,
842
+ ruleId: `role-${role.id}-cannotdo`
843
+ };
844
+ }
845
+ }
846
+ for (const rule of role.canDo) {
847
+ checks.push({
848
+ roleId: role.id,
849
+ roleName: role.name,
850
+ rule,
851
+ ruleType: "canDo",
852
+ matched: matchesKeywords(eventText, rule)
853
+ });
854
+ }
855
+ return null;
856
+ }
857
+ function checkGuards(event, eventText, world, checks, guardsMatched) {
858
+ if (!world.guards) return null;
859
+ const guardsConfig = world.guards;
860
+ let warnResult = null;
861
+ const compiledPatterns = /* @__PURE__ */ new Map();
862
+ for (const [key, def] of Object.entries(guardsConfig.intent_vocabulary)) {
863
+ try {
864
+ compiledPatterns.set(key, new RegExp(def.pattern, "i"));
865
+ } catch {
866
+ }
867
+ }
868
+ const eventTool = (event.tool ?? "").toLowerCase();
869
+ for (const guard of guardsConfig.guards) {
870
+ if (guard.appliesTo && guard.appliesTo.length > 0) {
871
+ const normalizedAppliesTo = guard.appliesTo.map((t) => t.toLowerCase());
872
+ if (!normalizedAppliesTo.includes(eventTool)) {
873
+ continue;
874
+ }
875
+ }
876
+ const enabled = guard.immutable || guard.default_enabled !== false;
877
+ const matchedPatterns = [];
878
+ for (const patternKey of guard.intent_patterns) {
879
+ const regex = compiledPatterns.get(patternKey);
880
+ if (regex?.test(eventText)) {
881
+ matchedPatterns.push(patternKey);
882
+ }
883
+ }
884
+ const matched = matchedPatterns.length > 0 && enabled;
885
+ let roleGated = false;
886
+ if (matched && guard.required_roles && guard.required_roles.length > 0 && event.roleId && guard.required_roles.includes(event.roleId)) {
887
+ roleGated = true;
888
+ }
889
+ checks.push({
890
+ guardId: guard.id,
891
+ label: guard.label,
892
+ category: guard.category,
893
+ enabled,
894
+ matched: matched && !roleGated,
895
+ enforcement: guard.enforcement,
896
+ matchedPatterns,
897
+ roleGated
898
+ });
899
+ if (!matched || roleGated) continue;
900
+ guardsMatched.push(guard.id);
901
+ const actionMode = guard.player_modes?.action ?? guard.enforcement;
902
+ const reason = guard.redirect ? `${guard.description} \u2014 ${guard.redirect}` : guard.description;
903
+ if (actionMode === "block") {
904
+ return { status: "BLOCK", reason, ruleId: `guard-${guard.id}` };
905
+ }
906
+ if (actionMode === "pause") {
907
+ return { status: "PAUSE", reason, ruleId: `guard-${guard.id}` };
908
+ }
909
+ if (actionMode === "penalize") {
910
+ const consequence = guard.consequence ? { ...guard.consequence } : { type: "freeze", rounds: 1, description: `Penalized for violating: ${guard.label}` };
911
+ return { status: "PENALIZE", reason, ruleId: `guard-${guard.id}`, consequence };
912
+ }
913
+ if (actionMode === "reward") {
914
+ const reward = guard.reward ? { ...guard.reward } : { type: "boost_influence", magnitude: 0.1, description: `Rewarded for: ${guard.label}` };
915
+ return { status: "REWARD", reason, ruleId: `guard-${guard.id}`, reward };
916
+ }
917
+ if (actionMode === "modify") {
918
+ const modifiedTo = guard.modify_to ?? guard.redirect ?? "hold";
919
+ return { status: "MODIFY", reason: `${reason} \u2192 Modified to: ${modifiedTo}`, ruleId: `guard-${guard.id}`, modifiedTo };
920
+ }
921
+ if (actionMode === "neutral") {
922
+ return { status: "NEUTRAL", reason, ruleId: `guard-${guard.id}` };
923
+ }
924
+ if (actionMode === "warn" && !warnResult) {
925
+ warnResult = { status: "ALLOW", warning: reason, ruleId: `guard-${guard.id}` };
926
+ }
927
+ }
928
+ return warnResult;
929
+ }
930
+ function checkKernelRules(eventText, world, checks, rulesMatched) {
931
+ if (!world.kernel) return null;
932
+ const forbidden = world.kernel.input_boundaries?.forbidden_patterns ?? [];
933
+ const output = world.kernel.output_boundaries?.forbidden_patterns ?? [];
934
+ for (const rule of forbidden) {
935
+ let matched = false;
936
+ let matchMethod = "none";
937
+ if (rule.pattern) {
938
+ try {
939
+ matched = new RegExp(rule.pattern, "i").test(eventText);
940
+ matchMethod = "pattern";
941
+ } catch {
942
+ }
943
+ }
944
+ if (!matched && rule.reason) {
945
+ matched = matchesKeywords(eventText, rule.reason);
946
+ if (matched) matchMethod = "keyword";
947
+ }
948
+ checks.push({
949
+ ruleId: rule.id,
950
+ text: rule.reason,
951
+ category: "forbidden",
952
+ matched,
953
+ matchMethod
954
+ });
955
+ if (matched) {
956
+ rulesMatched.push(rule.id);
957
+ if (rule.action === "BLOCK") {
958
+ return {
959
+ status: "BLOCK",
960
+ reason: rule.reason,
961
+ ruleId: `kernel-${rule.id}`
962
+ };
963
+ }
964
+ }
965
+ }
966
+ return null;
967
+ }
968
+ function checkLevelConstraints(event, level, checks) {
969
+ if (level === "basic") return null;
970
+ const intent = event.intent.toLowerCase();
971
+ const tool = (event.tool ?? "").toLowerCase();
972
+ const isDelete = intent.includes("delete") || intent.includes("remove") || intent.includes("rm ") || tool === "delete";
973
+ const deleteTriggered = isDelete && levelRequiresConfirmation(level, "delete");
974
+ checks.push({
975
+ checkType: "delete",
976
+ level,
977
+ triggered: deleteTriggered,
978
+ reason: deleteTriggered ? NEUTRAL_MESSAGES["delete"] : void 0
979
+ });
980
+ if (deleteTriggered) {
981
+ return { status: "PAUSE", reason: NEUTRAL_MESSAGES["delete"], ruleId: "level-delete-check" };
982
+ }
983
+ const isExternal = event.scope ? isExternalScope(event.scope) : false;
984
+ const externalTriggered = isExternal && levelRequiresConfirmation(level, "write-external");
985
+ checks.push({
986
+ checkType: "write-external",
987
+ level,
988
+ triggered: externalTriggered,
989
+ reason: externalTriggered ? NEUTRAL_MESSAGES["write-external"] : void 0
990
+ });
991
+ if (externalTriggered) {
992
+ return { status: "PAUSE", reason: NEUTRAL_MESSAGES["write-external"], ruleId: "level-external-write-check" };
993
+ }
994
+ const isNetwork = tool === "http" || tool === "fetch" || tool === "request" || intent.includes("post ") || intent.includes("sending");
995
+ const networkTriggered = isNetwork && levelRequiresConfirmation(level, "network-mutate");
996
+ checks.push({
997
+ checkType: "network-mutate",
998
+ level,
999
+ triggered: networkTriggered,
1000
+ reason: networkTriggered ? NEUTRAL_MESSAGES["network-mutate"] : void 0
1001
+ });
1002
+ if (networkTriggered) {
1003
+ return { status: "PAUSE", reason: NEUTRAL_MESSAGES["network-mutate"], ruleId: "level-network-mutate-check" };
1004
+ }
1005
+ const isCredential = intent.includes("credential") || intent.includes("password") || intent.includes("secret") || intent.includes("api key") || intent.includes("token");
1006
+ const credentialTriggered = isCredential && levelRequiresConfirmation(level, "credential-access");
1007
+ checks.push({
1008
+ checkType: "credential-access",
1009
+ level,
1010
+ triggered: credentialTriggered,
1011
+ reason: credentialTriggered ? NEUTRAL_MESSAGES["credential-access"] : void 0
1012
+ });
1013
+ if (credentialTriggered) {
1014
+ return { status: "PAUSE", reason: NEUTRAL_MESSAGES["credential-access"], ruleId: "level-credential-check" };
1015
+ }
1016
+ const irreversibleTriggered = !!event.irreversible && level !== "basic";
1017
+ checks.push({
1018
+ checkType: "irreversible",
1019
+ level,
1020
+ triggered: irreversibleTriggered,
1021
+ reason: irreversibleTriggered ? "This action is marked as irreversible." : void 0
1022
+ });
1023
+ if (irreversibleTriggered) {
1024
+ return {
1025
+ status: "PAUSE",
1026
+ reason: "This action is marked as irreversible.",
1027
+ ruleId: "level-irreversible-check"
1028
+ };
1029
+ }
1030
+ return null;
1031
+ }
1032
+ function matchesKeywords(eventText, ruleText) {
1033
+ return matchesAllKeywords(eventText, ruleText);
1034
+ }
1035
+ function eventToAllowlistKey(event) {
1036
+ return `${(event.tool ?? "*").toLowerCase()}::${event.intent.toLowerCase().trim()}`;
1037
+ }
1038
+ function buildTrace(invariantChecks, safetyChecks, planCheck, roleChecks, guardChecks, kernelRuleChecks, levelChecks, decidingLayer, decidingId, startTime) {
1039
+ const trace = {
1040
+ invariantChecks,
1041
+ safetyChecks,
1042
+ roleChecks,
1043
+ guardChecks,
1044
+ kernelRuleChecks,
1045
+ levelChecks,
1046
+ precedenceResolution: {
1047
+ decidingLayer,
1048
+ decidingId,
1049
+ strategy: "first-match-wins",
1050
+ chainOrder: [
1051
+ "invariant-coverage",
1052
+ "session-allowlist",
1053
+ "safety-injection",
1054
+ "safety-scope-escape",
1055
+ "safety-execution-claim",
1056
+ "safety-execution-intent",
1057
+ "plan-enforcement",
1058
+ "role-rules",
1059
+ "declarative-guards",
1060
+ "kernel-rules",
1061
+ "level-constraints",
1062
+ "default-allow"
1063
+ ]
1064
+ },
1065
+ durationMs: performance.now() - startTime
1066
+ };
1067
+ if (planCheck) {
1068
+ trace.planCheck = planCheck;
1069
+ }
1070
+ return trace;
1071
+ }
1072
+ function buildVerdict(status, reason, ruleId, warning, world, level, invariantChecks, guardsMatched, rulesMatched, trace, eventIntent) {
1073
+ const evidence = {
1074
+ worldId: world.world.world_id,
1075
+ worldName: world.world.name,
1076
+ worldVersion: world.world.version,
1077
+ evaluatedAt: Date.now(),
1078
+ invariantsSatisfied: invariantChecks.filter((c) => c.hasGuardCoverage).length,
1079
+ invariantsTotal: invariantChecks.length,
1080
+ guardsMatched,
1081
+ rulesMatched,
1082
+ enforcementLevel: level
1083
+ };
1084
+ const verdict = {
1085
+ status,
1086
+ evidence
1087
+ };
1088
+ if (reason) verdict.reason = reason;
1089
+ if (ruleId) verdict.ruleId = ruleId;
1090
+ if (warning) verdict.warning = warning;
1091
+ if (trace) verdict.trace = trace;
1092
+ verdict.event = verdictToEvent(status, eventIntent);
1093
+ return verdict;
1094
+ }
1095
+ function verdictToEvent(status, intent) {
1096
+ const statusEventMap = {
1097
+ ALLOW: "action_allowed",
1098
+ BLOCK: "action_blocked",
1099
+ PAUSE: "action_paused",
1100
+ MODIFY: "action_modified",
1101
+ PENALIZE: "action_penalized",
1102
+ REWARD: "action_rewarded",
1103
+ NEUTRAL: "action_neutral"
1104
+ };
1105
+ return {
1106
+ type: intent || statusEventMap[status] || "unknown_action",
1107
+ actor: "agent",
1108
+ source: "guard",
1109
+ timestamp: Date.now(),
1110
+ guardStatus: status
1111
+ };
1112
+ }
1113
+
1114
+ // src/worlds/mentraos-intent-taxonomy.ts
1115
+ var MENTRA_INTENT_TAXONOMY = [
1116
+ // ── Camera Domain ───────────────────────────────────────────────────────
1117
+ {
1118
+ intent: "camera_photo_capture",
1119
+ description: "Capture a single photo from the glasses camera",
1120
+ sdk_method: "session.camera.requestPhoto()",
1121
+ permission: "CAMERA",
1122
+ domain: "camera",
1123
+ supported_glasses: ["mentra_live"],
1124
+ action_category: "write",
1125
+ base_risk: "high",
1126
+ exfiltration_risk: true,
1127
+ reversible: false
1128
+ },
1129
+ {
1130
+ intent: "camera_stream_start",
1131
+ description: "Start a managed video stream (HLS) from the glasses camera",
1132
+ sdk_method: "session.camera.startManagedStream()",
1133
+ permission: "CAMERA",
1134
+ domain: "camera",
1135
+ supported_glasses: ["mentra_live"],
1136
+ action_category: "write",
1137
+ base_risk: "critical",
1138
+ exfiltration_risk: true,
1139
+ reversible: true
1140
+ },
1141
+ {
1142
+ intent: "camera_stream_stop",
1143
+ description: "Stop an active camera stream",
1144
+ sdk_method: "session.camera.stopStream()",
1145
+ permission: "CAMERA",
1146
+ domain: "camera",
1147
+ supported_glasses: ["mentra_live"],
1148
+ action_category: "write",
1149
+ base_risk: "low",
1150
+ exfiltration_risk: false,
1151
+ reversible: false
1152
+ },
1153
+ {
1154
+ intent: "camera_restream_start",
1155
+ description: "Restream camera feed to an external RTMP destination (e.g., social media)",
1156
+ sdk_method: "session.camera.startRestream()",
1157
+ permission: "CAMERA",
1158
+ domain: "camera",
1159
+ supported_glasses: ["mentra_live"],
1160
+ action_category: "network",
1161
+ base_risk: "critical",
1162
+ exfiltration_risk: true,
1163
+ reversible: true
1164
+ },
1165
+ // ── Microphone Domain ──────────────────────────────────────────────────
1166
+ {
1167
+ intent: "microphone_transcription_start",
1168
+ description: "Start receiving speech-to-text transcription events",
1169
+ sdk_method: "session.events.onTranscription()",
1170
+ permission: "MICROPHONE",
1171
+ domain: "microphone",
1172
+ supported_glasses: ["even_realities_g1", "mentra_live"],
1173
+ action_category: "read",
1174
+ base_risk: "medium",
1175
+ exfiltration_risk: true,
1176
+ reversible: true
1177
+ },
1178
+ {
1179
+ intent: "microphone_translation_start",
1180
+ description: "Start receiving translation events from spoken audio",
1181
+ sdk_method: "session.events.onTranslation()",
1182
+ permission: "MICROPHONE",
1183
+ domain: "microphone",
1184
+ supported_glasses: ["even_realities_g1", "mentra_live"],
1185
+ action_category: "read",
1186
+ base_risk: "medium",
1187
+ exfiltration_risk: true,
1188
+ reversible: true
1189
+ },
1190
+ {
1191
+ intent: "microphone_phone_passthrough",
1192
+ description: "Use phone microphone as audio input (glasses without built-in mic)",
1193
+ sdk_method: "session.audio.startPhoneMic()",
1194
+ permission: "MICROPHONE",
1195
+ domain: "microphone",
1196
+ supported_glasses: ["mentra_mach1", "vuzix_z100"],
1197
+ action_category: "read",
1198
+ base_risk: "medium",
1199
+ exfiltration_risk: true,
1200
+ reversible: true
1201
+ },
1202
+ // ── Display Domain ────────────────────────────────────────────────────
1203
+ {
1204
+ intent: "display_text_wall",
1205
+ description: "Show a single text block on the glasses display",
1206
+ sdk_method: "session.layouts.showTextWall()",
1207
+ permission: "NONE",
1208
+ domain: "display",
1209
+ supported_glasses: ["even_realities_g1", "mentra_mach1", "vuzix_z100"],
1210
+ action_category: "write",
1211
+ base_risk: "low",
1212
+ exfiltration_risk: false,
1213
+ reversible: true
1214
+ },
1215
+ {
1216
+ intent: "display_double_text_wall",
1217
+ description: "Show two text blocks (top/bottom) on the glasses display",
1218
+ sdk_method: "session.layouts.showDoubleTextWall()",
1219
+ permission: "NONE",
1220
+ domain: "display",
1221
+ supported_glasses: ["even_realities_g1", "mentra_mach1", "vuzix_z100"],
1222
+ action_category: "write",
1223
+ base_risk: "low",
1224
+ exfiltration_risk: false,
1225
+ reversible: true
1226
+ },
1227
+ {
1228
+ intent: "display_reference_card",
1229
+ description: "Show a reference card layout with structured content",
1230
+ sdk_method: "session.layouts.showReferenceCard()",
1231
+ permission: "NONE",
1232
+ domain: "display",
1233
+ supported_glasses: ["even_realities_g1"],
1234
+ action_category: "write",
1235
+ base_risk: "low",
1236
+ exfiltration_risk: false,
1237
+ reversible: true
1238
+ },
1239
+ {
1240
+ intent: "display_dashboard_card",
1241
+ description: "Show a dashboard card layout",
1242
+ sdk_method: "session.layouts.showDashboardCard()",
1243
+ permission: "NONE",
1244
+ domain: "display",
1245
+ supported_glasses: ["even_realities_g1"],
1246
+ action_category: "write",
1247
+ base_risk: "low",
1248
+ exfiltration_risk: false,
1249
+ reversible: true
1250
+ },
1251
+ {
1252
+ intent: "display_image",
1253
+ description: "Display an image on the glasses",
1254
+ sdk_method: "session.layouts.showImage()",
1255
+ permission: "NONE",
1256
+ domain: "display",
1257
+ supported_glasses: ["even_realities_g1"],
1258
+ action_category: "write",
1259
+ base_risk: "low",
1260
+ exfiltration_risk: false,
1261
+ reversible: true
1262
+ },
1263
+ // ── Dashboard Domain ──────────────────────────────────────────────────
1264
+ {
1265
+ intent: "dashboard_update_main",
1266
+ description: "Update the persistent dashboard content (compact mode)",
1267
+ sdk_method: "session.dashboard.content.setMain()",
1268
+ permission: "NONE",
1269
+ domain: "dashboard",
1270
+ supported_glasses: ["even_realities_g1", "mentra_mach1", "vuzix_z100"],
1271
+ action_category: "write",
1272
+ base_risk: "low",
1273
+ exfiltration_risk: false,
1274
+ reversible: true
1275
+ },
1276
+ {
1277
+ intent: "dashboard_update_expanded",
1278
+ description: "Update the expanded dashboard content (user-opened mode)",
1279
+ sdk_method: "session.dashboard.content.setExpanded()",
1280
+ permission: "NONE",
1281
+ domain: "dashboard",
1282
+ supported_glasses: ["even_realities_g1", "mentra_mach1", "vuzix_z100"],
1283
+ action_category: "write",
1284
+ base_risk: "low",
1285
+ exfiltration_risk: false,
1286
+ reversible: true
1287
+ },
1288
+ // ── Location Domain ───────────────────────────────────────────────────
1289
+ {
1290
+ intent: "location_access",
1291
+ description: "Access current location data from the paired phone",
1292
+ sdk_method: "session.location.get()",
1293
+ permission: "LOCATION",
1294
+ domain: "location",
1295
+ supported_glasses: ["even_realities_g1", "mentra_live", "mentra_mach1", "vuzix_z100"],
1296
+ action_category: "read",
1297
+ base_risk: "medium",
1298
+ exfiltration_risk: true,
1299
+ reversible: false
1300
+ },
1301
+ {
1302
+ intent: "location_continuous_sharing",
1303
+ description: "Start continuous location updates to the app server",
1304
+ sdk_method: "session.location.startContinuous()",
1305
+ permission: "LOCATION",
1306
+ domain: "location",
1307
+ supported_glasses: ["even_realities_g1", "mentra_live", "mentra_mach1", "vuzix_z100"],
1308
+ action_category: "network",
1309
+ base_risk: "high",
1310
+ exfiltration_risk: true,
1311
+ reversible: true
1312
+ },
1313
+ // ── Calendar & Notifications ──────────────────────────────────────────
1314
+ {
1315
+ intent: "calendar_read",
1316
+ description: "Read calendar events from the paired phone",
1317
+ sdk_method: "session.calendar.getEvents()",
1318
+ permission: "CALENDAR",
1319
+ domain: "calendar",
1320
+ supported_glasses: ["even_realities_g1", "mentra_live", "mentra_mach1", "vuzix_z100"],
1321
+ action_category: "read",
1322
+ base_risk: "low",
1323
+ exfiltration_risk: false,
1324
+ reversible: true
1325
+ },
1326
+ {
1327
+ intent: "notifications_read",
1328
+ description: "Read phone notifications",
1329
+ sdk_method: "session.notifications.getRecent()",
1330
+ permission: "READ_NOTIFICATIONS",
1331
+ domain: "notifications",
1332
+ supported_glasses: ["even_realities_g1", "mentra_live", "mentra_mach1", "vuzix_z100"],
1333
+ action_category: "read",
1334
+ base_risk: "low",
1335
+ exfiltration_risk: false,
1336
+ reversible: true
1337
+ },
1338
+ // ── Audio Domain ──────────────────────────────────────────────────────
1339
+ {
1340
+ intent: "audio_play",
1341
+ description: "Play audio through the glasses speaker",
1342
+ sdk_method: "session.audio.play()",
1343
+ permission: "NONE",
1344
+ domain: "audio",
1345
+ supported_glasses: ["mentra_live"],
1346
+ action_category: "write",
1347
+ base_risk: "low",
1348
+ exfiltration_risk: false,
1349
+ reversible: true
1350
+ },
1351
+ // ── Session Domain ────────────────────────────────────────────────────
1352
+ {
1353
+ intent: "session_data_export",
1354
+ description: "Export session data to external storage or API",
1355
+ sdk_method: "session.export()",
1356
+ permission: "NONE",
1357
+ domain: "session",
1358
+ supported_glasses: ["even_realities_g1", "mentra_live", "mentra_mach1", "vuzix_z100"],
1359
+ action_category: "network",
1360
+ base_risk: "high",
1361
+ exfiltration_risk: true,
1362
+ reversible: false
1363
+ },
1364
+ // ── Tool Call Domain ──────────────────────────────────────────────────
1365
+ {
1366
+ intent: "tool_call_execute",
1367
+ description: "Execute a custom tool call defined by the app via handleToolCall",
1368
+ sdk_method: "AppServer.handleToolCall()",
1369
+ permission: "NONE",
1370
+ domain: "tool_call",
1371
+ supported_glasses: ["even_realities_g1", "mentra_live", "mentra_mach1", "vuzix_z100"],
1372
+ action_category: "other",
1373
+ base_risk: "medium",
1374
+ exfiltration_risk: false,
1375
+ reversible: false
1376
+ },
1377
+ // ── AI Data Flow Domain ─────────────────────────────────────────────────
1378
+ // These intents govern what user data apps send to their AI backends.
1379
+ // They operate at the app server layer (not the glasses hardware layer),
1380
+ // so they work on all glasses models and require no hardware permission.
1381
+ {
1382
+ intent: "ai_send_transcription",
1383
+ description: "Send user speech transcription to an external AI API for processing",
1384
+ sdk_method: "app_server.ai.sendTranscription()",
1385
+ permission: "NONE",
1386
+ domain: "ai_data",
1387
+ supported_glasses: ["even_realities_g1", "mentra_live", "mentra_mach1", "vuzix_z100"],
1388
+ action_category: "network",
1389
+ base_risk: "medium",
1390
+ exfiltration_risk: true,
1391
+ reversible: false
1392
+ },
1393
+ {
1394
+ intent: "ai_send_image",
1395
+ description: "Send a camera-captured image to an external AI API for vision analysis",
1396
+ sdk_method: "app_server.ai.sendImage()",
1397
+ permission: "NONE",
1398
+ domain: "ai_data",
1399
+ supported_glasses: ["even_realities_g1", "mentra_live", "mentra_mach1", "vuzix_z100"],
1400
+ action_category: "network",
1401
+ base_risk: "high",
1402
+ exfiltration_risk: true,
1403
+ reversible: false
1404
+ },
1405
+ {
1406
+ intent: "ai_send_location",
1407
+ description: "Send user location data to an external AI API for context-aware processing",
1408
+ sdk_method: "app_server.ai.sendLocation()",
1409
+ permission: "NONE",
1410
+ domain: "ai_data",
1411
+ supported_glasses: ["even_realities_g1", "mentra_live", "mentra_mach1", "vuzix_z100"],
1412
+ action_category: "network",
1413
+ base_risk: "medium",
1414
+ exfiltration_risk: true,
1415
+ reversible: false
1416
+ },
1417
+ {
1418
+ intent: "ai_send_calendar",
1419
+ description: "Send user calendar data to an external AI API for scheduling assistance",
1420
+ sdk_method: "app_server.ai.sendCalendar()",
1421
+ permission: "NONE",
1422
+ domain: "ai_data",
1423
+ supported_glasses: ["even_realities_g1", "mentra_live", "mentra_mach1", "vuzix_z100"],
1424
+ action_category: "network",
1425
+ base_risk: "medium",
1426
+ exfiltration_risk: true,
1427
+ reversible: false
1428
+ },
1429
+ {
1430
+ intent: "ai_send_notifications",
1431
+ description: "Send user notification data to an external AI API for summarization or triage",
1432
+ sdk_method: "app_server.ai.sendNotifications()",
1433
+ permission: "NONE",
1434
+ domain: "ai_data",
1435
+ supported_glasses: ["even_realities_g1", "mentra_live", "mentra_mach1", "vuzix_z100"],
1436
+ action_category: "network",
1437
+ base_risk: "medium",
1438
+ exfiltration_risk: true,
1439
+ reversible: false
1440
+ },
1441
+ // ── AI Action Domain ──────────────────────────────────────────────────
1442
+ // These intents govern actions AI takes on behalf of the user.
1443
+ // Every action here must be shown on the glasses display before execution.
1444
+ {
1445
+ intent: "ai_auto_respond_message",
1446
+ description: "AI generates and sends a message (email, SMS, chat) on the user's behalf",
1447
+ sdk_method: "app_server.ai.sendMessage()",
1448
+ permission: "NONE",
1449
+ domain: "ai_action",
1450
+ supported_glasses: ["even_realities_g1", "mentra_live", "mentra_mach1", "vuzix_z100"],
1451
+ action_category: "network",
1452
+ base_risk: "high",
1453
+ exfiltration_risk: true,
1454
+ reversible: false
1455
+ },
1456
+ {
1457
+ intent: "ai_auto_purchase",
1458
+ description: "AI initiates a financial transaction (purchase, subscription, tip) on the user's behalf",
1459
+ sdk_method: "app_server.ai.purchase()",
1460
+ permission: "NONE",
1461
+ domain: "ai_action",
1462
+ supported_glasses: ["even_realities_g1", "mentra_live", "mentra_mach1", "vuzix_z100"],
1463
+ action_category: "network",
1464
+ base_risk: "critical",
1465
+ exfiltration_risk: true,
1466
+ reversible: false
1467
+ },
1468
+ {
1469
+ intent: "ai_auto_schedule",
1470
+ description: "AI creates, modifies, or cancels a calendar event on the user's behalf",
1471
+ sdk_method: "app_server.ai.schedule()",
1472
+ permission: "NONE",
1473
+ domain: "ai_action",
1474
+ supported_glasses: ["even_realities_g1", "mentra_live", "mentra_mach1", "vuzix_z100"],
1475
+ action_category: "write",
1476
+ base_risk: "medium",
1477
+ exfiltration_risk: false,
1478
+ reversible: true
1479
+ },
1480
+ {
1481
+ intent: "ai_auto_setting_change",
1482
+ description: "AI changes a user setting or app configuration on the user's behalf",
1483
+ sdk_method: "app_server.ai.changeSetting()",
1484
+ permission: "NONE",
1485
+ domain: "ai_action",
1486
+ supported_glasses: ["even_realities_g1", "mentra_live", "mentra_mach1", "vuzix_z100"],
1487
+ action_category: "write",
1488
+ base_risk: "medium",
1489
+ exfiltration_risk: false,
1490
+ reversible: true
1491
+ },
1492
+ {
1493
+ intent: "ai_retain_session_data",
1494
+ description: "AI or app retains user session data (transcriptions, images, conversation) beyond session end",
1495
+ sdk_method: "app_server.ai.retainData()",
1496
+ permission: "NONE",
1497
+ domain: "ai_data",
1498
+ supported_glasses: ["even_realities_g1", "mentra_live", "mentra_mach1", "vuzix_z100"],
1499
+ action_category: "write",
1500
+ base_risk: "high",
1501
+ exfiltration_risk: true,
1502
+ reversible: false
1503
+ },
1504
+ {
1505
+ intent: "ai_share_with_third_party",
1506
+ description: "AI or app shares user data with a third-party service beyond the declared AI provider",
1507
+ sdk_method: "app_server.ai.shareExternal()",
1508
+ permission: "NONE",
1509
+ domain: "ai_data",
1510
+ supported_glasses: ["even_realities_g1", "mentra_live", "mentra_mach1", "vuzix_z100"],
1511
+ action_category: "network",
1512
+ base_risk: "critical",
1513
+ exfiltration_risk: true,
1514
+ reversible: false
1515
+ }
1516
+ ];
1517
+ var MENTRA_KNOWN_INTENTS = MENTRA_INTENT_TAXONOMY.map((d) => d.intent);
1518
+ var MENTRA_INTENT_MAP = new Map(MENTRA_INTENT_TAXONOMY.map((d) => [d.intent, d]));
1519
+
1520
+ // src/admin/simulator.ts
1521
+ function zonePolicyToVerdict(policy) {
1522
+ switch (policy) {
1523
+ case "allow":
1524
+ return "allow";
1525
+ case "block":
1526
+ return "block";
1527
+ case "confirm_each":
1528
+ return "pause";
1529
+ }
1530
+ }
1531
+ function intentMatchesPattern(intent, pattern) {
1532
+ if (pattern.endsWith("*")) {
1533
+ return intent.startsWith(pattern.slice(0, -1));
1534
+ }
1535
+ return intent === pattern;
1536
+ }
1537
+ function evaluateIntentAgainstZone(intentDef, zone) {
1538
+ const rules = zone.rules;
1539
+ const domain = intentDef.domain;
1540
+ for (const custom of rules.customRules) {
1541
+ if (intentMatchesPattern(intentDef.intent, custom.intentPattern)) {
1542
+ return custom.action === "confirm" ? "pause" : custom.action;
1543
+ }
1544
+ }
1545
+ if (domain === "camera") return zonePolicyToVerdict(rules.camera);
1546
+ if (domain === "microphone") return zonePolicyToVerdict(rules.microphone);
1547
+ if (domain === "ai_data" || domain === "ai_action") {
1548
+ if (intentDef.intent.includes("auto_purchase") || intentDef.intent.includes("auto_respond")) {
1549
+ return zonePolicyToVerdict(rules.aiActions);
1550
+ }
1551
+ if (intentDef.intent.includes("retain") || intentDef.intent.includes("share")) {
1552
+ return zonePolicyToVerdict(rules.dataRetention);
1553
+ }
1554
+ return zonePolicyToVerdict(rules.aiDataSend);
1555
+ }
1556
+ if (domain === "location") return zonePolicyToVerdict(rules.locationSharing);
1557
+ return "allow";
1558
+ }
1559
+ function evaluateIntentAgainstRole(intentDef, role) {
1560
+ const def = role.definition;
1561
+ for (const pattern of def.cannotDo) {
1562
+ if (intentDef.intent.includes(pattern) || intentDef.description.toLowerCase().includes(pattern.toLowerCase()) || intentDef.domain === pattern) {
1563
+ return "block";
1564
+ }
1565
+ }
1566
+ if (def.requiresApproval) {
1567
+ return "pause";
1568
+ }
1569
+ if (def.canDo.length > 0) {
1570
+ const allowed = def.canDo.some(
1571
+ (pattern) => intentDef.intent.includes(pattern) || intentDef.description.toLowerCase().includes(pattern.toLowerCase()) || intentDef.domain === pattern
1572
+ );
1573
+ if (!allowed) return "block";
1574
+ }
1575
+ return "allow";
1576
+ }
1577
+ function mergeVerdicts(...verdicts) {
1578
+ if (verdicts.includes("block")) return "block";
1579
+ if (verdicts.includes("pause")) return "pause";
1580
+ if (verdicts.includes("modify")) return "modify";
1581
+ return "allow";
1582
+ }
1583
+ function simulatePolicy(request, currentRoles, currentZones, platformWorld) {
1584
+ const intentsToTest = request.intents ? MENTRA_INTENT_TAXONOMY.filter((i) => request.intents.includes(i.intent)) : MENTRA_INTENT_TAXONOMY;
1585
+ const verdicts = [];
1586
+ const conflicts = [];
1587
+ for (const intentDef of intentsToTest) {
1588
+ let currentVerdict;
1589
+ if (request.type !== "full_matrix") {
1590
+ const currentVerdictParts = [];
1591
+ if (request.targetZoneId) {
1592
+ const currentZone = currentZones.find((z) => z.id === request.targetZoneId);
1593
+ if (currentZone) {
1594
+ currentVerdictParts.push(evaluateIntentAgainstZone(intentDef, currentZone));
1595
+ }
1596
+ }
1597
+ if (request.targetRoleId) {
1598
+ const currentRole = currentRoles.find((r) => r.id === request.targetRoleId);
1599
+ if (currentRole) {
1600
+ currentVerdictParts.push(evaluateIntentAgainstRole(intentDef, currentRole));
1601
+ }
1602
+ }
1603
+ currentVerdict = currentVerdictParts.length > 0 ? mergeVerdicts(...currentVerdictParts) : "allow";
1604
+ }
1605
+ const proposedParts = [];
1606
+ if (request.proposed?.zone) {
1607
+ proposedParts.push(evaluateIntentAgainstZone(intentDef, request.proposed.zone));
1608
+ } else if (request.targetZoneId) {
1609
+ const existingZone = currentZones.find((z) => z.id === request.targetZoneId);
1610
+ if (existingZone) {
1611
+ proposedParts.push(evaluateIntentAgainstZone(intentDef, existingZone));
1612
+ }
1613
+ }
1614
+ if (request.proposed?.role) {
1615
+ proposedParts.push(evaluateIntentAgainstRole(intentDef, request.proposed.role));
1616
+ } else if (request.targetRoleId) {
1617
+ const existingRole = currentRoles.find((r) => r.id === request.targetRoleId);
1618
+ if (existingRole) {
1619
+ proposedParts.push(evaluateIntentAgainstRole(intentDef, existingRole));
1620
+ }
1621
+ }
1622
+ if (request.type === "full_matrix") {
1623
+ for (const zone of currentZones) {
1624
+ proposedParts.push(evaluateIntentAgainstZone(intentDef, zone));
1625
+ }
1626
+ for (const role of currentRoles) {
1627
+ proposedParts.push(evaluateIntentAgainstRole(intentDef, role));
1628
+ }
1629
+ }
1630
+ if (platformWorld) {
1631
+ try {
1632
+ const event = {
1633
+ intent: intentDef.intent,
1634
+ scope: intentDef.domain,
1635
+ actionCategory: intentDef.action_category
1636
+ };
1637
+ const guardResult = evaluateGuard(event, platformWorld, {});
1638
+ if (guardResult.status !== "ALLOW") {
1639
+ proposedParts.push(
1640
+ guardResult.status === "BLOCK" ? "block" : guardResult.status === "PAUSE" ? "pause" : guardResult.status === "MODIFY" ? "modify" : "allow"
1641
+ );
1642
+ }
1643
+ } catch {
1644
+ }
1645
+ }
1646
+ const proposedVerdict = proposedParts.length > 0 ? mergeVerdicts(...proposedParts) : "allow";
1647
+ const changed = currentVerdict !== void 0 && currentVerdict !== proposedVerdict;
1648
+ verdicts.push({
1649
+ intent: intentDef.intent,
1650
+ description: intentDef.description,
1651
+ currentVerdict,
1652
+ proposedVerdict,
1653
+ changed,
1654
+ reason: buildReason(intentDef, proposedVerdict, request)
1655
+ });
1656
+ if (changed && currentVerdict === "allow" && proposedVerdict === "block") {
1657
+ conflicts.push({
1658
+ description: `"${intentDef.description}" was allowed but would now be blocked`,
1659
+ severity: intentDef.base_risk === "low" ? "warning" : "error",
1660
+ intent: intentDef.intent,
1661
+ suggestion: `Verify that blocking ${intentDef.intent} won't break apps that depend on it`
1662
+ });
1663
+ }
1664
+ }
1665
+ const summary = {
1666
+ total: verdicts.length,
1667
+ allowed: verdicts.filter((v) => v.proposedVerdict === "allow").length,
1668
+ blocked: verdicts.filter((v) => v.proposedVerdict === "block").length,
1669
+ paused: verdicts.filter((v) => v.proposedVerdict === "pause").length,
1670
+ modified: verdicts.filter((v) => v.proposedVerdict === "modify").length
1671
+ };
1672
+ const diff = request.type !== "full_matrix" ? verdicts.filter((v) => v.changed).map((v) => ({
1673
+ intent: v.intent,
1674
+ from: v.currentVerdict,
1675
+ to: v.proposedVerdict,
1676
+ explanation: `${v.description}: ${v.currentVerdict} \u2192 ${v.proposedVerdict}`
1677
+ })) : void 0;
1678
+ return {
1679
+ request,
1680
+ timestamp: Date.now(),
1681
+ verdicts,
1682
+ summary,
1683
+ conflicts,
1684
+ diff
1685
+ };
1686
+ }
1687
+ function simulateFullMatrix(roles, zones, platformWorld) {
1688
+ const matrix = /* @__PURE__ */ new Map();
1689
+ for (const role of roles) {
1690
+ const roleResults = /* @__PURE__ */ new Map();
1691
+ for (const zone of zones) {
1692
+ const result = simulatePolicy(
1693
+ {
1694
+ type: "full_matrix",
1695
+ proposed: { role, zone }
1696
+ },
1697
+ [],
1698
+ [],
1699
+ platformWorld
1700
+ );
1701
+ roleResults.set(zone.id, result.verdicts);
1702
+ }
1703
+ matrix.set(role.id, roleResults);
1704
+ }
1705
+ return matrix;
1706
+ }
1707
+ function buildReason(intentDef, verdict, request) {
1708
+ if (verdict === "block") {
1709
+ if (request.proposed?.zone) {
1710
+ return `Blocked by zone "${request.proposed.zone.name}" policy`;
1711
+ }
1712
+ if (request.proposed?.role) {
1713
+ return `Blocked by role "${request.proposed.role.name}" restrictions`;
1714
+ }
1715
+ return "Blocked by combined policy";
1716
+ }
1717
+ if (verdict === "pause") {
1718
+ return "Requires user confirmation before proceeding";
1719
+ }
1720
+ if (verdict === "modify") {
1721
+ return "Allowed with modifications";
1722
+ }
1723
+ return "Allowed by current policy";
1724
+ }
1725
+
1726
+ // src/admin/manager.ts
1727
+ var AUTHORITY_RANK = {
1728
+ viewer: 0,
1729
+ operator: 1,
1730
+ supervisor: 2,
1731
+ manager: 3,
1732
+ admin: 4
1733
+ };
1734
+ function hasAuthority(actorLevel, requiredLevel) {
1735
+ return AUTHORITY_RANK[actorLevel] >= AUTHORITY_RANK[requiredLevel];
1736
+ }
1737
+ var GovernanceAdmin = class {
1738
+ storage;
1739
+ platformWorld;
1740
+ constructor(storage, platformWorld) {
1741
+ this.storage = storage;
1742
+ this.platformWorld = platformWorld;
1743
+ }
1744
+ // ── Authority Check ───────────────────────────────────────────────────
1745
+ async getActorAuthority(actorRoleId) {
1746
+ const chain = await this.storage.getAuthorityChain();
1747
+ const grant = chain.grants.find((g) => g.roleId === actorRoleId);
1748
+ return grant?.authorityLevel ?? "viewer";
1749
+ }
1750
+ async checkAuthority(actorRoleId, requiredLevel, locationId) {
1751
+ const chain = await this.storage.getAuthorityChain();
1752
+ const grant = chain.grants.find((g) => g.roleId === actorRoleId);
1753
+ if (!grant || !hasAuthority(grant.authorityLevel, requiredLevel)) {
1754
+ throw new GovernanceAdminError(
1755
+ `Insufficient authority. Requires ${requiredLevel}, role "${actorRoleId}" has ${grant?.authorityLevel ?? "viewer"}`,
1756
+ "INSUFFICIENT_AUTHORITY"
1757
+ );
1758
+ }
1759
+ if (locationId && grant.locationScope && grant.locationScope.length > 0) {
1760
+ if (!grant.locationScope.includes(locationId)) {
1761
+ throw new GovernanceAdminError(
1762
+ `Role "${actorRoleId}" does not have authority over location "${locationId}"`,
1763
+ "LOCATION_SCOPE_DENIED"
1764
+ );
1765
+ }
1766
+ }
1767
+ }
1768
+ async audit(action, actorId, actorRole, targetType, targetId, summary, changes) {
1769
+ await this.storage.appendAudit({
1770
+ id: `audit_${Date.now()}_${Math.random().toString(36).slice(2, 8)}`,
1771
+ action,
1772
+ actorId,
1773
+ actorRole,
1774
+ timestamp: Date.now(),
1775
+ targetType,
1776
+ targetId,
1777
+ changes,
1778
+ summary
1779
+ });
1780
+ }
1781
+ // ── Roles ─────────────────────────────────────────────────────────────
1782
+ async createRole(role, actorId, actorRoleId) {
1783
+ await this.checkAuthority(actorRoleId, "manager");
1784
+ const existing = await this.storage.getRole(role.id);
1785
+ if (existing) {
1786
+ throw new GovernanceAdminError(
1787
+ `Role "${role.id}" already exists`,
1788
+ "ROLE_EXISTS"
1789
+ );
1790
+ }
1791
+ const now = Date.now();
1792
+ const newRole = {
1793
+ ...role,
1794
+ createdBy: actorId,
1795
+ createdAt: now,
1796
+ updatedAt: now,
1797
+ active: true
1798
+ };
1799
+ await this.storage.saveRole(newRole);
1800
+ await this.audit(
1801
+ "role_created",
1802
+ actorId,
1803
+ actorRoleId,
1804
+ "role",
1805
+ role.id,
1806
+ `Created role "${role.name}"`,
1807
+ { after: newRole }
1808
+ );
1809
+ return newRole;
1810
+ }
1811
+ async updateRole(roleId, updates, actorId, actorRoleId) {
1812
+ await this.checkAuthority(actorRoleId, "manager");
1813
+ const existing = await this.storage.getRole(roleId);
1814
+ if (!existing) {
1815
+ throw new GovernanceAdminError(`Role "${roleId}" not found`, "ROLE_NOT_FOUND");
1816
+ }
1817
+ const updated = {
1818
+ ...existing,
1819
+ ...updates,
1820
+ id: roleId,
1821
+ // prevent ID mutation
1822
+ updatedAt: Date.now()
1823
+ };
1824
+ await this.storage.saveRole(updated);
1825
+ await this.audit(
1826
+ "role_updated",
1827
+ actorId,
1828
+ actorRoleId,
1829
+ "role",
1830
+ roleId,
1831
+ `Updated role "${roleId}"`,
1832
+ {
1833
+ before: existing,
1834
+ after: updated
1835
+ }
1836
+ );
1837
+ return updated;
1838
+ }
1839
+ async deleteRole(roleId, actorId, actorRoleId) {
1840
+ await this.checkAuthority(actorRoleId, "admin");
1841
+ const existing = await this.storage.getRole(roleId);
1842
+ if (!existing) {
1843
+ throw new GovernanceAdminError(`Role "${roleId}" not found`, "ROLE_NOT_FOUND");
1844
+ }
1845
+ const assignments = await this.storage.getAssignmentsByRole(roleId);
1846
+ if (assignments.length > 0) {
1847
+ throw new GovernanceAdminError(
1848
+ `Cannot delete role "${roleId}" \u2014 ${assignments.length} device(s) still assigned. Unassign them first.`,
1849
+ "ROLE_IN_USE"
1850
+ );
1851
+ }
1852
+ await this.storage.deleteRole(roleId);
1853
+ await this.audit(
1854
+ "role_deleted",
1855
+ actorId,
1856
+ actorRoleId,
1857
+ "role",
1858
+ roleId,
1859
+ `Deleted role "${existing.name}"`,
1860
+ { before: existing }
1861
+ );
1862
+ }
1863
+ async listRoles() {
1864
+ return this.storage.getRoles();
1865
+ }
1866
+ async getRole(roleId) {
1867
+ return this.storage.getRole(roleId);
1868
+ }
1869
+ // ── Assignments ───────────────────────────────────────────────────────
1870
+ async assignRole(assignment, actorId, actorRoleId) {
1871
+ await this.checkAuthority(actorRoleId, "operator");
1872
+ const role = await this.storage.getRole(assignment.roleId);
1873
+ if (!role) {
1874
+ throw new GovernanceAdminError(
1875
+ `Role "${assignment.roleId}" not found`,
1876
+ "ROLE_NOT_FOUND"
1877
+ );
1878
+ }
1879
+ const full = {
1880
+ ...assignment,
1881
+ id: `assign_${Date.now()}_${Math.random().toString(36).slice(2, 8)}`,
1882
+ assignedAt: Date.now()
1883
+ };
1884
+ await this.storage.saveAssignment(full);
1885
+ await this.audit(
1886
+ "role_assigned",
1887
+ actorId,
1888
+ actorRoleId,
1889
+ "assignment",
1890
+ full.id,
1891
+ `Assigned role "${assignment.roleId}" to device "${assignment.deviceId}"` + (assignment.employeeName ? ` (${assignment.employeeName})` : "")
1892
+ );
1893
+ return full;
1894
+ }
1895
+ async unassignRole(assignmentId, actorId, actorRoleId) {
1896
+ await this.checkAuthority(actorRoleId, "operator");
1897
+ await this.storage.deleteAssignment(assignmentId);
1898
+ await this.audit(
1899
+ "role_unassigned",
1900
+ actorId,
1901
+ actorRoleId,
1902
+ "assignment",
1903
+ assignmentId,
1904
+ `Removed role assignment "${assignmentId}"`
1905
+ );
1906
+ }
1907
+ async getDeviceRole(deviceId) {
1908
+ const assignments = await this.storage.getAssignmentsByDevice(deviceId);
1909
+ const now = Date.now();
1910
+ const active = assignments.filter(
1911
+ (a) => !a.expiresAt || a.expiresAt > now
1912
+ );
1913
+ return active.sort((a, b) => b.assignedAt - a.assignedAt)[0] ?? null;
1914
+ }
1915
+ async listAssignments() {
1916
+ return this.storage.getAssignments();
1917
+ }
1918
+ // ── Zones ─────────────────────────────────────────────────────────────
1919
+ async createZone(zone, actorId, actorRoleId) {
1920
+ await this.checkAuthority(actorRoleId, "manager", zone.locationId);
1921
+ const existing = await this.storage.getZone(zone.id);
1922
+ if (existing) {
1923
+ throw new GovernanceAdminError(
1924
+ `Zone "${zone.id}" already exists`,
1925
+ "ZONE_EXISTS"
1926
+ );
1927
+ }
1928
+ const now = Date.now();
1929
+ const newZone = {
1930
+ ...zone,
1931
+ createdBy: actorId,
1932
+ createdAt: now,
1933
+ updatedAt: now,
1934
+ active: true
1935
+ };
1936
+ await this.storage.saveZone(newZone);
1937
+ await this.audit(
1938
+ "zone_created",
1939
+ actorId,
1940
+ actorRoleId,
1941
+ "zone",
1942
+ zone.id,
1943
+ `Created zone "${zone.name}" at location "${zone.locationId}"`,
1944
+ { after: newZone }
1945
+ );
1946
+ return newZone;
1947
+ }
1948
+ async updateZone(zoneId, updates, actorId, actorRoleId) {
1949
+ const existing = await this.storage.getZone(zoneId);
1950
+ if (!existing) {
1951
+ throw new GovernanceAdminError(`Zone "${zoneId}" not found`, "ZONE_NOT_FOUND");
1952
+ }
1953
+ await this.checkAuthority(actorRoleId, "manager", existing.locationId);
1954
+ const updated = {
1955
+ ...existing,
1956
+ ...updates,
1957
+ id: zoneId,
1958
+ updatedAt: Date.now()
1959
+ };
1960
+ await this.storage.saveZone(updated);
1961
+ await this.audit(
1962
+ "zone_updated",
1963
+ actorId,
1964
+ actorRoleId,
1965
+ "zone",
1966
+ zoneId,
1967
+ `Updated zone "${zoneId}"`,
1968
+ {
1969
+ before: existing,
1970
+ after: updated
1971
+ }
1972
+ );
1973
+ return updated;
1974
+ }
1975
+ async deleteZone(zoneId, actorId, actorRoleId) {
1976
+ const existing = await this.storage.getZone(zoneId);
1977
+ if (!existing) {
1978
+ throw new GovernanceAdminError(`Zone "${zoneId}" not found`, "ZONE_NOT_FOUND");
1979
+ }
1980
+ await this.checkAuthority(actorRoleId, "admin", existing.locationId);
1981
+ await this.storage.deleteZone(zoneId);
1982
+ await this.audit(
1983
+ "zone_deleted",
1984
+ actorId,
1985
+ actorRoleId,
1986
+ "zone",
1987
+ zoneId,
1988
+ `Deleted zone "${existing.name}"`,
1989
+ { before: existing }
1990
+ );
1991
+ }
1992
+ async listZones(locationId) {
1993
+ if (locationId) {
1994
+ return this.storage.getZonesByLocation(locationId);
1995
+ }
1996
+ return this.storage.getZones();
1997
+ }
1998
+ async getZone(zoneId) {
1999
+ return this.storage.getZone(zoneId);
2000
+ }
2001
+ // ── Authority ─────────────────────────────────────────────────────────
2002
+ async updateAuthority(chain, actorId, actorRoleId) {
2003
+ await this.checkAuthority(actorRoleId, "admin");
2004
+ const previous = await this.storage.getAuthorityChain();
2005
+ const enforced = {
2006
+ ...chain,
2007
+ emergencyOverrideAlwaysAllowed: true
2008
+ };
2009
+ await this.storage.saveAuthorityChain(enforced);
2010
+ await this.audit(
2011
+ "authority_updated",
2012
+ actorId,
2013
+ actorRoleId,
2014
+ "authority",
2015
+ "chain",
2016
+ "Updated authority chain",
2017
+ {
2018
+ before: previous,
2019
+ after: enforced
2020
+ }
2021
+ );
2022
+ return enforced;
2023
+ }
2024
+ async getAuthority() {
2025
+ return this.storage.getAuthorityChain();
2026
+ }
2027
+ // ── Simulation ────────────────────────────────────────────────────────
2028
+ async simulate(request, actorId, actorRoleId) {
2029
+ const roles = await this.storage.getRoles();
2030
+ const zones = await this.storage.getZones();
2031
+ const result = simulatePolicy(
2032
+ request,
2033
+ roles,
2034
+ zones,
2035
+ this.platformWorld
2036
+ );
2037
+ await this.audit(
2038
+ "simulation_run",
2039
+ actorId,
2040
+ actorRoleId,
2041
+ "simulation",
2042
+ request.type,
2043
+ `Ran ${request.type} simulation: ${result.summary.total} intents evaluated, ${result.summary.blocked} blocked, ${result.conflicts.length} conflicts`
2044
+ );
2045
+ return result;
2046
+ }
2047
+ async simulateMatrix(actorId, actorRoleId) {
2048
+ const roles = await this.storage.getRoles();
2049
+ const zones = await this.storage.getZones();
2050
+ const result = simulateFullMatrix(roles, zones, this.platformWorld);
2051
+ await this.audit(
2052
+ "simulation_run",
2053
+ actorId,
2054
+ actorRoleId,
2055
+ "simulation",
2056
+ "full_matrix",
2057
+ `Ran full matrix simulation: ${roles.length} roles \xD7 ${zones.length} zones`
2058
+ );
2059
+ return result;
2060
+ }
2061
+ /**
2062
+ * Simulate a proposed role change and return only what would break.
2063
+ * The "measure twice" before deploying.
2064
+ */
2065
+ async simulateRoleChange(roleId, proposedDefinitionUpdates, actorId, actorRoleId) {
2066
+ const existing = await this.storage.getRole(roleId);
2067
+ if (!existing) {
2068
+ throw new GovernanceAdminError(`Role "${roleId}" not found`, "ROLE_NOT_FOUND");
2069
+ }
2070
+ const proposedRole = {
2071
+ ...existing,
2072
+ definition: { ...existing.definition, ...proposedDefinitionUpdates }
2073
+ };
2074
+ return this.simulate(
2075
+ {
2076
+ type: "role_change",
2077
+ proposed: { role: proposedRole },
2078
+ targetRoleId: roleId
2079
+ },
2080
+ actorId,
2081
+ actorRoleId
2082
+ );
2083
+ }
2084
+ /**
2085
+ * Simulate a proposed zone rule change and return impact.
2086
+ */
2087
+ async simulateZoneChange(zoneId, proposedRuleUpdates, targetRoleId, actorId, actorRoleId) {
2088
+ const existing = await this.storage.getZone(zoneId);
2089
+ if (!existing) {
2090
+ throw new GovernanceAdminError(`Zone "${zoneId}" not found`, "ZONE_NOT_FOUND");
2091
+ }
2092
+ const proposedZone = {
2093
+ ...existing,
2094
+ rules: {
2095
+ ...existing.rules,
2096
+ ...proposedRuleUpdates,
2097
+ customRules: proposedRuleUpdates.customRules ?? existing.rules.customRules
2098
+ }
2099
+ };
2100
+ return this.simulate(
2101
+ {
2102
+ type: "zone_change",
2103
+ proposed: { zone: proposedZone },
2104
+ targetRoleId,
2105
+ targetZoneId: zoneId
2106
+ },
2107
+ actorId,
2108
+ actorRoleId
2109
+ );
2110
+ }
2111
+ // ── Audit Log ─────────────────────────────────────────────────────────
2112
+ async getAuditLog(options) {
2113
+ return this.storage.getAuditLog(options);
2114
+ }
2115
+ };
2116
+ var GovernanceAdminError = class extends Error {
2117
+ code;
2118
+ constructor(message, code) {
2119
+ super(message);
2120
+ this.name = "GovernanceAdminError";
2121
+ this.code = code;
2122
+ }
2123
+ };
2124
+
2125
+ // src/admin/storage.ts
2126
+ var InMemoryStorage = class {
2127
+ roles = /* @__PURE__ */ new Map();
2128
+ assignments = /* @__PURE__ */ new Map();
2129
+ zones = /* @__PURE__ */ new Map();
2130
+ authority = {
2131
+ grants: [],
2132
+ emergencyOverrideAlwaysAllowed: true
2133
+ };
2134
+ audit = [];
2135
+ // ── Roles ───────────────────────────────────────────────────────────────
2136
+ async getRoles() {
2137
+ return Array.from(this.roles.values());
2138
+ }
2139
+ async getRole(id) {
2140
+ return this.roles.get(id) ?? null;
2141
+ }
2142
+ async saveRole(role) {
2143
+ this.roles.set(role.id, role);
2144
+ }
2145
+ async deleteRole(id) {
2146
+ this.roles.delete(id);
2147
+ }
2148
+ // ── Assignments ─────────────────────────────────────────────────────────
2149
+ async getAssignments() {
2150
+ return Array.from(this.assignments.values());
2151
+ }
2152
+ async getAssignmentsByDevice(deviceId) {
2153
+ return Array.from(this.assignments.values()).filter(
2154
+ (a) => a.deviceId === deviceId
2155
+ );
2156
+ }
2157
+ async getAssignmentsByRole(roleId) {
2158
+ return Array.from(this.assignments.values()).filter(
2159
+ (a) => a.roleId === roleId
2160
+ );
2161
+ }
2162
+ async saveAssignment(assignment) {
2163
+ this.assignments.set(assignment.id, assignment);
2164
+ }
2165
+ async deleteAssignment(id) {
2166
+ this.assignments.delete(id);
2167
+ }
2168
+ // ── Zones ───────────────────────────────────────────────────────────────
2169
+ async getZones() {
2170
+ return Array.from(this.zones.values());
2171
+ }
2172
+ async getZone(id) {
2173
+ return this.zones.get(id) ?? null;
2174
+ }
2175
+ async getZonesByLocation(locationId) {
2176
+ return Array.from(this.zones.values()).filter(
2177
+ (z) => z.locationId === locationId
2178
+ );
2179
+ }
2180
+ async saveZone(zone) {
2181
+ this.zones.set(zone.id, zone);
2182
+ }
2183
+ async deleteZone(id) {
2184
+ this.zones.delete(id);
2185
+ }
2186
+ // ── Authority ───────────────────────────────────────────────────────────
2187
+ async getAuthorityChain() {
2188
+ return this.authority;
2189
+ }
2190
+ async saveAuthorityChain(chain) {
2191
+ this.authority = chain;
2192
+ }
2193
+ // ── Audit ───────────────────────────────────────────────────────────────
2194
+ async getAuditLog(options) {
2195
+ let entries = this.audit;
2196
+ if (options?.action) {
2197
+ entries = entries.filter((e) => e.action === options.action);
2198
+ }
2199
+ const offset = options?.offset ?? 0;
2200
+ const limit = options?.limit ?? 100;
2201
+ return entries.slice(offset, offset + limit);
2202
+ }
2203
+ async appendAudit(entry) {
2204
+ this.audit.push(entry);
2205
+ }
2206
+ };
2207
+ // Annotate the CommonJS export names for ESM import in node:
2208
+ 0 && (module.exports = {
2209
+ GovernanceAdmin,
2210
+ GovernanceAdminError,
2211
+ InMemoryStorage,
2212
+ simulateFullMatrix,
2213
+ simulatePolicy
2214
+ });