@trillboards/edge-core 0.1.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 (49) hide show
  1. package/dist/DeviceIdentity.d.ts +32 -0
  2. package/dist/DeviceIdentity.d.ts.map +1 -0
  3. package/dist/DeviceIdentity.js +131 -0
  4. package/dist/DeviceIdentity.js.map +1 -0
  5. package/dist/DeviceProfile.d.ts +39 -0
  6. package/dist/DeviceProfile.d.ts.map +1 -0
  7. package/dist/DeviceProfile.js +199 -0
  8. package/dist/DeviceProfile.js.map +1 -0
  9. package/dist/ObservationSignalClassifier.d.ts +33 -0
  10. package/dist/ObservationSignalClassifier.d.ts.map +1 -0
  11. package/dist/ObservationSignalClassifier.js +116 -0
  12. package/dist/ObservationSignalClassifier.js.map +1 -0
  13. package/dist/RecoveryLadder.d.ts +58 -0
  14. package/dist/RecoveryLadder.d.ts.map +1 -0
  15. package/dist/RecoveryLadder.js +199 -0
  16. package/dist/RecoveryLadder.js.map +1 -0
  17. package/dist/SensingConfig.d.ts +33 -0
  18. package/dist/SensingConfig.d.ts.map +1 -0
  19. package/dist/SensingConfig.js +179 -0
  20. package/dist/SensingConfig.js.map +1 -0
  21. package/dist/SignalBuffer.d.ts +56 -0
  22. package/dist/SignalBuffer.d.ts.map +1 -0
  23. package/dist/SignalBuffer.js +143 -0
  24. package/dist/SignalBuffer.js.map +1 -0
  25. package/dist/SocketManager.d.ts +73 -0
  26. package/dist/SocketManager.d.ts.map +1 -0
  27. package/dist/SocketManager.js +160 -0
  28. package/dist/SocketManager.js.map +1 -0
  29. package/dist/SubsystemHealthRegistry.d.ts +61 -0
  30. package/dist/SubsystemHealthRegistry.d.ts.map +1 -0
  31. package/dist/SubsystemHealthRegistry.js +230 -0
  32. package/dist/SubsystemHealthRegistry.js.map +1 -0
  33. package/dist/TelemetryScheduler.d.ts +56 -0
  34. package/dist/TelemetryScheduler.d.ts.map +1 -0
  35. package/dist/TelemetryScheduler.js +99 -0
  36. package/dist/TelemetryScheduler.js.map +1 -0
  37. package/dist/VASWeightConfig.d.ts +26 -0
  38. package/dist/VASWeightConfig.d.ts.map +1 -0
  39. package/dist/VASWeightConfig.js +83 -0
  40. package/dist/VASWeightConfig.js.map +1 -0
  41. package/dist/index.d.ts +15 -0
  42. package/dist/index.d.ts.map +1 -0
  43. package/dist/index.js +44 -0
  44. package/dist/index.js.map +1 -0
  45. package/dist/types.d.ts +530 -0
  46. package/dist/types.d.ts.map +1 -0
  47. package/dist/types.js +96 -0
  48. package/dist/types.js.map +1 -0
  49. package/package.json +40 -0
