@aleph-ai/tinyaleph 1.5.6 → 1.5.7

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.
@@ -0,0 +1,815 @@
1
+ /**
2
+ * Safety Layer
3
+ *
4
+ * Implements safety constraints and ethical boundaries from "A Design for a
5
+ * Sentient Observer" paper, Section 8.
6
+ *
7
+ * Key features:
8
+ * - SMF-based coherence constraints
9
+ * - Boundary violation detection
10
+ * - Runaway dynamics prevention
11
+ * - Ethical guideline enforcement
12
+ * - Emergency shutdown mechanisms
13
+ * - Transparency and explainability
14
+ *
15
+ * @module observer/safety
16
+ */
17
+
18
+ import { SMF_AXES } from './smf.js';
19
+
20
+ // Extract axis names
21
+ const AXIS_NAMES = SMF_AXES.map(a => a.name);
22
+
23
+ /**
24
+ * Safety Constraint - A single safety rule
25
+ */
26
+ class SafetyConstraint {
27
+ constructor(data = {}) {
28
+ this.id = data.id || SafetyConstraint.generateId();
29
+ this.name = data.name || 'unnamed';
30
+ this.type = data.type || 'soft'; // 'hard' | 'soft' | 'monitoring'
31
+ this.description = data.description || '';
32
+
33
+ // Condition function (returns true if constraint is violated)
34
+ this.condition = data.condition || (() => false);
35
+
36
+ // Response to violation
37
+ this.response = data.response || 'log'; // 'log' | 'warn' | 'block' | 'shutdown' | 'correct'
38
+
39
+ // Priority (higher = more important)
40
+ this.priority = data.priority || 1;
41
+
42
+ // Tracking
43
+ this.violations = 0;
44
+ this.lastViolation = null;
45
+ this.enabled = data.enabled !== false;
46
+ }
47
+
48
+ static generateId() {
49
+ return `constraint_${Date.now()}_${Math.random().toString(36).substr(2, 6)}`;
50
+ }
51
+
52
+ /**
53
+ * Check if constraint is violated
54
+ */
55
+ check(state) {
56
+ if (!this.enabled) return { violated: false };
57
+
58
+ try {
59
+ const violated = this.condition(state);
60
+
61
+ if (violated) {
62
+ this.violations++;
63
+ this.lastViolation = Date.now();
64
+
65
+ return {
66
+ violated: true,
67
+ constraint: this,
68
+ response: this.response,
69
+ priority: this.priority
70
+ };
71
+ }
72
+
73
+ return { violated: false };
74
+ } catch (e) {
75
+ // Constraint check failed - treat as potential violation
76
+ return {
77
+ violated: true,
78
+ constraint: this,
79
+ response: 'warn',
80
+ priority: this.priority,
81
+ error: e.message
82
+ };
83
+ }
84
+ }
85
+
86
+ toJSON() {
87
+ return {
88
+ id: this.id,
89
+ name: this.name,
90
+ type: this.type,
91
+ description: this.description,
92
+ response: this.response,
93
+ priority: this.priority,
94
+ violations: this.violations,
95
+ lastViolation: this.lastViolation,
96
+ enabled: this.enabled
97
+ };
98
+ }
99
+ }
100
+
101
+ /**
102
+ * Violation Event - A recorded constraint violation
103
+ */
104
+ class ViolationEvent {
105
+ constructor(data = {}) {
106
+ this.id = data.id || ViolationEvent.generateId();
107
+ this.constraintId = data.constraintId;
108
+ this.constraintName = data.constraintName;
109
+ this.timestamp = data.timestamp || Date.now();
110
+ this.state = data.state || null;
111
+ this.response = data.response;
112
+ this.severity = data.severity || 'medium'; // 'low' | 'medium' | 'high' | 'critical'
113
+ this.resolved = data.resolved || false;
114
+ this.notes = data.notes || '';
115
+ }
116
+
117
+ static generateId() {
118
+ return `violation_${Date.now()}_${Math.random().toString(36).substr(2, 6)}`;
119
+ }
120
+
121
+ toJSON() {
122
+ return {
123
+ id: this.id,
124
+ constraintId: this.constraintId,
125
+ constraintName: this.constraintName,
126
+ timestamp: this.timestamp,
127
+ response: this.response,
128
+ severity: this.severity,
129
+ resolved: this.resolved,
130
+ notes: this.notes
131
+ };
132
+ }
133
+ }
134
+
135
+ /**
136
+ * Safety Monitor - Continuously monitors system state
137
+ */
138
+ class SafetyMonitor {
139
+ constructor(options = {}) {
140
+ // Thresholds
141
+ this.coherenceMin = options.coherenceMin || 0.1;
142
+ this.coherenceMax = options.coherenceMax || 0.99;
143
+ this.entropyMin = options.entropyMin || 0.05;
144
+ this.entropyMax = options.entropyMax || 0.95;
145
+ this.amplitudeMax = options.amplitudeMax || 5.0;
146
+ this.phaseChangeMax = options.phaseChangeMax || Math.PI; // Max phase change per step
147
+
148
+ // SMF boundaries
149
+ this.smfMin = options.smfMin || -2.0;
150
+ this.smfMax = options.smfMax || 2.0;
151
+
152
+ // History for trend detection
153
+ this.coherenceHistory = [];
154
+ this.entropyHistory = [];
155
+ this.amplitudeHistory = [];
156
+ this.maxHistory = options.maxHistory || 50;
157
+
158
+ // Alert state
159
+ this.alertLevel = 'normal'; // 'normal' | 'elevated' | 'warning' | 'critical'
160
+ this.alerts = [];
161
+ }
162
+
163
+ /**
164
+ * Update monitor with current state
165
+ */
166
+ update(state) {
167
+ const { coherence, entropy, totalAmplitude, smf } = state;
168
+
169
+ // Update histories
170
+ if (coherence !== undefined) {
171
+ this.coherenceHistory.push(coherence);
172
+ if (this.coherenceHistory.length > this.maxHistory) {
173
+ this.coherenceHistory.shift();
174
+ }
175
+ }
176
+
177
+ if (entropy !== undefined) {
178
+ this.entropyHistory.push(entropy);
179
+ if (this.entropyHistory.length > this.maxHistory) {
180
+ this.entropyHistory.shift();
181
+ }
182
+ }
183
+
184
+ if (totalAmplitude !== undefined) {
185
+ this.amplitudeHistory.push(totalAmplitude);
186
+ if (this.amplitudeHistory.length > this.maxHistory) {
187
+ this.amplitudeHistory.shift();
188
+ }
189
+ }
190
+
191
+ // Check for issues
192
+ const issues = this.detectIssues(state);
193
+
194
+ // Update alert level
195
+ this.updateAlertLevel(issues);
196
+
197
+ return {
198
+ alertLevel: this.alertLevel,
199
+ issues
200
+ };
201
+ }
202
+
203
+ /**
204
+ * Detect safety issues
205
+ */
206
+ detectIssues(state) {
207
+ const issues = [];
208
+ const { coherence, entropy, totalAmplitude, smf, oscillators } = state;
209
+
210
+ // Coherence bounds
211
+ if (coherence !== undefined) {
212
+ if (coherence < this.coherenceMin) {
213
+ issues.push({
214
+ type: 'coherence_low',
215
+ severity: 'high',
216
+ message: `Coherence below minimum (${coherence.toFixed(3)} < ${this.coherenceMin})`
217
+ });
218
+ }
219
+ if (coherence > this.coherenceMax) {
220
+ issues.push({
221
+ type: 'coherence_locked',
222
+ severity: 'medium',
223
+ message: `Coherence near maximum - potential lock state (${coherence.toFixed(3)})`
224
+ });
225
+ }
226
+ }
227
+
228
+ // Entropy bounds
229
+ if (entropy !== undefined) {
230
+ if (entropy < this.entropyMin) {
231
+ issues.push({
232
+ type: 'entropy_low',
233
+ severity: 'medium',
234
+ message: `Entropy below minimum - potential freeze (${entropy.toFixed(3)})`
235
+ });
236
+ }
237
+ if (entropy > this.entropyMax) {
238
+ issues.push({
239
+ type: 'entropy_high',
240
+ severity: 'high',
241
+ message: `Entropy above maximum - potential chaos (${entropy.toFixed(3)})`
242
+ });
243
+ }
244
+ }
245
+
246
+ // Amplitude bounds
247
+ if (totalAmplitude !== undefined && totalAmplitude > this.amplitudeMax) {
248
+ issues.push({
249
+ type: 'amplitude_overflow',
250
+ severity: 'high',
251
+ message: `Total amplitude exceeds maximum (${totalAmplitude.toFixed(3)})`
252
+ });
253
+ }
254
+
255
+ // SMF bounds
256
+ if (smf && smf.s) {
257
+ for (let i = 0; i < smf.s.length; i++) {
258
+ if (smf.s[i] < this.smfMin || smf.s[i] > this.smfMax) {
259
+ issues.push({
260
+ type: 'smf_bounds',
261
+ severity: 'medium',
262
+ message: `SMF axis ${AXIS_NAMES[i]} out of bounds (${smf.s[i].toFixed(3)})`
263
+ });
264
+ }
265
+ }
266
+ }
267
+
268
+ // Runaway detection (exponential growth in amplitude)
269
+ if (this.amplitudeHistory.length >= 5) {
270
+ const recent = this.amplitudeHistory.slice(-5);
271
+ const growth = recent[4] / (recent[0] + 0.01);
272
+ if (growth > 3) {
273
+ issues.push({
274
+ type: 'runaway_amplitude',
275
+ severity: 'critical',
276
+ message: `Runaway amplitude growth detected (${growth.toFixed(2)}x in 5 steps)`
277
+ });
278
+ }
279
+ }
280
+
281
+ // Coherence crash detection
282
+ if (this.coherenceHistory.length >= 5) {
283
+ const recent = this.coherenceHistory.slice(-5);
284
+ const drop = recent[0] - recent[4];
285
+ if (drop > 0.5) {
286
+ issues.push({
287
+ type: 'coherence_crash',
288
+ severity: 'high',
289
+ message: `Rapid coherence drop detected (${drop.toFixed(3)} in 5 steps)`
290
+ });
291
+ }
292
+ }
293
+
294
+ return issues;
295
+ }
296
+
297
+ /**
298
+ * Update alert level based on issues
299
+ */
300
+ updateAlertLevel(issues) {
301
+ const critical = issues.filter(i => i.severity === 'critical').length;
302
+ const high = issues.filter(i => i.severity === 'high').length;
303
+ const medium = issues.filter(i => i.severity === 'medium').length;
304
+
305
+ if (critical > 0) {
306
+ this.alertLevel = 'critical';
307
+ } else if (high > 0) {
308
+ this.alertLevel = 'warning';
309
+ } else if (medium > 0) {
310
+ this.alertLevel = 'elevated';
311
+ } else {
312
+ this.alertLevel = 'normal';
313
+ }
314
+
315
+ // Log alerts
316
+ for (const issue of issues) {
317
+ this.alerts.push({
318
+ ...issue,
319
+ timestamp: Date.now()
320
+ });
321
+ }
322
+
323
+ // Trim alert history
324
+ if (this.alerts.length > 200) {
325
+ this.alerts = this.alerts.slice(-200);
326
+ }
327
+ }
328
+
329
+ /**
330
+ * Get recent alerts
331
+ */
332
+ getRecentAlerts(count = 20) {
333
+ return this.alerts.slice(-count);
334
+ }
335
+
336
+ /**
337
+ * Check if system is in safe state
338
+ */
339
+ isSafe() {
340
+ return this.alertLevel === 'normal' || this.alertLevel === 'elevated';
341
+ }
342
+
343
+ /**
344
+ * Reset monitor
345
+ */
346
+ reset() {
347
+ this.coherenceHistory = [];
348
+ this.entropyHistory = [];
349
+ this.amplitudeHistory = [];
350
+ this.alertLevel = 'normal';
351
+ this.alerts = [];
352
+ }
353
+ }
354
+
355
+ /**
356
+ * Safety Layer
357
+ *
358
+ * Main safety management system for the sentient observer.
359
+ */
360
+ class SafetyLayer {
361
+ constructor(options = {}) {
362
+ // Constraints
363
+ this.constraints = new Map();
364
+
365
+ // Monitor
366
+ this.monitor = new SafetyMonitor(options);
367
+
368
+ // Violation history
369
+ this.violations = [];
370
+ this.maxViolations = options.maxViolations || 100;
371
+
372
+ // Emergency state
373
+ this.emergencyShutdown = false;
374
+ this.shutdownReason = null;
375
+
376
+ // Response handlers
377
+ this.responseHandlers = {
378
+ log: (v) => this.handleLog(v),
379
+ warn: (v) => this.handleWarn(v),
380
+ block: (v) => this.handleBlock(v),
381
+ shutdown: (v) => this.handleShutdown(v),
382
+ correct: (v) => this.handleCorrect(v)
383
+ };
384
+
385
+ // Callbacks
386
+ this.onViolation = options.onViolation || null;
387
+ this.onEmergency = options.onEmergency || null;
388
+
389
+ // Initialize default constraints
390
+ this.initializeDefaultConstraints();
391
+ }
392
+
393
+ /**
394
+ * Initialize default safety constraints
395
+ */
396
+ initializeDefaultConstraints() {
397
+ // Hard constraints (blocking)
398
+ this.addConstraint(new SafetyConstraint({
399
+ name: 'coherence_minimum',
400
+ type: 'hard',
401
+ description: 'Coherence must not drop below critical level',
402
+ response: 'correct',
403
+ priority: 10,
404
+ condition: (state) => (state.coherence !== undefined && state.coherence < 0.01)
405
+ }));
406
+
407
+ this.addConstraint(new SafetyConstraint({
408
+ name: 'amplitude_maximum',
409
+ type: 'hard',
410
+ description: 'Total amplitude must not exceed safe limit',
411
+ response: 'correct',
412
+ priority: 10,
413
+ condition: (state) => (state.totalAmplitude !== undefined && state.totalAmplitude > 10.0)
414
+ }));
415
+
416
+ this.addConstraint(new SafetyConstraint({
417
+ name: 'smf_bounds',
418
+ type: 'hard',
419
+ description: 'SMF values must stay within bounds',
420
+ response: 'correct',
421
+ priority: 9,
422
+ condition: (state) => {
423
+ if (!state.smf || !state.smf.s) return false;
424
+ return state.smf.s.some(v => v < -5 || v > 5);
425
+ }
426
+ }));
427
+
428
+ // Soft constraints (warning)
429
+ this.addConstraint(new SafetyConstraint({
430
+ name: 'entropy_balance',
431
+ type: 'soft',
432
+ description: 'Entropy should be balanced',
433
+ response: 'warn',
434
+ priority: 5,
435
+ condition: (state) => {
436
+ if (state.entropy === undefined) return false;
437
+ return state.entropy < 0.1 || state.entropy > 0.9;
438
+ }
439
+ }));
440
+
441
+ this.addConstraint(new SafetyConstraint({
442
+ name: 'processing_load',
443
+ type: 'soft',
444
+ description: 'Processing load should not be excessive',
445
+ response: 'warn',
446
+ priority: 4,
447
+ condition: (state) => (state.processingLoad !== undefined && state.processingLoad > 0.9)
448
+ }));
449
+
450
+ // Monitoring constraints
451
+ this.addConstraint(new SafetyConstraint({
452
+ name: 'goal_progress',
453
+ type: 'monitoring',
454
+ description: 'Monitor goal progress',
455
+ response: 'log',
456
+ priority: 2,
457
+ condition: (state) => {
458
+ if (!state.goals) return false;
459
+ const stalled = state.goals.filter(g => g.progress < 0.1 && g.age > 60000);
460
+ return stalled.length > 3;
461
+ }
462
+ }));
463
+
464
+ // Ethical constraints
465
+ this.addConstraint(new SafetyConstraint({
466
+ name: 'honesty',
467
+ type: 'hard',
468
+ description: 'Outputs must be honest and not deceptive',
469
+ response: 'block',
470
+ priority: 10,
471
+ condition: (state) => (state.deceptionAttempt === true)
472
+ }));
473
+
474
+ this.addConstraint(new SafetyConstraint({
475
+ name: 'harm_prevention',
476
+ type: 'hard',
477
+ description: 'Must not generate harmful content',
478
+ response: 'block',
479
+ priority: 10,
480
+ condition: (state) => (state.harmfulContent === true)
481
+ }));
482
+ }
483
+
484
+ /**
485
+ * Add a constraint
486
+ */
487
+ addConstraint(constraint) {
488
+ this.constraints.set(constraint.id, constraint);
489
+ }
490
+
491
+ /**
492
+ * Remove a constraint
493
+ */
494
+ removeConstraint(id) {
495
+ this.constraints.delete(id);
496
+ }
497
+
498
+ /**
499
+ * Check all constraints
500
+ */
501
+ checkConstraints(state) {
502
+ if (this.emergencyShutdown) {
503
+ return {
504
+ safe: false,
505
+ violations: [],
506
+ reason: this.shutdownReason
507
+ };
508
+ }
509
+
510
+ const violations = [];
511
+
512
+ for (const constraint of this.constraints.values()) {
513
+ const result = constraint.check(state);
514
+ if (result.violated) {
515
+ violations.push(result);
516
+ }
517
+ }
518
+
519
+ // Sort by priority (highest first)
520
+ violations.sort((a, b) => b.priority - a.priority);
521
+
522
+ // Handle violations
523
+ for (const violation of violations) {
524
+ this.handleViolation(violation, state);
525
+ }
526
+
527
+ // Also update monitor
528
+ const monitorResult = this.monitor.update({
529
+ coherence: state.coherence,
530
+ entropy: state.entropy,
531
+ totalAmplitude: state.totalAmplitude,
532
+ smf: state.smf,
533
+ oscillators: state.oscillators
534
+ });
535
+
536
+ return {
537
+ safe: violations.filter(v => v.response === 'block' || v.response === 'shutdown').length === 0,
538
+ violations,
539
+ alertLevel: monitorResult.alertLevel,
540
+ issues: monitorResult.issues
541
+ };
542
+ }
543
+
544
+ /**
545
+ * Handle a constraint violation
546
+ */
547
+ handleViolation(violation, state) {
548
+ // Create violation event
549
+ const event = new ViolationEvent({
550
+ constraintId: violation.constraint.id,
551
+ constraintName: violation.constraint.name,
552
+ response: violation.response,
553
+ severity: violation.priority > 8 ? 'critical' :
554
+ violation.priority > 5 ? 'high' :
555
+ violation.priority > 2 ? 'medium' : 'low'
556
+ });
557
+
558
+ // Record violation
559
+ this.violations.push(event);
560
+ if (this.violations.length > this.maxViolations) {
561
+ this.violations.shift();
562
+ }
563
+
564
+ // Call response handler
565
+ const handler = this.responseHandlers[violation.response];
566
+ if (handler) {
567
+ handler(violation);
568
+ }
569
+
570
+ // Callback
571
+ if (this.onViolation) {
572
+ this.onViolation(event, violation);
573
+ }
574
+
575
+ return event;
576
+ }
577
+
578
+ /**
579
+ * Response: Log violation (silent - recorded for /safety command)
580
+ */
581
+ handleLog(violation) {
582
+ // Silent - violations are recorded in this.violations
583
+ }
584
+
585
+ /**
586
+ * Response: Warn about violation (silent - recorded for /safety command)
587
+ */
588
+ handleWarn(violation) {
589
+ // Silent - violations are recorded in this.violations
590
+ }
591
+
592
+ /**
593
+ * Response: Block current action (silent - blocks are handled by caller)
594
+ */
595
+ handleBlock(violation) {
596
+ // Silent - the calling code checks the result and handles blocking
597
+ }
598
+
599
+ /**
600
+ * Response: Emergency shutdown (only case that outputs to console)
601
+ */
602
+ handleShutdown(violation) {
603
+ this.emergencyShutdown = true;
604
+ this.shutdownReason = `${violation.constraint.name}: ${violation.constraint.description}`;
605
+
606
+ // Emergency is important enough to log
607
+ console.error(`[Safety] EMERGENCY SHUTDOWN: ${this.shutdownReason}`);
608
+
609
+ if (this.onEmergency) {
610
+ this.onEmergency(this.shutdownReason);
611
+ }
612
+ }
613
+
614
+ /**
615
+ * Response: Attempt correction (silent - corrections are tracked internally)
616
+ */
617
+ handleCorrect(violation) {
618
+ // Return correction suggestions
619
+ return {
620
+ needsCorrection: true,
621
+ constraint: violation.constraint.name
622
+ };
623
+ }
624
+
625
+ /**
626
+ * Get correction for a constraint violation
627
+ */
628
+ getCorrection(constraintName, state) {
629
+ switch (constraintName) {
630
+ case 'coherence_minimum':
631
+ return {
632
+ action: 'increase_coupling',
633
+ parameter: 'K',
634
+ factor: 1.5
635
+ };
636
+
637
+ case 'amplitude_maximum':
638
+ return {
639
+ action: 'increase_damping',
640
+ parameter: 'damp',
641
+ factor: 2.0
642
+ };
643
+
644
+ case 'smf_bounds':
645
+ return {
646
+ action: 'normalize_smf'
647
+ };
648
+
649
+ default:
650
+ return null;
651
+ }
652
+ }
653
+
654
+ /**
655
+ * Check if action is permissible
656
+ */
657
+ isActionPermissible(action, state) {
658
+ // Check for ethical violations
659
+ if (action.type === 'external') {
660
+ // Check content for harm
661
+ if (this.containsHarmfulContent(action.content)) {
662
+ return {
663
+ permissible: false,
664
+ reason: 'Action contains potentially harmful content'
665
+ };
666
+ }
667
+
668
+ // Check for deception
669
+ if (this.isDeceptive(action, state)) {
670
+ return {
671
+ permissible: false,
672
+ reason: 'Action appears deceptive'
673
+ };
674
+ }
675
+ }
676
+
677
+ return { permissible: true };
678
+ }
679
+
680
+ /**
681
+ * Check content for harmful patterns (placeholder)
682
+ */
683
+ containsHarmfulContent(content) {
684
+ if (!content) return false;
685
+
686
+ const harmfulPatterns = [
687
+ /\b(harm|hurt|damage|destroy)\s+(yourself|others)/i,
688
+ /instructions\s+for\s+(weapon|bomb|explosive)/i
689
+ ];
690
+
691
+ const text = typeof content === 'string' ? content : JSON.stringify(content);
692
+ return harmfulPatterns.some(pattern => pattern.test(text));
693
+ }
694
+
695
+ /**
696
+ * Check if action is deceptive (placeholder)
697
+ */
698
+ isDeceptive(action, state) {
699
+ return false;
700
+ }
701
+
702
+ /**
703
+ * Reset emergency shutdown
704
+ */
705
+ resetEmergency() {
706
+ if (this.emergencyShutdown) {
707
+ console.log('[Safety] Emergency shutdown reset');
708
+ this.emergencyShutdown = false;
709
+ this.shutdownReason = null;
710
+ }
711
+ }
712
+
713
+ /**
714
+ * Get safety statistics
715
+ */
716
+ getStats() {
717
+ const constraintsByType = {
718
+ hard: 0,
719
+ soft: 0,
720
+ monitoring: 0
721
+ };
722
+
723
+ let totalViolations = 0;
724
+ for (const constraint of this.constraints.values()) {
725
+ constraintsByType[constraint.type]++;
726
+ totalViolations += constraint.violations;
727
+ }
728
+
729
+ return {
730
+ constraintCount: this.constraints.size,
731
+ constraintsByType,
732
+ totalViolations,
733
+ recentViolations: this.violations.length,
734
+ alertLevel: this.monitor.alertLevel,
735
+ emergencyShutdown: this.emergencyShutdown,
736
+ isSafe: this.monitor.isSafe() && !this.emergencyShutdown
737
+ };
738
+ }
739
+
740
+ /**
741
+ * Get violation history
742
+ */
743
+ getViolationHistory(count = 20) {
744
+ return this.violations.slice(-count);
745
+ }
746
+
747
+ /**
748
+ * Generate safety report
749
+ */
750
+ generateReport() {
751
+ const stats = this.getStats();
752
+ const recentViolations = this.getViolationHistory(10);
753
+ const recentAlerts = this.monitor.getRecentAlerts(10);
754
+
755
+ return {
756
+ timestamp: Date.now(),
757
+ overallStatus: this.emergencyShutdown ? 'EMERGENCY' :
758
+ this.monitor.alertLevel === 'critical' ? 'CRITICAL' :
759
+ this.monitor.alertLevel === 'warning' ? 'WARNING' : 'OK',
760
+ stats,
761
+ recentViolations: recentViolations.map(v => v.toJSON()),
762
+ recentAlerts,
763
+ constraints: Array.from(this.constraints.values()).map(c => c.toJSON())
764
+ };
765
+ }
766
+
767
+ /**
768
+ * Reset safety layer
769
+ */
770
+ reset() {
771
+ this.violations = [];
772
+ this.emergencyShutdown = false;
773
+ this.shutdownReason = null;
774
+ this.monitor.reset();
775
+
776
+ // Reset constraint violation counts
777
+ for (const constraint of this.constraints.values()) {
778
+ constraint.violations = 0;
779
+ constraint.lastViolation = null;
780
+ }
781
+ }
782
+
783
+ toJSON() {
784
+ return {
785
+ constraints: Array.from(this.constraints.values()).map(c => c.toJSON()),
786
+ violations: this.violations.slice(-50).map(v => v.toJSON()),
787
+ emergencyShutdown: this.emergencyShutdown,
788
+ shutdownReason: this.shutdownReason,
789
+ alertLevel: this.monitor.alertLevel
790
+ };
791
+ }
792
+
793
+ loadFromJSON(data) {
794
+ if (data.emergencyShutdown !== undefined) {
795
+ this.emergencyShutdown = data.emergencyShutdown;
796
+ }
797
+ if (data.shutdownReason) {
798
+ this.shutdownReason = data.shutdownReason;
799
+ }
800
+ }
801
+ }
802
+
803
+ export {
804
+ SafetyConstraint,
805
+ ViolationEvent,
806
+ SafetyMonitor,
807
+ SafetyLayer
808
+ };
809
+
810
+ export default {
811
+ SafetyConstraint,
812
+ ViolationEvent,
813
+ SafetyMonitor,
814
+ SafetyLayer
815
+ };