@@ -0,0 +1,32 @@
1
+ /**
2
+ * DeviceIdentity — Cross-platform device fingerprinting.
3
+ * Port of DeviceIdentity.kt with Linux/Windows support.
4
+ *
5
+ * Linux: SHA-256 of /etc/machine-id + CPU model
6
+ * Windows: SHA-256 of registry MachineGuid + CPU model
7
+ * Persisted to ~/.trillboards/identity.json for stability.
8
+ */
9
+ import type { DeviceIdentityInfo, PlatformAdapter } from './types';
10
+ export declare class DeviceIdentity {
11
+ private identityInfo;
12
+ private platform;
13
+ constructor(platform: PlatformAdapter);
14
+ /**
15
+ * Generate or load a stable device fingerprint.
16
+ * SHA-256 hash of platform-specific machine identifiers.
17
+ */
18
+ getFingerprint(): Promise<string>;
19
+ /**
20
+ * Ephemeral session ID — new UUID each process launch.
21
+ * NOT persisted. Used for session-level tracking.
22
+ */
23
+ getSessionId(): string;
24
+ /**
25
+ * Full identity info including fingerprint, session, platform.
26
+ */
27
+ getIdentityInfo(): Promise<DeviceIdentityInfo>;
28
+ private detectPlatform;
29
+ private loadPersistedIdentity;
30
+ private persistIdentity;
31
+ }
32
+ //# sourceMappingURL=DeviceIdentity.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"DeviceIdentity.d.ts","sourceRoot":"","sources":["../src/DeviceIdentity.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAOH,OAAO,KAAK,EAAE,kBAAkB,EAAE,eAAe,EAAE,MAAM,SAAS,CAAC;AAKnE,qBAAa,cAAc;IACzB,OAAO,CAAC,YAAY,CAAmC;IACvD,OAAO,CAAC,QAAQ,CAAkB;gBAEtB,QAAQ,EAAE,eAAe;IAIrC;;;OAGG;IACG,cAAc,IAAI,OAAO,CAAC,MAAM,CAAC;IAKvC;;;OAGG;IACH,YAAY,IAAI,MAAM;IAOtB;;OAEG;IACG,eAAe,IAAI,OAAO,CAAC,kBAAkB,CAAC;IAmCpD,OAAO,CAAC,cAAc;IAOtB,OAAO,CAAC,qBAAqB;IAY7B,OAAO,CAAC,eAAe;CAUxB"}
@@ -0,0 +1,131 @@
1
+ "use strict";
2
+ /**
3
+ * DeviceIdentity — Cross-platform device fingerprinting.
4
+ * Port of DeviceIdentity.kt with Linux/Windows support.
5
+ *
6
+ * Linux: SHA-256 of /etc/machine-id + CPU model
7
+ * Windows: SHA-256 of registry MachineGuid + CPU model
8
+ * Persisted to ~/.trillboards/identity.json for stability.
9
+ */
10
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
11
+ if (k2 === undefined) k2 = k;
12
+ var desc = Object.getOwnPropertyDescriptor(m, k);
13
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
14
+ desc = { enumerable: true, get: function() { return m[k]; } };
15
+ }
16
+ Object.defineProperty(o, k2, desc);
17
+ }) : (function(o, m, k, k2) {
18
+ if (k2 === undefined) k2 = k;
19
+ o[k2] = m[k];
20
+ }));
21
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
22
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
23
+ }) : function(o, v) {
24
+ o["default"] = v;
25
+ });
26
+ var __importStar = (this && this.__importStar) || function (mod) {
27
+ if (mod && mod.__esModule) return mod;
28
+ var result = {};
29
+ if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
30
+ __setModuleDefault(result, mod);
31
+ return result;
32
+ };
33
+ Object.defineProperty(exports, "__esModule", { value: true });
34
+ exports.DeviceIdentity = void 0;
35
+ const crypto = __importStar(require("crypto"));
36
+ const fs = __importStar(require("fs"));
37
+ const path = __importStar(require("path"));
38
+ const os = __importStar(require("os"));
39
+ const uuid_1 = require("uuid");
40
+ const IDENTITY_DIR = path.join(os.homedir(), '.trillboards');
41
+ const IDENTITY_FILE = path.join(IDENTITY_DIR, 'identity.json');
42
+ class DeviceIdentity {
43
+ constructor(platform) {
44
+ this.identityInfo = null;
45
+ this.platform = platform;
46
+ }
47
+ /**
48
+ * Generate or load a stable device fingerprint.
49
+ * SHA-256 hash of platform-specific machine identifiers.
50
+ */
51
+ async getFingerprint() {
52
+ const info = await this.getIdentityInfo();
53
+ return info.fingerprint;
54
+ }
55
+ /**
56
+ * Ephemeral session ID — new UUID each process launch.
57
+ * NOT persisted. Used for session-level tracking.
58
+ */
59
+ getSessionId() {
60
+ if (!this.identityInfo) {
61
+ return (0, uuid_1.v4)();
62
+ }
63
+ return this.identityInfo.sessionId;
64
+ }
65
+ /**
66
+ * Full identity info including fingerprint, session, platform.
67
+ */
68
+ async getIdentityInfo() {
69
+ if (this.identityInfo) {
70
+ return this.identityInfo;
71
+ }
72
+ // Try to load persisted fingerprint
73
+ const persisted = this.loadPersistedIdentity();
74
+ if (persisted) {
75
+ // Reuse fingerprint, but generate fresh sessionId
76
+ this.identityInfo = {
77
+ ...persisted,
78
+ sessionId: (0, uuid_1.v4)(),
79
+ };
80
+ return this.identityInfo;
81
+ }
82
+ // Generate new fingerprint
83
+ const machineId = await this.platform.getMachineId();
84
+ const cpuInfo = this.platform.getCpuInfo();
85
+ const raw = `${machineId}|${cpuInfo.model}|${cpuInfo.architecture}`;
86
+ const fingerprint = crypto.createHash('sha256').update(raw).digest('hex');
87
+ const platformName = this.detectPlatform();
88
+ this.identityInfo = {
89
+ fingerprint,
90
+ sessionId: (0, uuid_1.v4)(),
91
+ platform: platformName,
92
+ machineId,
93
+ createdAt: Date.now(),
94
+ };
95
+ this.persistIdentity(this.identityInfo);
96
+ return this.identityInfo;
97
+ }
98
+ detectPlatform() {
99
+ const p = os.platform();
100
+ if (p === 'linux')
101
+ return 'linux';
102
+ if (p === 'win32')
103
+ return 'windows';
104
+ return 'linux'; // default for cross-platform
105
+ }
106
+ loadPersistedIdentity() {
107
+ try {
108
+ if (fs.existsSync(IDENTITY_FILE)) {
109
+ const data = fs.readFileSync(IDENTITY_FILE, 'utf-8');
110
+ return JSON.parse(data);
111
+ }
112
+ }
113
+ catch {
114
+ // Corrupted file — regenerate
115
+ }
116
+ return null;
117
+ }
118
+ persistIdentity(info) {
119
+ try {
120
+ if (!fs.existsSync(IDENTITY_DIR)) {
121
+ fs.mkdirSync(IDENTITY_DIR, { recursive: true });
122
+ }
123
+ fs.writeFileSync(IDENTITY_FILE, JSON.stringify(info, null, 2));
124
+ }
125
+ catch {
126
+ // Non-fatal — fingerprint still works in-memory
127
+ }
128
+ }
129
+ }
130
+ exports.DeviceIdentity = DeviceIdentity;
131
+ //# sourceMappingURL=DeviceIdentity.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"DeviceIdentity.js","sourceRoot":"","sources":["../src/DeviceIdentity.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,39 @@
1
+ /**
2
+ * DeviceProfile — Capability matrix for cross-platform devices.
3
+ * Port of DeviceProfile.kt, extended for Linux/Windows tiers.
4
+ *
5
+ * Tiers:
6
+ * TIER_1: 6+ TOPS NPU, 8GB+ — ALL models always ON (e.g., RK3588)
7
+ * TIER_2: 4-8GB RAM — most models ON (e.g., Backpackster i7-7500U, Snapdragon 6xx+)
8
+ * TIER_3: 2-4GB RAM — basic models, aggressive shedding
9
+ * TIER_4: <2GB — display only, NO ML
10
+ *
11
+ * Extended for cross-platform:
12
+ * - Linux x86_64 with OpenVINO → TIER_2 (Backpackster)
13
+ * - Windows with DirectML GPU → TIER_1 or TIER_2
14
+ * - Windows CPU-only → TIER_2 or TIER_3
15
+ */
16
+ import { CapabilityTier, ModelAvailability, AttenuationTier, type DeviceCapabilityMatrix, type PlatformAdapter } from './types';
17
+ /**
18
+ * Compute capability tier from runtime capabilities.
19
+ * Runtime capabilities are the PRIMARY signal, not hardware strings.
20
+ */
21
+ export declare function computeTier(platform: PlatformAdapter): CapabilityTier;
22
+ /**
23
+ * Get the full capability matrix for a given tier.
24
+ */
25
+ export declare function getCapabilityMatrix(tier: CapabilityTier): DeviceCapabilityMatrix;
26
+ /**
27
+ * Check if a model should be active given current attenuation tier.
28
+ */
29
+ export declare function isModelActive(availability: ModelAvailability, currentAttenuation: AttenuationTier): boolean;
30
+ /**
31
+ * Get the order in which models should be shed under memory pressure.
32
+ * Last in array = shed first.
33
+ */
34
+ export declare function getShedOrder(): string[];
35
+ /**
36
+ * Determine current attenuation tier from memory free percentage.
37
+ */
38
+ export declare function getAttenuationTier(matrix: DeviceCapabilityMatrix, freeMemoryPercent: number): AttenuationTier;
39
+ //# sourceMappingURL=DeviceProfile.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"DeviceProfile.d.ts","sourceRoot":"","sources":["../src/DeviceProfile.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AAEH,OAAO,EACL,cAAc,EACd,iBAAiB,EACjB,eAAe,EACf,KAAK,sBAAsB,EAC3B,KAAK,eAAe,EACrB,MAAM,SAAS,CAAC;AA4GjB;;;GAGG;AACH,wBAAgB,WAAW,CAAC,QAAQ,EAAE,eAAe,GAAG,cAAc,CAqBrE;AAED;;GAEG;AACH,wBAAgB,mBAAmB,CAAC,IAAI,EAAE,cAAc,GAAG,sBAAsB,CAWhF;AAED;;GAEG;AACH,wBAAgB,aAAa,CAC3B,YAAY,EAAE,iBAAiB,EAC/B,kBAAkB,EAAE,eAAe,GAClC,OAAO,CAcT;AAED;;;GAGG;AACH,wBAAgB,YAAY,IAAI,MAAM,EAAE,CAEvC;AAED;;GAEG;AACH,wBAAgB,kBAAkB,CAChC,MAAM,EAAE,sBAAsB,EAC9B,iBAAiB,EAAE,MAAM,GACxB,eAAe,CAMjB"}
@@ -0,0 +1,199 @@
1
+ "use strict";
2
+ /**
3
+ * DeviceProfile — Capability matrix for cross-platform devices.
4
+ * Port of DeviceProfile.kt, extended for Linux/Windows tiers.
5
+ *
6
+ * Tiers:
7
+ * TIER_1: 6+ TOPS NPU, 8GB+ — ALL models always ON (e.g., RK3588)
8
+ * TIER_2: 4-8GB RAM — most models ON (e.g., Backpackster i7-7500U, Snapdragon 6xx+)
9
+ * TIER_3: 2-4GB RAM — basic models, aggressive shedding
10
+ * TIER_4: <2GB — display only, NO ML
11
+ *
12
+ * Extended for cross-platform:
13
+ * - Linux x86_64 with OpenVINO → TIER_2 (Backpackster)
14
+ * - Windows with DirectML GPU → TIER_1 or TIER_2
15
+ * - Windows CPU-only → TIER_2 or TIER_3
16
+ */
17
+ Object.defineProperty(exports, "__esModule", { value: true });
18
+ exports.getAttenuationTier = exports.getShedOrder = exports.isModelActive = exports.getCapabilityMatrix = exports.computeTier = void 0;
19
+ const types_1 = require("./types");
20
+ /** Tier-specific model shed order: last to shed → first to shed */
21
+ const SHED_ORDER = [
22
+ 'footTraffic',
23
+ 'ageGender',
24
+ 'gazeTracking',
25
+ 'poseEngagement',
26
+ 'ferEmotion',
27
+ 'whisperAsr',
28
+ 'yamnetAudio',
29
+ 'blazeFace',
30
+ ];
31
+ // ─── Preset Capability Matrices ─────────────────────────────────────────────
32
+ const TIER_1_MATRIX = {
33
+ tier: types_1.CapabilityTier.TIER_1,
34
+ description: 'High-end NPU (RK3588) or dedicated GPU — ALL models always ON',
35
+ targetFps: 12,
36
+ resolution: { width: 480, height: 360 },
37
+ blazeFace: types_1.ModelAvailability.ALWAYS_ON,
38
+ yamnetAudio: types_1.ModelAvailability.ALWAYS_ON,
39
+ whisperAsr: types_1.ModelAvailability.ALWAYS_ON,
40
+ ferEmotion: types_1.ModelAvailability.ALWAYS_ON,
41
+ poseEngagement: types_1.ModelAvailability.ALWAYS_ON,
42
+ ageGender: types_1.ModelAvailability.ALWAYS_ON,
43
+ gazeTracking: types_1.ModelAvailability.ALWAYS_ON,
44
+ geminiVisionCadenceSeconds: 300,
45
+ geminiVisionFaceDetectedCadenceSeconds: 30,
46
+ federatedLearningEnabled: true,
47
+ federatedSparseUpload: false,
48
+ attenuationEnabled: false,
49
+ attenuationMediumThreshold: 0,
50
+ attenuationHighThreshold: 0,
51
+ attenuationCriticalThreshold: 0,
52
+ maxRecoveryLevel: 6,
53
+ };
54
+ const TIER_2_MATRIX = {
55
+ tier: types_1.CapabilityTier.TIER_2,
56
+ description: 'Mid-range (Backpackster i7, Snapdragon 6xx+, Windows mini-PC) — most models ON',
57
+ targetFps: 8,
58
+ resolution: { width: 480, height: 360 },
59
+ blazeFace: types_1.ModelAvailability.ALWAYS_ON,
60
+ yamnetAudio: types_1.ModelAvailability.ALWAYS_ON,
61
+ whisperAsr: types_1.ModelAvailability.ALWAYS_ON,
62
+ ferEmotion: types_1.ModelAvailability.SHED_AT_HIGH,
63
+ poseEngagement: types_1.ModelAvailability.SHED_AT_HIGH,
64
+ ageGender: types_1.ModelAvailability.SHED_AT_MEDIUM,
65
+ gazeTracking: types_1.ModelAvailability.SHED_AT_HIGH,
66
+ geminiVisionCadenceSeconds: 300,
67
+ geminiVisionFaceDetectedCadenceSeconds: 30,
68
+ federatedLearningEnabled: true,
69
+ federatedSparseUpload: false,
70
+ attenuationEnabled: true,
71
+ attenuationMediumThreshold: 30,
72
+ attenuationHighThreshold: 20,
73
+ attenuationCriticalThreshold: 10,
74
+ maxRecoveryLevel: 5,
75
+ };
76
+ const TIER_3_MATRIX = {
77
+ tier: types_1.CapabilityTier.TIER_3,
78
+ description: 'Budget (2-4GB RAM) — basic models, aggressive shedding',
79
+ targetFps: 4,
80
+ resolution: { width: 320, height: 240 },
81
+ blazeFace: types_1.ModelAvailability.ALWAYS_ON,
82
+ yamnetAudio: types_1.ModelAvailability.ALWAYS_ON,
83
+ whisperAsr: types_1.ModelAvailability.SHED_AT_MEDIUM,
84
+ ferEmotion: types_1.ModelAvailability.SHED_AT_MEDIUM,
85
+ poseEngagement: types_1.ModelAvailability.OFF,
86
+ ageGender: types_1.ModelAvailability.OFF,
87
+ gazeTracking: types_1.ModelAvailability.OFF,
88
+ geminiVisionCadenceSeconds: 300,
89
+ geminiVisionFaceDetectedCadenceSeconds: 30,
90
+ federatedLearningEnabled: true,
91
+ federatedSparseUpload: true, // top 5% only
92
+ attenuationEnabled: true,
93
+ attenuationMediumThreshold: 40,
94
+ attenuationHighThreshold: 25,
95
+ attenuationCriticalThreshold: 15,
96
+ maxRecoveryLevel: 4,
97
+ };
98
+ const TIER_4_MATRIX = {
99
+ tier: types_1.CapabilityTier.TIER_4,
100
+ description: 'Legacy (<2GB) — display only, NO ML',
101
+ targetFps: 0,
102
+ resolution: { width: 320, height: 240 },
103
+ blazeFace: types_1.ModelAvailability.OFF,
104
+ yamnetAudio: types_1.ModelAvailability.OFF,
105
+ whisperAsr: types_1.ModelAvailability.OFF,
106
+ ferEmotion: types_1.ModelAvailability.OFF,
107
+ poseEngagement: types_1.ModelAvailability.OFF,
108
+ ageGender: types_1.ModelAvailability.OFF,
109
+ gazeTracking: types_1.ModelAvailability.OFF,
110
+ geminiVisionCadenceSeconds: 600,
111
+ geminiVisionFaceDetectedCadenceSeconds: 60,
112
+ federatedLearningEnabled: false,
113
+ federatedSparseUpload: false,
114
+ attenuationEnabled: false,
115
+ attenuationMediumThreshold: 0,
116
+ attenuationHighThreshold: 0,
117
+ attenuationCriticalThreshold: 0,
118
+ maxRecoveryLevel: 2,
119
+ };
120
+ /**
121
+ * Compute capability tier from runtime capabilities.
122
+ * Runtime capabilities are the PRIMARY signal, not hardware strings.
123
+ */
124
+ function computeTier(platform) {
125
+ const mem = platform.getMemoryInfo();
126
+ const cpu = platform.getCpuInfo();
127
+ // TIER_1: 8GB+ RAM and 6+ cores (or known NPU hardware)
128
+ if (mem.totalMb >= 8192 && cpu.cores >= 6) {
129
+ return types_1.CapabilityTier.TIER_1;
130
+ }
131
+ // TIER_2: 4GB+ RAM and 4+ cores (Backpackster: 16GB, i7-7500U 4 threads)
132
+ if (mem.totalMb >= 3072 && cpu.cores >= 4) {
133
+ return types_1.CapabilityTier.TIER_2;
134
+ }
135
+ // TIER_3: 2GB+ RAM
136
+ if (mem.totalMb >= 2048) {
137
+ return types_1.CapabilityTier.TIER_3;
138
+ }
139
+ // TIER_4: <2GB
140
+ return types_1.CapabilityTier.TIER_4;
141
+ }
142
+ exports.computeTier = computeTier;
143
+ /**
144
+ * Get the full capability matrix for a given tier.
145
+ */
146
+ function getCapabilityMatrix(tier) {
147
+ switch (tier) {
148
+ case types_1.CapabilityTier.TIER_1:
149
+ return { ...TIER_1_MATRIX };
150
+ case types_1.CapabilityTier.TIER_2:
151
+ return { ...TIER_2_MATRIX };
152
+ case types_1.CapabilityTier.TIER_3:
153
+ return { ...TIER_3_MATRIX };
154
+ case types_1.CapabilityTier.TIER_4:
155
+ return { ...TIER_4_MATRIX };
156
+ }
157
+ }
158
+ exports.getCapabilityMatrix = getCapabilityMatrix;
159
+ /**
160
+ * Check if a model should be active given current attenuation tier.
161
+ */
162
+ function isModelActive(availability, currentAttenuation) {
163
+ switch (availability) {
164
+ case types_1.ModelAvailability.ALWAYS_ON:
165
+ return true;
166
+ case types_1.ModelAvailability.SHED_AT_MEDIUM:
167
+ return currentAttenuation === types_1.AttenuationTier.NONE;
168
+ case types_1.ModelAvailability.SHED_AT_HIGH:
169
+ return (currentAttenuation === types_1.AttenuationTier.NONE ||
170
+ currentAttenuation === types_1.AttenuationTier.MEDIUM);
171
+ case types_1.ModelAvailability.OFF:
172
+ return false;
173
+ }
174
+ }
175
+ exports.isModelActive = isModelActive;
176
+ /**
177
+ * Get the order in which models should be shed under memory pressure.
178
+ * Last in array = shed first.
179
+ */
180
+ function getShedOrder() {
181
+ return [...SHED_ORDER];
182
+ }
183
+ exports.getShedOrder = getShedOrder;
184
+ /**
185
+ * Determine current attenuation tier from memory free percentage.
186
+ */
187
+ function getAttenuationTier(matrix, freeMemoryPercent) {
188
+ if (!matrix.attenuationEnabled)
189
+ return types_1.AttenuationTier.NONE;
190
+ if (freeMemoryPercent <= matrix.attenuationCriticalThreshold)
191
+ return types_1.AttenuationTier.CRITICAL;
192
+ if (freeMemoryPercent <= matrix.attenuationHighThreshold)
193
+ return types_1.AttenuationTier.HIGH;
194
+ if (freeMemoryPercent <= matrix.attenuationMediumThreshold)
195
+ return types_1.AttenuationTier.MEDIUM;
196
+ return types_1.AttenuationTier.NONE;
197
+ }
198
+ exports.getAttenuationTier = getAttenuationTier;
199
+ //# sourceMappingURL=DeviceProfile.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"DeviceProfile.js","sourceRoot":"","sources":["../src/DeviceProfile.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,33 @@
1
+ /**
2
+ * ObservationSignalClassifier — Signal taxonomy and classification.
3
+ * 1:1 port of ObservationSignalClassifier.kt.
4
+ *
5
+ * Classifies each aggregation window into:
6
+ * - observationFamily: SCREEN_AUDIENCE | AMBIENT_COMMERCE_INTENT | DIAGNOSTIC_PROBE | EMPTY_SCENE
7
+ * - evidenceGrade: FACE_BACKED | PERSON_BACKED | SPEECH_ONLY | PROXY_ONLY | EMPTY
8
+ * - decisionability: DECISIONABLE | NON_DECISIONABLE
9
+ * - measurementQuality: face_backed | ambient_proxy_only | proxy_only | empty_scene
10
+ */
11
+ import type { ObservationSignalClassification } from './types';
12
+ export interface ClassificationInput {
13
+ avgFaceCountWindow: number;
14
+ currentFaceCount: number;
15
+ avgPersonCountWindow: number;
16
+ currentPersonCount: number;
17
+ speechSignalsPresent: boolean;
18
+ proxySignalsPresent: boolean;
19
+ proxySignalMismatch: boolean;
20
+ captureMode?: string;
21
+ }
22
+ /**
23
+ * Classify an observation window. Logic matches Kotlin exactly:
24
+ *
25
+ * 1. Compute baseEvidenceGrade from sensor signals
26
+ * 2. Downgrade to PROXY_ONLY if captureMode is probe and not face-backed
27
+ * 3. Determine measurementQuality (proxySignalMismatch demotes to proxy_only)
28
+ * 4. Determine observationFamily (captureMode checked FIRST)
29
+ * 5. Determine decisionability
30
+ * 6. Collect per-grade decision block reasons (one reason per grade, not multiple)
31
+ */
32
+ export declare function classifyObservationSignal(input: ClassificationInput): ObservationSignalClassification;
33
+ //# sourceMappingURL=ObservationSignalClassifier.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ObservationSignalClassifier.d.ts","sourceRoot":"","sources":["../src/ObservationSignalClassifier.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAEH,OAAO,KAAK,EAIV,+BAA+B,EAChC,MAAM,SAAS,CAAC;AAEjB,MAAM,WAAW,mBAAmB;IAClC,kBAAkB,EAAE,MAAM,CAAC;IAC3B,gBAAgB,EAAE,MAAM,CAAC;IACzB,oBAAoB,EAAE,MAAM,CAAC;IAC7B,kBAAkB,EAAE,MAAM,CAAC;IAC3B,oBAAoB,EAAE,OAAO,CAAC;IAC9B,mBAAmB,EAAE,OAAO,CAAC;IAC7B,mBAAmB,EAAE,OAAO,CAAC;IAC7B,WAAW,CAAC,EAAE,MAAM,CAAC;CACtB;AAED;;;;;;;;;GASG;AACH,wBAAgB,yBAAyB,CACvC,KAAK,EAAE,mBAAmB,GACzB,+BAA+B,CAgGjC"}
@@ -0,0 +1,116 @@
1
+ "use strict";
2
+ /**
3
+ * ObservationSignalClassifier — Signal taxonomy and classification.
4
+ * 1:1 port of ObservationSignalClassifier.kt.
5
+ *
6
+ * Classifies each aggregation window into:
7
+ * - observationFamily: SCREEN_AUDIENCE | AMBIENT_COMMERCE_INTENT | DIAGNOSTIC_PROBE | EMPTY_SCENE
8
+ * - evidenceGrade: FACE_BACKED | PERSON_BACKED | SPEECH_ONLY | PROXY_ONLY | EMPTY
9
+ * - decisionability: DECISIONABLE | NON_DECISIONABLE
10
+ * - measurementQuality: face_backed | ambient_proxy_only | proxy_only | empty_scene
11
+ */
12
+ Object.defineProperty(exports, "__esModule", { value: true });
13
+ exports.classifyObservationSignal = void 0;
14
+ /**
15
+ * Classify an observation window. Logic matches Kotlin exactly:
16
+ *
17
+ * 1. Compute baseEvidenceGrade from sensor signals
18
+ * 2. Downgrade to PROXY_ONLY if captureMode is probe and not face-backed
19
+ * 3. Determine measurementQuality (proxySignalMismatch demotes to proxy_only)
20
+ * 4. Determine observationFamily (captureMode checked FIRST)
21
+ * 5. Determine decisionability
22
+ * 6. Collect per-grade decision block reasons (one reason per grade, not multiple)
23
+ */
24
+ function classifyObservationSignal(input) {
25
+ // Step 1: Base evidence grade — Kotlin thresholds exactly
26
+ let baseEvidenceGrade;
27
+ if (input.currentFaceCount > 0 || input.avgFaceCountWindow >= 1.0) {
28
+ baseEvidenceGrade = 'FACE_BACKED';
29
+ }
30
+ else if (input.currentPersonCount > 0 ||
31
+ input.avgPersonCountWindow >= 0.75) {
32
+ baseEvidenceGrade = 'PERSON_BACKED';
33
+ }
34
+ else if (input.speechSignalsPresent) {
35
+ baseEvidenceGrade = 'SPEECH_ONLY';
36
+ }
37
+ else if (input.proxySignalsPresent || input.proxySignalMismatch) {
38
+ baseEvidenceGrade = 'PROXY_ONLY';
39
+ }
40
+ else {
41
+ baseEvidenceGrade = 'EMPTY';
42
+ }
43
+ // Step 2: Downgrade for probe mode (Kotlin line 43-47)
44
+ const evidenceGrade = input.captureMode === 'signal_mismatch_probe' &&
45
+ baseEvidenceGrade !== 'FACE_BACKED'
46
+ ? 'PROXY_ONLY'
47
+ : baseEvidenceGrade;
48
+ // Step 3: Measurement quality — Kotlin checks proxySignalMismatch for all non-face grades
49
+ let measurementQuality;
50
+ switch (evidenceGrade) {
51
+ case 'FACE_BACKED':
52
+ measurementQuality = 'face_backed';
53
+ break;
54
+ case 'PERSON_BACKED':
55
+ case 'SPEECH_ONLY':
56
+ case 'PROXY_ONLY':
57
+ measurementQuality = input.proxySignalMismatch
58
+ ? 'proxy_only'
59
+ : 'ambient_proxy_only';
60
+ break;
61
+ default:
62
+ measurementQuality = 'empty_scene';
63
+ }
64
+ // Step 4: Observation family — captureMode checked FIRST (Kotlin line 55-59)
65
+ let observationFamily;
66
+ if (input.captureMode === 'signal_mismatch_probe') {
67
+ observationFamily = 'DIAGNOSTIC_PROBE';
68
+ }
69
+ else if (evidenceGrade === 'FACE_BACKED') {
70
+ observationFamily = 'SCREEN_AUDIENCE';
71
+ }
72
+ else if (evidenceGrade === 'EMPTY') {
73
+ observationFamily = 'EMPTY_SCENE';
74
+ }
75
+ else {
76
+ observationFamily = 'AMBIENT_COMMERCE_INTENT';
77
+ }
78
+ // Step 5: Decisionability
79
+ const decisionability = observationFamily === 'SCREEN_AUDIENCE' &&
80
+ evidenceGrade === 'FACE_BACKED'
81
+ ? 'DECISIONABLE'
82
+ : 'NON_DECISIONABLE';
83
+ // Step 6: Decision block reasons — per-grade, using linkedSet semantics (no dupes)
84
+ const reasons = new Set();
85
+ if (observationFamily === 'DIAGNOSTIC_PROBE') {
86
+ reasons.add('diagnostic_capture');
87
+ }
88
+ if (decisionability === 'NON_DECISIONABLE') {
89
+ switch (evidenceGrade) {
90
+ case 'PERSON_BACKED':
91
+ reasons.add('no_face_confirmation');
92
+ break;
93
+ case 'SPEECH_ONLY':
94
+ reasons.add('no_visual_confirmation');
95
+ break;
96
+ case 'PROXY_ONLY':
97
+ reasons.add('proxy_signal_only');
98
+ break;
99
+ case 'EMPTY':
100
+ reasons.add('empty_scene');
101
+ break;
102
+ }
103
+ }
104
+ if (input.proxySignalMismatch) {
105
+ reasons.add('proxy_signal_mismatch');
106
+ }
107
+ return {
108
+ observationFamily,
109
+ evidenceGrade,
110
+ decisionability,
111
+ decisionBlockReasons: [...reasons],
112
+ measurementQuality,
113
+ };
114
+ }
115
+ exports.classifyObservationSignal = classifyObservationSignal;
116
+ //# sourceMappingURL=ObservationSignalClassifier.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ObservationSignalClassifier.js","sourceRoot":"","sources":["../src/ObservationSignalClassifier.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,58 @@
1
+ /**
2
+ * RecoveryLadder — 6-level autonomous recovery system.
3
+ * Port of RecoveryLadder.kt. Platform-specific recovery actions via callbacks.
4
+ *
5
+ * Levels:
6
+ * 1. Network Drop → Socket.io handles (infinite reconnect with exp backoff)
7
+ * 2. Player Stuck → Reload (3x) → Recreate (3x)
8
+ * 3. Camera/Mic Failure → Unbind/rebind hardware
9
+ * 4. Memory Pressure → Attenuate (shed models, restore)
10
+ * 5. Full Service Restart → Stop + restart service (3x per 24h)
11
+ * 6. OTA Rollback → Server-initiated install of previous version
12
+ */
13
+ import { Subsystem, type RecoveryAction } from './types';
14
+ import { SubsystemHealthRegistry } from './SubsystemHealthRegistry';
15
+ export interface RecoveryCallbacks {
16
+ onReloadPlayer(): Promise<void>;
17
+ onRecreatePlayer(): Promise<void>;
18
+ onRebindCamera(): Promise<void>;
19
+ onRebindMicrophone(): Promise<void>;
20
+ onRestartSensingService(): Promise<void>;
21
+ onRestartDeviceService(): Promise<void>;
22
+ onRollback?(version: string): Promise<void>;
23
+ onEmitRecoveryEvent(event: string, details: Record<string, unknown>): void;
24
+ }
25
+ export declare class RecoveryLadder {
26
+ private healthRegistry;
27
+ private callbacks;
28
+ private maxRecoveryLevel;
29
+ private state;
30
+ private recoveryHistory;
31
+ private checkInterval;
32
+ constructor(healthRegistry: SubsystemHealthRegistry, callbacks: RecoveryCallbacks, maxRecoveryLevel?: number);
33
+ /**
34
+ * Start periodic recovery checks (every 30s, matching Kotlin).
35
+ */
36
+ start(): void;
37
+ /**
38
+ * Stop periodic checks.
39
+ */
40
+ stop(): void;
41
+ /**
42
+ * Check subsystem health and trigger recovery if needed.
43
+ * Called every 30s by the interval, or manually.
44
+ */
45
+ checkAndRecover(): Promise<void>;
46
+ /**
47
+ * Handle a specific subsystem failure with escalating recovery.
48
+ */
49
+ handleFailure(subsystem: Subsystem): Promise<void>;
50
+ private recoverPlayer;
51
+ private recoverHardware;
52
+ private attemptServiceRestart;
53
+ private recordAction;
54
+ private resetAttempts;
55
+ private getDayOfYear;
56
+ getRecoveryHistory(): RecoveryAction[];
57
+ }
58
+ //# sourceMappingURL=RecoveryLadder.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"RecoveryLadder.d.ts","sourceRoot":"","sources":["../src/RecoveryLadder.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAEH,OAAO,EACL,SAAS,EAET,KAAK,cAAc,EACpB,MAAM,SAAS,CAAC;AACjB,OAAO,EAAE,uBAAuB,EAAE,MAAM,2BAA2B,CAAC;AAEpE,MAAM,WAAW,iBAAiB;IAChC,cAAc,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC;IAChC,gBAAgB,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC;IAClC,cAAc,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC;IAChC,kBAAkB,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC;IACpC,uBAAuB,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC;IACzC,sBAAsB,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC;IACxC,UAAU,CAAC,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAC5C,mBAAmB,CAAC,KAAK,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,IAAI,CAAC;CAC5E;AAiBD,qBAAa,cAAc;IACzB,OAAO,CAAC,cAAc,CAA0B;IAChD,OAAO,CAAC,SAAS,CAAoB;IACrC,OAAO,CAAC,gBAAgB,CAAS;IACjC,OAAO,CAAC,KAAK,CAAgB;IAC7B,OAAO,CAAC,eAAe,CAAwB;IAC/C,OAAO,CAAC,aAAa,CAA+C;gBAGlE,cAAc,EAAE,uBAAuB,EACvC,SAAS,EAAE,iBAAiB,EAC5B,gBAAgB,GAAE,MAAU;IAe9B;;OAEG;IACH,KAAK,IAAI,IAAI;IAKb;;OAEG;IACH,IAAI,IAAI,IAAI;IAOZ;;;OAGG;IACG,eAAe,IAAI,OAAO,CAAC,IAAI,CAAC;IAiBtC;;OAEG;IACG,aAAa,CAAC,SAAS,EAAE,SAAS,GAAG,OAAO,CAAC,IAAI,CAAC;YA8C1C,aAAa;YAoBb,eAAe;YAmBf,qBAAqB;IAsBnC,OAAO,CAAC,YAAY;IAqBpB,OAAO,CAAC,aAAa;IAMrB,OAAO,CAAC,YAAY;IAOpB,kBAAkB,IAAI,cAAc,EAAE;CAGvC"}