adaptive-bitmask 1.0.0-rc.1

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,2402 @@
1
+ // src/transports/websocket.ts
2
+ import { WebSocketServer, WebSocket } from "ws";
3
+
4
+ // src/message.ts
5
+ var MESSAGE_SIZE_BYTES = 24;
6
+ var UINT32_MAX = 4294967295;
7
+ var UINT64_MAX = (1n << 64n) - 1n;
8
+ var BitmaskMessage = class _BitmaskMessage {
9
+ mask;
10
+ agentId;
11
+ timestampMs;
12
+ schemaVersion;
13
+ constructor(data) {
14
+ this._assertValid(data);
15
+ this.mask = data.mask;
16
+ this.agentId = data.agentId;
17
+ this.timestampMs = data.timestampMs;
18
+ this.schemaVersion = data.schemaVersion;
19
+ }
20
+ /** Create a message with current timestamp. */
21
+ static now(mask, agentId, schemaVersion) {
22
+ return new _BitmaskMessage({
23
+ mask,
24
+ agentId,
25
+ timestampMs: Date.now(),
26
+ schemaVersion
27
+ });
28
+ }
29
+ /** Wire size in bytes. */
30
+ get sizeBytes() {
31
+ return MESSAGE_SIZE_BYTES;
32
+ }
33
+ /**
34
+ * Serialize to 24-byte ArrayBuffer (little-endian).
35
+ *
36
+ * This is the canonical wire format. gRPC/WebSocket transports
37
+ * should send these bytes directly.
38
+ */
39
+ serialize() {
40
+ const buf = new ArrayBuffer(MESSAGE_SIZE_BYTES);
41
+ const view = new DataView(buf);
42
+ view.setBigUint64(0, this.mask, true);
43
+ view.setUint32(8, this.agentId, true);
44
+ view.setBigInt64(12, BigInt(this.timestampMs), true);
45
+ view.setUint32(20, this.schemaVersion, true);
46
+ return buf;
47
+ }
48
+ /** Serialize to Uint8Array. */
49
+ toBytes() {
50
+ return new Uint8Array(this.serialize());
51
+ }
52
+ /**
53
+ * Deserialize from ArrayBuffer or Uint8Array.
54
+ * Validates length before parsing.
55
+ */
56
+ static deserialize(data) {
57
+ const buf = data instanceof Uint8Array ? data.buffer.slice(data.byteOffset, data.byteOffset + data.byteLength) : data;
58
+ if (buf.byteLength !== MESSAGE_SIZE_BYTES) {
59
+ throw new Error(
60
+ `Invalid message: expected exactly ${MESSAGE_SIZE_BYTES} bytes, got ${buf.byteLength}`
61
+ );
62
+ }
63
+ const view = new DataView(buf);
64
+ return new _BitmaskMessage({
65
+ mask: view.getBigUint64(0, true),
66
+ agentId: view.getUint32(8, true),
67
+ timestampMs: Number(view.getBigInt64(12, true)),
68
+ schemaVersion: view.getUint32(20, true)
69
+ });
70
+ }
71
+ /**
72
+ * JSON-equivalent size for comparison.
73
+ * Useful for demonstrating compression ratio.
74
+ */
75
+ get jsonSize() {
76
+ return JSON.stringify({
77
+ mask: this.mask.toString(),
78
+ agentId: this.agentId,
79
+ timestampMs: this.timestampMs,
80
+ schemaVersion: this.schemaVersion
81
+ }).length;
82
+ }
83
+ /** Compression ratio vs JSON encoding. */
84
+ get compressionVsJson() {
85
+ return this.jsonSize / MESSAGE_SIZE_BYTES;
86
+ }
87
+ /** Human-readable string for debugging. */
88
+ toString() {
89
+ return `BitmaskMessage(agent=${this.agentId}, v=${this.schemaVersion}, bits=${this.mask.toString(2).padStart(64, "0")}, t=${this.timestampMs})`;
90
+ }
91
+ _assertValid(data) {
92
+ if (typeof data.mask !== "bigint") {
93
+ throw new TypeError(`mask must be bigint, got ${typeof data.mask}`);
94
+ }
95
+ if (data.mask < 0n || data.mask > UINT64_MAX) {
96
+ throw new RangeError(`mask out of uint64 range: ${data.mask.toString()}`);
97
+ }
98
+ this._assertUint32("agentId", data.agentId);
99
+ this._assertUint32("schemaVersion", data.schemaVersion);
100
+ if (!Number.isSafeInteger(data.timestampMs)) {
101
+ throw new RangeError(
102
+ `timestampMs must be a safe integer, got ${data.timestampMs}`
103
+ );
104
+ }
105
+ }
106
+ _assertUint32(field, value) {
107
+ if (!Number.isInteger(value) || value < 0 || value > UINT32_MAX) {
108
+ throw new RangeError(
109
+ `${field} must be an integer in [0, ${UINT32_MAX}], got ${value}`
110
+ );
111
+ }
112
+ }
113
+ };
114
+
115
+ // src/errors.ts
116
+ var AdaptiveBitmaskError = class _AdaptiveBitmaskError extends Error {
117
+ code;
118
+ category;
119
+ context;
120
+ timestamp;
121
+ constructor(message, code, category, context) {
122
+ super(message);
123
+ this.name = "AdaptiveBitmaskError";
124
+ this.code = code;
125
+ this.category = category;
126
+ this.context = context;
127
+ this.timestamp = Date.now();
128
+ if (typeof Error.captureStackTrace === "function") {
129
+ Error.captureStackTrace(this, _AdaptiveBitmaskError);
130
+ }
131
+ }
132
+ toJSON() {
133
+ return {
134
+ name: this.name,
135
+ message: this.message,
136
+ code: this.code,
137
+ category: this.category,
138
+ context: this.context,
139
+ timestamp: this.timestamp,
140
+ stack: this.stack
141
+ };
142
+ }
143
+ };
144
+ var ValidationError = class extends AdaptiveBitmaskError {
145
+ constructor(message, context) {
146
+ super(message, "VALIDATION_ERROR", "VALIDATION", context);
147
+ this.name = "ValidationError";
148
+ }
149
+ };
150
+ var SchemaValidationError = class extends ValidationError {
151
+ constructor(message, schemaVersion, feature) {
152
+ super(message, { schemaVersion, feature });
153
+ this.name = "SchemaValidationError";
154
+ }
155
+ };
156
+ var MessageValidationError = class extends ValidationError {
157
+ constructor(message, agentId, messageSize) {
158
+ super(message, { agentId, messageSize });
159
+ this.name = "MessageValidationError";
160
+ }
161
+ };
162
+ var RuntimeError = class extends AdaptiveBitmaskError {
163
+ constructor(message, context) {
164
+ super(message, "RUNTIME_ERROR", "RUNTIME", context);
165
+ this.name = "RuntimeError";
166
+ }
167
+ };
168
+ var CoordinatorError = class extends RuntimeError {
169
+ constructor(message, agentCount, deadlineMs) {
170
+ super(message, { agentCount, deadlineMs });
171
+ this.name = "CoordinatorError";
172
+ }
173
+ };
174
+ var ArbiterError = class extends RuntimeError {
175
+ constructor(message, score, decision) {
176
+ super(message, { score, decision });
177
+ this.name = "ArbiterError";
178
+ }
179
+ };
180
+ var NetworkError = class extends AdaptiveBitmaskError {
181
+ constructor(message, context) {
182
+ super(message, "NETWORK_ERROR", "NETWORK", context);
183
+ this.name = "NetworkError";
184
+ }
185
+ };
186
+ var TimeoutError = class extends NetworkError {
187
+ constructor(message, timeoutMs, operation) {
188
+ super(message, { timeoutMs, operation });
189
+ this.name = "TimeoutError";
190
+ }
191
+ };
192
+ var SystemError = class extends AdaptiveBitmaskError {
193
+ constructor(message, context) {
194
+ super(message, "SYSTEM_ERROR", "SYSTEM", context);
195
+ this.name = "SystemError";
196
+ }
197
+ };
198
+ var MemoryError = class extends SystemError {
199
+ constructor(message, memoryUsage, limit) {
200
+ super(message, { memoryUsage, limit });
201
+ this.name = "MemoryError";
202
+ }
203
+ };
204
+ var Validator = class {
205
+ static validateAgentId(agentId) {
206
+ if (!Number.isInteger(agentId) || agentId < 0 || agentId > 4294967295) {
207
+ throw new MessageValidationError(
208
+ `Invalid agent ID: ${agentId}. Must be a non-negative integer <= 4294967295`,
209
+ agentId
210
+ );
211
+ }
212
+ }
213
+ static validateFeatureName(feature) {
214
+ if (typeof feature !== "string" || feature.length === 0) {
215
+ throw new ValidationError(
216
+ `Invalid feature name: must be a non-empty string`,
217
+ { feature }
218
+ );
219
+ }
220
+ if (feature.length > 100) {
221
+ throw new ValidationError(
222
+ `Feature name too long: ${feature.length} chars. Max: 100`,
223
+ { feature, length: feature.length }
224
+ );
225
+ }
226
+ if (!/^[a-zA-Z0-9_-]+$/.test(feature)) {
227
+ throw new ValidationError(
228
+ `Invalid feature name format: ${feature}. Only alphanumeric, underscore, and hyphen allowed`,
229
+ { feature }
230
+ );
231
+ }
232
+ }
233
+ static validateFeatureArray(features) {
234
+ if (!Array.isArray(features)) {
235
+ throw new ValidationError(
236
+ `Features must be an array`,
237
+ { type: typeof features }
238
+ );
239
+ }
240
+ if (features.length > 64) {
241
+ throw new ValidationError(
242
+ `Too many features: ${features.length}. Max: 64`,
243
+ { count: features.length }
244
+ );
245
+ }
246
+ const seen = /* @__PURE__ */ new Set();
247
+ for (const feature of features) {
248
+ this.validateFeatureName(feature);
249
+ if (seen.has(feature)) {
250
+ throw new ValidationError(
251
+ `Duplicate feature: ${feature}`,
252
+ { feature }
253
+ );
254
+ }
255
+ seen.add(feature);
256
+ }
257
+ }
258
+ static validateSchemaVersion(version) {
259
+ if (!Number.isInteger(version) || version < 0 || version > 4294967295) {
260
+ throw new SchemaValidationError(
261
+ `Invalid schema version: ${version}. Must be a non-negative integer <= 4294967295`,
262
+ version
263
+ );
264
+ }
265
+ }
266
+ static validateDeadlineMs(deadlineMs) {
267
+ if (!Number.isInteger(deadlineMs) || deadlineMs < 0 || deadlineMs > 6e4) {
268
+ throw new ValidationError(
269
+ `Invalid deadline: ${deadlineMs}ms. Must be 0-60000ms`,
270
+ { deadlineMs }
271
+ );
272
+ }
273
+ }
274
+ static validateTimeout(timeoutMs, operation) {
275
+ if (!Number.isInteger(timeoutMs) || timeoutMs < 0 || timeoutMs > 3e5) {
276
+ throw new ValidationError(
277
+ `Invalid timeout for ${operation}: ${timeoutMs}ms. Must be 0-300000ms`,
278
+ { operation, timeoutMs }
279
+ );
280
+ }
281
+ }
282
+ };
283
+ var CircuitBreaker = class {
284
+ constructor(failureThreshold = 5, recoveryTimeoutMs = 6e4, monitoringPeriodMs = 1e4) {
285
+ this.failureThreshold = failureThreshold;
286
+ this.recoveryTimeoutMs = recoveryTimeoutMs;
287
+ this.monitoringPeriodMs = monitoringPeriodMs;
288
+ }
289
+ failures = 0;
290
+ lastFailureTime = 0;
291
+ state = "CLOSED";
292
+ async execute(operation, operationName) {
293
+ if (this.state === "OPEN") {
294
+ if (Date.now() - this.lastFailureTime > this.recoveryTimeoutMs) {
295
+ this.state = "HALF_OPEN";
296
+ } else {
297
+ throw new RuntimeError(
298
+ `Circuit breaker OPEN for ${operationName}. Too many failures.`,
299
+ {
300
+ failures: this.failures,
301
+ state: this.state,
302
+ nextRetry: this.lastFailureTime + this.recoveryTimeoutMs
303
+ }
304
+ );
305
+ }
306
+ }
307
+ try {
308
+ const result = await operation();
309
+ if (this.state === "HALF_OPEN") {
310
+ this.reset();
311
+ }
312
+ return result;
313
+ } catch (error) {
314
+ this.recordFailure();
315
+ if (this.failures >= this.failureThreshold) {
316
+ this.state = "OPEN";
317
+ this.lastFailureTime = Date.now();
318
+ }
319
+ throw error;
320
+ }
321
+ }
322
+ recordFailure() {
323
+ this.failures++;
324
+ this.lastFailureTime = Date.now();
325
+ }
326
+ reset() {
327
+ this.failures = 0;
328
+ this.state = "CLOSED";
329
+ }
330
+ getStatus() {
331
+ return {
332
+ state: this.state,
333
+ failures: this.failures,
334
+ lastFailureTime: this.lastFailureTime,
335
+ isHealthy: this.state !== "OPEN"
336
+ };
337
+ }
338
+ };
339
+ var TimeoutManager = class {
340
+ static async withTimeout(promise, timeoutMs, operation) {
341
+ Validator.validateTimeout(timeoutMs, operation);
342
+ const timeoutPromise = new Promise((_, reject) => {
343
+ const timeoutId = setTimeout(() => {
344
+ reject(new TimeoutError(
345
+ `Operation timed out: ${operation}`,
346
+ timeoutMs,
347
+ operation
348
+ ));
349
+ }, timeoutMs);
350
+ promise.finally(() => clearTimeout(timeoutId));
351
+ });
352
+ return Promise.race([promise, timeoutPromise]);
353
+ }
354
+ };
355
+ var RecoveryManager = class {
356
+ static async withRetry(operation, maxRetries = 3, baseDelayMs = 1e3, operationName = "operation") {
357
+ let lastError;
358
+ for (let attempt = 0; attempt <= maxRetries; attempt++) {
359
+ try {
360
+ return await operation();
361
+ } catch (error) {
362
+ lastError = error;
363
+ if (attempt === maxRetries) {
364
+ throw new RuntimeError(
365
+ `Operation failed after ${maxRetries + 1} attempts: ${operationName}`,
366
+ {
367
+ attempts: attempt + 1,
368
+ maxRetries: maxRetries + 1,
369
+ lastError: lastError.message
370
+ }
371
+ );
372
+ }
373
+ const delay = baseDelayMs * Math.pow(2, attempt) + Math.random() * 1e3;
374
+ await new Promise((resolve) => setTimeout(resolve, delay));
375
+ }
376
+ }
377
+ throw lastError;
378
+ }
379
+ };
380
+
381
+ // src/transports/websocket.ts
382
+ var WebSocketTransport = class {
383
+ server;
384
+ agents = /* @__PURE__ */ new Map();
385
+ nextAgentId = 1;
386
+ logger = Logger.getInstance();
387
+ metrics;
388
+ circuitBreaker;
389
+ config;
390
+ isShuttingDown = false;
391
+ constructor(config) {
392
+ this.config = {
393
+ maxConnections: 1e3,
394
+ connectionTimeoutMs: 3e4,
395
+ messageTimeoutMs: 5e3,
396
+ enableCompression: true,
397
+ circuitBreakerThreshold: 10,
398
+ healthCheckIntervalMs: 3e4,
399
+ ...config
400
+ };
401
+ this.metrics = new MetricsCollector();
402
+ this.circuitBreaker = new CircuitBreaker(this.config.circuitBreakerThreshold);
403
+ this.server = new WebSocketServer({
404
+ port: this.config.port,
405
+ perMessageDeflate: this.config.enableCompression
406
+ });
407
+ this.setupEventHandlers();
408
+ this.startHealthChecks();
409
+ this.logger.info("WebSocketTransport", `Server started on port ${this.config.port}`);
410
+ }
411
+ setupEventHandlers() {
412
+ this.server.on("connection", (socket, req) => {
413
+ this.handleConnection(socket, req);
414
+ });
415
+ this.server.on("error", (error) => {
416
+ this.logger.error("WebSocketTransport", "Server error", error);
417
+ this.metrics.recordError("WebSocketServerError");
418
+ });
419
+ this.server.on("listening", () => {
420
+ this.logger.info("WebSocketTransport", `Server listening on port ${this.config.port}`);
421
+ });
422
+ }
423
+ handleConnection(socket, req) {
424
+ if (this.isShuttingDown) {
425
+ socket.close(1013, "Server shutting down");
426
+ return;
427
+ }
428
+ if (this.agents.size >= this.config.maxConnections) {
429
+ socket.close(1013, "Server at capacity");
430
+ this.logger.warn("WebSocketTransport", "Connection rejected: server at capacity");
431
+ return;
432
+ }
433
+ const agentId = this.nextAgentId++;
434
+ const agent = {
435
+ id: agentId,
436
+ socket,
437
+ lastSeen: Date.now(),
438
+ messagesReceived: 0,
439
+ messagesSent: 0,
440
+ schemaVersion: 0
441
+ };
442
+ this.agents.set(agentId, agent);
443
+ this.metrics.setAgentsConnected(this.agents.size);
444
+ this.logger.info("WebSocketTransport", `Agent connected: ${agentId}`, {
445
+ remoteAddress: req.socket?.remoteAddress,
446
+ totalAgents: this.agents.size
447
+ });
448
+ socket.on("message", (data) => {
449
+ this.handleMessage(agentId, data);
450
+ });
451
+ socket.on("close", (code, reason) => {
452
+ this.handleDisconnection(agentId, code, reason.toString());
453
+ });
454
+ socket.on("error", (error) => {
455
+ this.logger.error("WebSocketTransport", `Socket error for agent ${agentId}`, error);
456
+ this.metrics.recordError("SocketError");
457
+ });
458
+ socket.on("pong", () => {
459
+ agent.lastSeen = Date.now();
460
+ });
461
+ this.sendMessage(agentId, {
462
+ type: "HEARTBEAT",
463
+ payload: { agentId, timestamp: Date.now() },
464
+ timestamp: Date.now()
465
+ });
466
+ this.startHeartbeat(agentId);
467
+ }
468
+ async handleMessage(agentId, data) {
469
+ const agent = this.agents.get(agentId);
470
+ if (!agent) return;
471
+ try {
472
+ await TimeoutManager.withTimeout(
473
+ this.processMessage(agentId, data),
474
+ this.config.messageTimeoutMs,
475
+ `process message from agent ${agentId}`
476
+ );
477
+ } catch (error) {
478
+ if (error instanceof TimeoutError) {
479
+ this.logger.warn("WebSocketTransport", `Message timeout for agent ${agentId}`);
480
+ this.metrics.recordTimeout();
481
+ } else {
482
+ this.logger.error("WebSocketTransport", `Message processing error for agent ${agentId}`, error);
483
+ this.metrics.recordError("MessageProcessingError");
484
+ }
485
+ }
486
+ }
487
+ async processMessage(agentId, data) {
488
+ const startTime = performance.now();
489
+ const agent = this.agents.get(agentId);
490
+ if (!agent) return;
491
+ try {
492
+ const message = this.parseMessage(data);
493
+ if (message.type === "BITMASK_MESSAGE") {
494
+ const bitmaskMsg = BitmaskMessage.deserialize(message.payload);
495
+ agent.schemaVersion = bitmaskMsg.schemaVersion;
496
+ agent.messagesReceived++;
497
+ agent.lastSeen = Date.now();
498
+ this.metrics.incrementMessagesProcessed();
499
+ const processingTime = (performance.now() - startTime) * 1e3;
500
+ this.metrics.recordMessageProcessingTime(processingTime);
501
+ this.logger.debug("WebSocketTransport", `Received bitmask from agent ${agentId}`, {
502
+ messageSize: data.length,
503
+ processingTimeUs: processingTime,
504
+ schemaVersion: bitmaskMsg.schemaVersion
505
+ });
506
+ this.emit("message", { agentId, message: bitmaskMsg });
507
+ } else if (message.type === "HEARTBEAT") {
508
+ agent.lastSeen = Date.now();
509
+ this.logger.debug("WebSocketTransport", `Heartbeat from agent ${agentId}`);
510
+ }
511
+ } catch (error) {
512
+ this.logger.error("WebSocketTransport", `Invalid message from agent ${agentId}`, error);
513
+ this.sendMessage(agentId, {
514
+ type: "ERROR",
515
+ payload: { error: "Invalid message format" },
516
+ timestamp: Date.now()
517
+ });
518
+ throw error;
519
+ }
520
+ }
521
+ parseMessage(data) {
522
+ try {
523
+ const typeByte = data.readUInt8(0);
524
+ const type = ["BITMASK_MESSAGE", "HEARTBEAT", "ERROR", "SCHEMA_UPDATE"][typeByte];
525
+ if (!type) {
526
+ throw new Error(`Unknown message type: ${typeByte}`);
527
+ }
528
+ const payload = data.subarray(1);
529
+ return {
530
+ type,
531
+ payload: type === "BITMASK_MESSAGE" ? payload : JSON.parse(payload.toString()),
532
+ timestamp: Date.now()
533
+ };
534
+ } catch (error) {
535
+ throw new AdaptiveBitmaskError("Failed to parse message", "PARSE_ERROR", "RUNTIME");
536
+ }
537
+ }
538
+ sendMessage(agentId, message) {
539
+ const agent = this.agents.get(agentId);
540
+ if (!agent || agent.socket.readyState !== WebSocket.OPEN) {
541
+ return;
542
+ }
543
+ try {
544
+ let payload;
545
+ if (message.type === "BITMASK_MESSAGE") {
546
+ const typeByte = Buffer.from([0]);
547
+ payload = Buffer.concat([typeByte, Buffer.from(message.payload)]);
548
+ } else {
549
+ const typeByte = Buffer.from([["BITMASK_MESSAGE", "HEARTBEAT", "ERROR", "SCHEMA_UPDATE"].indexOf(message.type)]);
550
+ const jsonPayload = Buffer.from(JSON.stringify(message.payload));
551
+ payload = Buffer.concat([typeByte, jsonPayload]);
552
+ }
553
+ agent.socket.send(payload);
554
+ agent.messagesSent++;
555
+ agent.lastSeen = Date.now();
556
+ this.logger.debug("WebSocketTransport", `Sent message to agent ${agentId}`, {
557
+ type: message.type,
558
+ size: payload.length
559
+ });
560
+ } catch (error) {
561
+ this.logger.error("WebSocketTransport", `Failed to send message to agent ${agentId}`, error);
562
+ this.metrics.recordError("SendMessageError");
563
+ }
564
+ }
565
+ handleDisconnection(agentId, code, reason) {
566
+ const agent = this.agents.get(agentId);
567
+ if (!agent) return;
568
+ this.agents.delete(agentId);
569
+ this.metrics.setAgentsConnected(this.agents.size);
570
+ this.logger.info("WebSocketTransport", `Agent disconnected: ${agentId}`, {
571
+ code,
572
+ reason,
573
+ duration: Date.now() - agent.lastSeen,
574
+ messagesExchanged: agent.messagesReceived + agent.messagesSent
575
+ });
576
+ this.emit("disconnection", { agentId, code, reason });
577
+ }
578
+ startHeartbeat(agentId) {
579
+ const interval = setInterval(() => {
580
+ const agent = this.agents.get(agentId);
581
+ if (!agent) {
582
+ clearInterval(interval);
583
+ return;
584
+ }
585
+ if (agent.socket.readyState !== WebSocket.OPEN) {
586
+ clearInterval(interval);
587
+ return;
588
+ }
589
+ if (Date.now() - agent.lastSeen > this.config.connectionTimeoutMs) {
590
+ this.logger.warn("WebSocketTransport", `Agent ${agentId} connection stale, closing`);
591
+ agent.socket.terminate();
592
+ clearInterval(interval);
593
+ return;
594
+ }
595
+ try {
596
+ agent.socket.ping();
597
+ } catch (error) {
598
+ this.logger.error("WebSocketTransport", `Failed to ping agent ${agentId}`, error);
599
+ clearInterval(interval);
600
+ }
601
+ }, this.config.healthCheckIntervalMs);
602
+ }
603
+ startHealthChecks() {
604
+ setInterval(() => {
605
+ const now = Date.now();
606
+ const staleAgents = [];
607
+ for (const [agentId, agent] of this.agents) {
608
+ if (now - agent.lastSeen > this.config.connectionTimeoutMs) {
609
+ staleAgents.push(agentId);
610
+ }
611
+ }
612
+ for (const agentId of staleAgents) {
613
+ const agent = this.agents.get(agentId);
614
+ if (agent) {
615
+ this.logger.warn("WebSocketTransport", `Cleaning up stale agent ${agentId}`);
616
+ agent.socket.terminate();
617
+ }
618
+ }
619
+ this.metrics.updateMemoryUsage();
620
+ }, this.config.healthCheckIntervalMs);
621
+ }
622
+ // Public API methods
623
+ broadcast(message) {
624
+ for (const agentId of this.agents.keys()) {
625
+ this.sendMessage(agentId, message);
626
+ }
627
+ }
628
+ sendToAgent(agentId, message) {
629
+ this.sendMessage(agentId, message);
630
+ }
631
+ getConnectedAgents() {
632
+ return Array.from(this.agents.values());
633
+ }
634
+ getAgentCount() {
635
+ return this.agents.size;
636
+ }
637
+ getMetrics() {
638
+ return this.metrics;
639
+ }
640
+ async shutdown() {
641
+ this.isShuttingDown = true;
642
+ this.logger.info("WebSocketTransport", "Shutting down server...");
643
+ const closePromises = Array.from(this.agents.values()).map((agent) => {
644
+ return new Promise((resolve) => {
645
+ if (agent.socket.readyState === WebSocket.OPEN) {
646
+ agent.socket.close(1013, "Server shutting down");
647
+ agent.socket.on("close", () => resolve());
648
+ } else {
649
+ resolve();
650
+ }
651
+ });
652
+ });
653
+ await Promise.all(closePromises);
654
+ return new Promise((resolve) => {
655
+ this.server.close(() => {
656
+ this.logger.info("WebSocketTransport", "Server shutdown complete");
657
+ resolve();
658
+ });
659
+ });
660
+ }
661
+ // Event emitter functionality (simple implementation)
662
+ listeners = /* @__PURE__ */ new Map();
663
+ emit(event, data) {
664
+ const eventListeners = this.listeners.get(event) || [];
665
+ for (const listener of eventListeners) {
666
+ try {
667
+ listener(data);
668
+ } catch (error) {
669
+ this.logger.error("WebSocketTransport", `Event listener error for ${event}`, error);
670
+ }
671
+ }
672
+ }
673
+ on(event, listener) {
674
+ const eventListeners = this.listeners.get(event) || [];
675
+ eventListeners.push(listener);
676
+ this.listeners.set(event, eventListeners);
677
+ }
678
+ off(event, listener) {
679
+ const eventListeners = this.listeners.get(event) || [];
680
+ const index = eventListeners.indexOf(listener);
681
+ if (index > -1) {
682
+ eventListeners.splice(index, 1);
683
+ }
684
+ }
685
+ };
686
+ function createWebSocketTransport(config) {
687
+ return new WebSocketTransport(config);
688
+ }
689
+
690
+ // src/transports/http.ts
691
+ import { createServer } from "http";
692
+ var HttpTransport = class {
693
+ server;
694
+ logger = Logger.getInstance();
695
+ metrics;
696
+ circuitBreaker;
697
+ config;
698
+ requestCounts = /* @__PURE__ */ new Map();
699
+ isShuttingDown = false;
700
+ constructor(config) {
701
+ this.config = {
702
+ requestTimeoutMs: 1e4,
703
+ maxBodySize: 1024 * 1024,
704
+ // 1MB
705
+ enableCors: true,
706
+ circuitBreakerThreshold: 20,
707
+ rateLimitPerMinute: 1e3,
708
+ ...config
709
+ };
710
+ this.metrics = new MetricsCollector();
711
+ this.circuitBreaker = new CircuitBreaker(this.config.circuitBreakerThreshold);
712
+ this.server = createServer((req, res) => {
713
+ this.handleRequest(req, res);
714
+ });
715
+ this.startRateLimitCleanup();
716
+ this.logger.info("HttpTransport", `Server started on port ${this.config.port}`);
717
+ }
718
+ async handleRequest(req, res) {
719
+ if (this.isShuttingDown) {
720
+ this.sendResponse(res, 503, JSON.stringify({ error: "Server shutting down" }));
721
+ return;
722
+ }
723
+ const clientIP = req.socket.remoteAddress || "unknown";
724
+ const url = req.url || "/";
725
+ if (this.isRateLimited(clientIP)) {
726
+ this.sendResponse(res, 429, JSON.stringify({ error: "Rate limit exceeded" }));
727
+ this.logger.warn("HttpTransport", `Rate limit exceeded for ${clientIP}`);
728
+ return;
729
+ }
730
+ if (this.config.enableCors) {
731
+ res.setHeader("Access-Control-Allow-Origin", "*");
732
+ res.setHeader("Access-Control-Allow-Methods", "GET, POST, PUT, DELETE, OPTIONS");
733
+ res.setHeader("Access-Control-Allow-Headers", "Content-Type, Authorization");
734
+ }
735
+ if (req.method === "OPTIONS") {
736
+ res.writeHead(200);
737
+ res.end();
738
+ return;
739
+ }
740
+ try {
741
+ await TimeoutManager.withTimeout(
742
+ this.processRequest(req, res),
743
+ this.config.requestTimeoutMs,
744
+ `HTTP request from ${clientIP}`
745
+ );
746
+ } catch (error) {
747
+ if (error instanceof TimeoutError) {
748
+ this.sendResponse(res, 408, JSON.stringify({ error: "Request timeout" }));
749
+ this.metrics.recordTimeout();
750
+ } else {
751
+ this.logger.error("HttpTransport", "Request processing error", error);
752
+ this.sendResponse(res, 500, JSON.stringify({ error: "Internal server error" }));
753
+ this.metrics.recordError("HttpRequestError");
754
+ }
755
+ }
756
+ }
757
+ async processRequest(req, res) {
758
+ const startTime = performance.now();
759
+ const method = req.method || "GET";
760
+ const url = req.url || "/";
761
+ let body;
762
+ if (method === "POST" || method === "PUT") {
763
+ body = await this.parseRequestBody(req);
764
+ }
765
+ const request = {
766
+ method,
767
+ url,
768
+ headers: req.headers,
769
+ body,
770
+ timestamp: Date.now()
771
+ };
772
+ this.logger.debug("HttpTransport", `Incoming ${method} ${url}`, {
773
+ contentLength: body?.length,
774
+ userAgent: request.headers["user-agent"]
775
+ });
776
+ let response;
777
+ try {
778
+ response = await this.routeRequest(request);
779
+ } catch (error) {
780
+ if (error instanceof ValidationError) {
781
+ response = {
782
+ statusCode: 400,
783
+ headers: { "Content-Type": "application/json" },
784
+ body: JSON.stringify({ error: error.message, code: error.code }),
785
+ timestamp: Date.now()
786
+ };
787
+ } else if (error instanceof AdaptiveBitmaskError) {
788
+ response = {
789
+ statusCode: 500,
790
+ headers: { "Content-Type": "application/json" },
791
+ body: JSON.stringify({ error: error.message, code: error.code }),
792
+ timestamp: Date.now()
793
+ };
794
+ } else {
795
+ throw error;
796
+ }
797
+ }
798
+ this.sendResponse(res, response.statusCode, response.body, response.headers);
799
+ const processingTime = (performance.now() - startTime) * 1e3;
800
+ this.metrics.recordMessageProcessingTime(processingTime);
801
+ this.metrics.incrementMessagesProcessed();
802
+ this.logger.debug("HttpTransport", `Response sent`, {
803
+ statusCode: response.statusCode,
804
+ processingTimeUs: processingTime
805
+ });
806
+ }
807
+ async parseRequestBody(req) {
808
+ return new Promise((resolve, reject) => {
809
+ const chunks = [];
810
+ let totalSize = 0;
811
+ req.on("data", (chunk) => {
812
+ totalSize += chunk.length;
813
+ if (totalSize > this.config.maxBodySize) {
814
+ reject(new ValidationError(`Request body too large: ${totalSize} bytes`));
815
+ return;
816
+ }
817
+ chunks.push(chunk);
818
+ });
819
+ req.on("end", () => {
820
+ resolve(Buffer.concat(chunks));
821
+ });
822
+ req.on("error", reject);
823
+ });
824
+ }
825
+ async routeRequest(request) {
826
+ const { method, url, body } = request;
827
+ if (url === "/api/coordinate" && method === "POST") {
828
+ return await this.handleCoordinate(body);
829
+ }
830
+ if (url === "/api/health" && method === "GET") {
831
+ return await this.handleHealth();
832
+ }
833
+ if (url === "/api/metrics" && method === "GET") {
834
+ return await this.handleMetrics();
835
+ }
836
+ if (url === "/api/schema" && method === "GET") {
837
+ return await this.handleGetSchema();
838
+ }
839
+ if (url === "/api/schema" && method === "POST") {
840
+ return await this.handleUpdateSchema(body);
841
+ }
842
+ return {
843
+ statusCode: 404,
844
+ headers: { "Content-Type": "application/json" },
845
+ body: JSON.stringify({ error: "Not found" }),
846
+ timestamp: Date.now()
847
+ };
848
+ }
849
+ async handleCoordinate(body) {
850
+ if (!body) {
851
+ throw new ValidationError("Missing request body");
852
+ }
853
+ try {
854
+ const bitmaskMsg = BitmaskMessage.deserialize(body);
855
+ this.logger.info("HttpTransport", "Received bitmask message", {
856
+ agentId: bitmaskMsg.agentId,
857
+ schemaVersion: bitmaskMsg.schemaVersion,
858
+ messageSize: body.length
859
+ });
860
+ this.emit("message", { message: bitmaskMsg });
861
+ return {
862
+ statusCode: 200,
863
+ headers: { "Content-Type": "application/json" },
864
+ body: JSON.stringify({
865
+ success: true,
866
+ received: true,
867
+ timestamp: Date.now()
868
+ }),
869
+ timestamp: Date.now()
870
+ };
871
+ } catch (error) {
872
+ throw new ValidationError("Invalid bitmask message format");
873
+ }
874
+ }
875
+ async handleHealth() {
876
+ const metrics = this.metrics.getMetrics();
877
+ const latencyStats = this.metrics.getLatencyStats();
878
+ const health = {
879
+ status: "HEALTHY",
880
+ uptime: metrics.uptimeMs,
881
+ timestamp: Date.now(),
882
+ version: "0.2.0-rc.0",
883
+ metrics: {
884
+ messagesProcessed: metrics.messagesProcessed,
885
+ memoryUsageMB: metrics.memoryUsageMB,
886
+ avgLatencyUs: latencyStats.mean
887
+ }
888
+ };
889
+ return {
890
+ statusCode: 200,
891
+ headers: { "Content-Type": "application/json" },
892
+ body: JSON.stringify(health),
893
+ timestamp: Date.now()
894
+ };
895
+ }
896
+ async handleMetrics() {
897
+ const metrics = this.metrics.getMetrics();
898
+ const latencyStats = this.metrics.getLatencyStats();
899
+ return {
900
+ statusCode: 200,
901
+ headers: { "Content-Type": "application/json" },
902
+ body: JSON.stringify({
903
+ ...metrics,
904
+ latencyStats
905
+ }),
906
+ timestamp: Date.now()
907
+ };
908
+ }
909
+ async handleGetSchema() {
910
+ return {
911
+ statusCode: 200,
912
+ headers: { "Content-Type": "application/json" },
913
+ body: JSON.stringify({
914
+ version: 1,
915
+ features: [],
916
+ timestamp: Date.now()
917
+ }),
918
+ timestamp: Date.now()
919
+ };
920
+ }
921
+ async handleUpdateSchema(body) {
922
+ return {
923
+ statusCode: 501,
924
+ headers: { "Content-Type": "application/json" },
925
+ body: JSON.stringify({ error: "Schema updates not implemented yet" }),
926
+ timestamp: Date.now()
927
+ };
928
+ }
929
+ sendResponse(res, statusCode, body, headers) {
930
+ if (headers) {
931
+ Object.entries(headers).forEach(([key, value]) => {
932
+ res.setHeader(key, value);
933
+ });
934
+ }
935
+ if (body && !res.getHeader("Content-Type")) {
936
+ res.setHeader("Content-Type", "application/json");
937
+ }
938
+ res.writeHead(statusCode);
939
+ res.end(body);
940
+ }
941
+ isRateLimited(clientIP) {
942
+ const now = Date.now();
943
+ const oneMinuteAgo = now - 6e4;
944
+ let requests = this.requestCounts.get(clientIP) || [];
945
+ requests = requests.filter((timestamp) => timestamp > oneMinuteAgo);
946
+ requests.push(now);
947
+ this.requestCounts.set(clientIP, requests);
948
+ return requests.length > this.config.rateLimitPerMinute;
949
+ }
950
+ startRateLimitCleanup() {
951
+ setInterval(() => {
952
+ const now = Date.now();
953
+ const oneMinuteAgo = now - 6e4;
954
+ for (const [ip, requests] of this.requestCounts) {
955
+ const filtered = requests.filter((timestamp) => timestamp > oneMinuteAgo);
956
+ if (filtered.length === 0) {
957
+ this.requestCounts.delete(ip);
958
+ } else {
959
+ this.requestCounts.set(ip, filtered);
960
+ }
961
+ }
962
+ }, 6e4);
963
+ }
964
+ // Public API methods
965
+ start() {
966
+ this.server.listen(this.config.port);
967
+ }
968
+ async shutdown() {
969
+ this.isShuttingDown = true;
970
+ this.logger.info("HttpTransport", "Shutting down server...");
971
+ return new Promise((resolve) => {
972
+ this.server.close(() => {
973
+ this.logger.info("HttpTransport", "Server shutdown complete");
974
+ resolve();
975
+ });
976
+ });
977
+ }
978
+ getMetrics() {
979
+ return this.metrics;
980
+ }
981
+ // Event emitter functionality (simple implementation)
982
+ listeners = /* @__PURE__ */ new Map();
983
+ emit(event, data) {
984
+ const eventListeners = this.listeners.get(event) || [];
985
+ for (const listener of eventListeners) {
986
+ try {
987
+ listener(data);
988
+ } catch (error) {
989
+ this.logger.error("HttpTransport", `Event listener error for ${event}`, error);
990
+ }
991
+ }
992
+ }
993
+ on(event, listener) {
994
+ const eventListeners = this.listeners.get(event) || [];
995
+ eventListeners.push(listener);
996
+ this.listeners.set(event, eventListeners);
997
+ }
998
+ off(event, listener) {
999
+ const eventListeners = this.listeners.get(event) || [];
1000
+ const index = eventListeners.indexOf(listener);
1001
+ if (index > -1) {
1002
+ eventListeners.splice(index, 1);
1003
+ }
1004
+ }
1005
+ };
1006
+ function createHttpTransport(config) {
1007
+ const transport = new HttpTransport(config);
1008
+ transport.start();
1009
+ return transport;
1010
+ }
1011
+
1012
+ // src/telemetry.ts
1013
+ var LogLevel = /* @__PURE__ */ ((LogLevel2) => {
1014
+ LogLevel2[LogLevel2["DEBUG"] = 0] = "DEBUG";
1015
+ LogLevel2[LogLevel2["INFO"] = 1] = "INFO";
1016
+ LogLevel2[LogLevel2["WARN"] = 2] = "WARN";
1017
+ LogLevel2[LogLevel2["ERROR"] = 3] = "ERROR";
1018
+ LogLevel2[LogLevel2["FATAL"] = 4] = "FATAL";
1019
+ return LogLevel2;
1020
+ })(LogLevel || {});
1021
+ var Logger = class _Logger {
1022
+ static instance;
1023
+ logLevel = 1 /* INFO */;
1024
+ logs = [];
1025
+ maxLogSize = 1e4;
1026
+ onLog;
1027
+ constructor() {
1028
+ }
1029
+ static getInstance() {
1030
+ if (!_Logger.instance) {
1031
+ _Logger.instance = new _Logger();
1032
+ }
1033
+ return _Logger.instance;
1034
+ }
1035
+ setLogLevel(level) {
1036
+ this.logLevel = level;
1037
+ }
1038
+ setLogCallback(callback) {
1039
+ this.onLog = callback;
1040
+ }
1041
+ shouldLog(level) {
1042
+ return level >= this.logLevel;
1043
+ }
1044
+ createLogEntry(level, component, message, metadata, error) {
1045
+ const entry = {
1046
+ timestamp: Date.now(),
1047
+ level,
1048
+ component,
1049
+ message,
1050
+ metadata
1051
+ };
1052
+ if (error) {
1053
+ entry.error = {
1054
+ name: error.name,
1055
+ message: error.message,
1056
+ stack: error.stack,
1057
+ code: error.code
1058
+ };
1059
+ }
1060
+ return entry;
1061
+ }
1062
+ addLog(entry) {
1063
+ this.logs.push(entry);
1064
+ if (this.logs.length > this.maxLogSize) {
1065
+ this.logs = this.logs.slice(-this.maxLogSize);
1066
+ }
1067
+ if (this.onLog) {
1068
+ this.onLog(entry);
1069
+ }
1070
+ if (this.shouldLog(entry.level)) {
1071
+ const levelName = LogLevel[entry.level];
1072
+ const message = `[${new Date(entry.timestamp).toISOString()}] ${levelName} [${entry.component}] ${entry.message}`;
1073
+ switch (entry.level) {
1074
+ case 0 /* DEBUG */:
1075
+ console.debug(message, entry.metadata || "");
1076
+ break;
1077
+ case 1 /* INFO */:
1078
+ console.info(message, entry.metadata || "");
1079
+ break;
1080
+ case 2 /* WARN */:
1081
+ console.warn(message, entry.metadata || "");
1082
+ break;
1083
+ case 3 /* ERROR */:
1084
+ case 4 /* FATAL */:
1085
+ console.error(message, entry.error || entry.metadata || "");
1086
+ break;
1087
+ }
1088
+ }
1089
+ }
1090
+ debug(component, message, metadata) {
1091
+ const entry = this.createLogEntry(0 /* DEBUG */, component, message, metadata);
1092
+ this.addLog(entry);
1093
+ }
1094
+ info(component, message, metadata) {
1095
+ const entry = this.createLogEntry(1 /* INFO */, component, message, metadata);
1096
+ this.addLog(entry);
1097
+ }
1098
+ warn(component, message, metadata) {
1099
+ const entry = this.createLogEntry(2 /* WARN */, component, message, metadata);
1100
+ this.addLog(entry);
1101
+ }
1102
+ error(component, message, error, metadata) {
1103
+ const entry = this.createLogEntry(3 /* ERROR */, component, message, metadata, error);
1104
+ this.addLog(entry);
1105
+ }
1106
+ fatal(component, message, error, metadata) {
1107
+ const entry = this.createLogEntry(4 /* FATAL */, component, message, metadata, error);
1108
+ this.addLog(entry);
1109
+ }
1110
+ getRecentLogs(count = 100) {
1111
+ return this.logs.slice(-count);
1112
+ }
1113
+ getLogsByLevel(level) {
1114
+ return this.logs.filter((log) => log.level === level);
1115
+ }
1116
+ clearLogs() {
1117
+ this.logs = [];
1118
+ }
1119
+ };
1120
+ var MetricsCollector = class {
1121
+ metrics;
1122
+ startTime;
1123
+ logger = Logger.getInstance();
1124
+ constructor() {
1125
+ this.startTime = Date.now();
1126
+ this.metrics = this.initializeMetrics();
1127
+ }
1128
+ initializeMetrics() {
1129
+ return {
1130
+ coordinationLatencyUs: [],
1131
+ messageProcessingTimeUs: [],
1132
+ encodingTimeUs: [],
1133
+ decodingTimeUs: [],
1134
+ messagesProcessed: 0,
1135
+ agentsConnected: 0,
1136
+ featuresActive: 0,
1137
+ schemaVersion: 0,
1138
+ errorsByType: {},
1139
+ timeouts: 0,
1140
+ circuitBreakerTrips: 0,
1141
+ memoryUsageMB: 0,
1142
+ bufferUtilization: 0,
1143
+ lastReset: Date.now(),
1144
+ uptimeMs: 0
1145
+ };
1146
+ }
1147
+ recordCoordinationLatency(latencyUs) {
1148
+ this.metrics.coordinationLatencyUs.push(latencyUs);
1149
+ this.logger.debug("Metrics", "Coordination latency recorded", { latencyUs });
1150
+ }
1151
+ recordMessageProcessingTime(timeUs) {
1152
+ this.metrics.messageProcessingTimeUs.push(timeUs);
1153
+ }
1154
+ recordEncodingTime(timeUs) {
1155
+ this.metrics.encodingTimeUs.push(timeUs);
1156
+ }
1157
+ recordDecodingTime(timeUs) {
1158
+ this.metrics.decodingTimeUs.push(timeUs);
1159
+ }
1160
+ incrementMessagesProcessed() {
1161
+ this.metrics.messagesProcessed++;
1162
+ }
1163
+ setAgentsConnected(count) {
1164
+ this.metrics.agentsConnected = count;
1165
+ }
1166
+ setFeaturesActive(count) {
1167
+ this.metrics.featuresActive = count;
1168
+ }
1169
+ setSchemaVersion(version) {
1170
+ this.metrics.schemaVersion = version;
1171
+ }
1172
+ recordError(errorType) {
1173
+ this.metrics.errorsByType[errorType] = (this.metrics.errorsByType[errorType] || 0) + 1;
1174
+ }
1175
+ recordTimeout() {
1176
+ this.metrics.timeouts++;
1177
+ }
1178
+ recordCircuitBreakerTrip() {
1179
+ this.metrics.circuitBreakerTrips++;
1180
+ }
1181
+ updateMemoryUsage() {
1182
+ if (typeof process !== "undefined" && process.memoryUsage) {
1183
+ const usage = process.memoryUsage();
1184
+ this.metrics.memoryUsageMB = usage.heapUsed / 1024 / 1024;
1185
+ }
1186
+ }
1187
+ setBufferUtilization(utilization) {
1188
+ this.metrics.bufferUtilization = Math.min(1, Math.max(0, utilization));
1189
+ }
1190
+ getMetrics() {
1191
+ this.metrics.uptimeMs = Date.now() - this.startTime;
1192
+ return { ...this.metrics };
1193
+ }
1194
+ getLatencyStats() {
1195
+ const latencies = this.metrics.coordinationLatencyUs;
1196
+ if (latencies.length === 0) {
1197
+ return { mean: 0, p50: 0, p95: 0, p99: 0, max: 0, min: 0 };
1198
+ }
1199
+ const sorted = [...latencies].sort((a, b) => a - b);
1200
+ const sum = latencies.reduce((a, b) => a + b, 0);
1201
+ return {
1202
+ mean: sum / latencies.length,
1203
+ p50: sorted[Math.floor(sorted.length * 0.5)],
1204
+ p95: sorted[Math.floor(sorted.length * 0.95)],
1205
+ p99: sorted[Math.floor(sorted.length * 0.99)],
1206
+ max: sorted[sorted.length - 1],
1207
+ min: sorted[0]
1208
+ };
1209
+ }
1210
+ reset() {
1211
+ this.metrics = this.initializeMetrics();
1212
+ this.logger.info("Metrics", "Metrics reset");
1213
+ }
1214
+ };
1215
+ var HealthChecker = class {
1216
+ metrics;
1217
+ logger = Logger.getInstance();
1218
+ version = "0.2.0-rc.0";
1219
+ constructor(metrics) {
1220
+ this.metrics = metrics;
1221
+ }
1222
+ getHealthStatus() {
1223
+ const metrics = this.metrics.getMetrics();
1224
+ const latencyStats = this.metrics.getLatencyStats();
1225
+ const memoryHealthy = metrics.memoryUsageMB < 500;
1226
+ const latencyHealthy = latencyStats.p99 < 1e4;
1227
+ const errorRateHealthy = this.getErrorRate() < 0.01;
1228
+ const circuitBreakersHealthy = metrics.circuitBreakerTrips === 0;
1229
+ const allHealthy = memoryHealthy && latencyHealthy && errorRateHealthy && circuitBreakersHealthy;
1230
+ const someHealthy = memoryHealthy && latencyHealthy;
1231
+ let status;
1232
+ if (allHealthy) {
1233
+ status = "HEALTHY";
1234
+ } else if (someHealthy) {
1235
+ status = "DEGRADED";
1236
+ } else {
1237
+ status = "UNHEALTHY";
1238
+ }
1239
+ return {
1240
+ status,
1241
+ uptime: metrics.uptimeMs,
1242
+ version: this.version,
1243
+ timestamp: Date.now(),
1244
+ checks: {
1245
+ memory: memoryHealthy,
1246
+ latency: latencyHealthy,
1247
+ errorRate: errorRateHealthy,
1248
+ circuitBreakers: circuitBreakersHealthy
1249
+ },
1250
+ metrics: {
1251
+ messagesProcessed: metrics.messagesProcessed,
1252
+ agentsConnected: metrics.agentsConnected,
1253
+ coordinationLatencyUs: latencyStats,
1254
+ memoryUsageMB: metrics.memoryUsageMB,
1255
+ errorsByType: metrics.errorsByType
1256
+ }
1257
+ };
1258
+ }
1259
+ getErrorRate() {
1260
+ const metrics = this.metrics.getMetrics();
1261
+ const totalErrors = Object.values(metrics.errorsByType).reduce((sum, count) => sum + count, 0);
1262
+ const totalOperations = metrics.messagesProcessed + totalErrors;
1263
+ return totalOperations > 0 ? totalErrors / totalOperations : 0;
1264
+ }
1265
+ startHealthCheck(intervalMs = 3e4) {
1266
+ setInterval(() => {
1267
+ const status = this.getHealthStatus();
1268
+ this.logger.info("Health", `Health status: ${status.status}`, {
1269
+ uptime: status.uptime,
1270
+ checks: status.checks,
1271
+ messagesProcessed: status.metrics.messagesProcessed
1272
+ });
1273
+ if (status.status === "UNHEALTHY") {
1274
+ this.logger.error("Health", "System is unhealthy", void 0, {
1275
+ checks: status.checks,
1276
+ metrics: status.metrics
1277
+ });
1278
+ }
1279
+ }, intervalMs);
1280
+ }
1281
+ };
1282
+ var Profiler = class {
1283
+ static sessions = /* @__PURE__ */ new Map();
1284
+ logger = Logger.getInstance();
1285
+ static startSession(sessionId) {
1286
+ this.sessions.set(sessionId, {
1287
+ startTime: performance.now(),
1288
+ events: []
1289
+ });
1290
+ this.getLogger().debug("Profiler", `Started profiling session: ${sessionId}`);
1291
+ }
1292
+ static recordEvent(sessionId, event, data) {
1293
+ const session = this.sessions.get(sessionId);
1294
+ if (!session) {
1295
+ this.getLogger().warn("Profiler", `No active session: ${sessionId}`);
1296
+ return;
1297
+ }
1298
+ session.events.push({
1299
+ time: performance.now() - session.startTime,
1300
+ event,
1301
+ data
1302
+ });
1303
+ }
1304
+ static endSession(sessionId) {
1305
+ const session = this.sessions.get(sessionId);
1306
+ if (!session) {
1307
+ this.getLogger().warn("Profiler", `No active session: ${sessionId}`);
1308
+ return [];
1309
+ }
1310
+ this.sessions.delete(sessionId);
1311
+ this.getLogger().debug("Profiler", `Ended profiling session: ${sessionId}`, {
1312
+ duration: performance.now() - session.startTime,
1313
+ eventCount: session.events.length
1314
+ });
1315
+ return session.events;
1316
+ }
1317
+ static getLogger() {
1318
+ return Logger.getInstance();
1319
+ }
1320
+ };
1321
+ function createProductionLogger(config) {
1322
+ const logger = Logger.getInstance();
1323
+ if (config?.level !== void 0) {
1324
+ logger.setLogLevel(config.level);
1325
+ }
1326
+ if (config?.enableConsole === false) {
1327
+ logger.setLogCallback(() => {
1328
+ });
1329
+ }
1330
+ return logger;
1331
+ }
1332
+ function createMetricsCollector(autoUpdateMs) {
1333
+ const collector = new MetricsCollector();
1334
+ if (autoUpdateMs) {
1335
+ setInterval(() => {
1336
+ collector.updateMemoryUsage();
1337
+ }, autoUpdateMs);
1338
+ }
1339
+ return collector;
1340
+ }
1341
+
1342
+ // src/bitmask.ts
1343
+ var BITMASK_WIDTH = 64;
1344
+ var EMERGENCY_RANGE = [56, 63];
1345
+ var HIGH_FREQ_RANGE = [0, 47];
1346
+ var MED_FREQ_RANGE = [48, 55];
1347
+ var SINGLE_BIT_TO_POSITION = new Map(
1348
+ Array.from({ length: BITMASK_WIDTH }, (_, i) => [1n << BigInt(i), i])
1349
+ );
1350
+ function empty() {
1351
+ return 0n;
1352
+ }
1353
+ function setBit(mask, position) {
1354
+ if (position < 0 || position >= BITMASK_WIDTH) {
1355
+ throw new RangeError(`Bit position ${position} out of range [0, ${BITMASK_WIDTH - 1}]`);
1356
+ }
1357
+ return mask | 1n << BigInt(position);
1358
+ }
1359
+ function clearBit(mask, position) {
1360
+ if (position < 0 || position >= BITMASK_WIDTH) {
1361
+ throw new RangeError(`Bit position ${position} out of range [0, ${BITMASK_WIDTH - 1}]`);
1362
+ }
1363
+ return mask & ~(1n << BigInt(position));
1364
+ }
1365
+ function testBit(mask, position) {
1366
+ if (position < 0 || position >= BITMASK_WIDTH) {
1367
+ throw new RangeError(`Bit position ${position} out of range [0, ${BITMASK_WIDTH - 1}]`);
1368
+ }
1369
+ return (mask & 1n << BigInt(position)) !== 0n;
1370
+ }
1371
+ function popcount(mask) {
1372
+ let count = 0;
1373
+ let m = mask;
1374
+ while (m > 0n) {
1375
+ m &= m - 1n;
1376
+ count++;
1377
+ }
1378
+ return count;
1379
+ }
1380
+ function activeBits(mask) {
1381
+ const bits = [];
1382
+ forEachSetBit(mask, (bit) => bits.push(bit));
1383
+ return bits;
1384
+ }
1385
+ function forEachSetBit(mask, fn) {
1386
+ if (mask < 0n) {
1387
+ throw new RangeError("Bitmask must be non-negative");
1388
+ }
1389
+ let m = mask;
1390
+ while (m !== 0n) {
1391
+ const leastSignificantBit = m & -m;
1392
+ const position = SINGLE_BIT_TO_POSITION.get(leastSignificantBit);
1393
+ if (position === void 0) {
1394
+ throw new Error(`Invalid 64-bit mask: ${mask.toString()}`);
1395
+ }
1396
+ fn(position);
1397
+ m ^= leastSignificantBit;
1398
+ }
1399
+ }
1400
+ function merge(a, b) {
1401
+ return a | b;
1402
+ }
1403
+ function intersect(a, b) {
1404
+ return a & b;
1405
+ }
1406
+ function delta(prev, next) {
1407
+ return prev ^ next;
1408
+ }
1409
+ function hammingDistance(a, b) {
1410
+ return popcount(a ^ b);
1411
+ }
1412
+ function hasEmergency(mask) {
1413
+ const emergencyMask = 0xFFn << 56n;
1414
+ return (mask & emergencyMask) !== 0n;
1415
+ }
1416
+ function emergencyBits(mask) {
1417
+ return mask & 0xFFn << 56n;
1418
+ }
1419
+ function toBytes(mask) {
1420
+ const buf = new Uint8Array(8);
1421
+ let m = mask;
1422
+ for (let i = 0; i < 8; i++) {
1423
+ buf[i] = Number(m & 0xFFn);
1424
+ m >>= 8n;
1425
+ }
1426
+ return buf;
1427
+ }
1428
+ function fromBytes(buf) {
1429
+ if (buf.length < 8) {
1430
+ throw new Error(`Expected 8 bytes, got ${buf.length}`);
1431
+ }
1432
+ let mask = 0n;
1433
+ for (let i = 7; i >= 0; i--) {
1434
+ mask = mask << 8n | BigInt(buf[i]);
1435
+ }
1436
+ return mask;
1437
+ }
1438
+ function encode(features, schema, options = {}) {
1439
+ let mask = 0n;
1440
+ let mapped = 0;
1441
+ let unmapped = 0;
1442
+ const unknownFeatures = [];
1443
+ for (const feature of features) {
1444
+ const bit = schema.get(feature);
1445
+ if (bit !== void 0) {
1446
+ mask |= 1n << BigInt(bit);
1447
+ mapped++;
1448
+ } else {
1449
+ unmapped++;
1450
+ unknownFeatures.push(feature);
1451
+ }
1452
+ }
1453
+ if (options.throwOnUnknownFeatures && unknownFeatures.length > 0) {
1454
+ const uniqueUnknown = [...new Set(unknownFeatures)];
1455
+ throw new Error(
1456
+ `Unknown features (${uniqueUnknown.length}): ${uniqueUnknown.join(", ")}`
1457
+ );
1458
+ }
1459
+ return { mask, mapped, unmapped };
1460
+ }
1461
+ function decode(mask, reverseSchema) {
1462
+ const features = [];
1463
+ for (let i = 0; i < BITMASK_WIDTH; i++) {
1464
+ if (mask & 1n << BigInt(i)) {
1465
+ const feats = reverseSchema.get(i);
1466
+ if (feats) {
1467
+ features.push(...feats);
1468
+ }
1469
+ }
1470
+ }
1471
+ return features;
1472
+ }
1473
+
1474
+ // src/schema.ts
1475
+ var MAX_EMERGENCY_FEATURES = EMERGENCY_RANGE[1] - EMERGENCY_RANGE[0] + 1;
1476
+ var MAX_REGULAR_FEATURES = HIGH_FREQ_RANGE[1] - HIGH_FREQ_RANGE[0] + 1 + (MED_FREQ_RANGE[1] - MED_FREQ_RANGE[0] + 1);
1477
+ var FNV_OFFSET_64 = 0xcbf29ce484222325n;
1478
+ var FNV_PRIME_64 = 0x100000001b3n;
1479
+ var MASK_64 = (1n << 64n) - 1n;
1480
+ var SchemaManager = class {
1481
+ _version = 0;
1482
+ _featureToBit = /* @__PURE__ */ new Map();
1483
+ _bitToFeatures = /* @__PURE__ */ new Map();
1484
+ _activationCounts = /* @__PURE__ */ new Map();
1485
+ _totalActivations = 0;
1486
+ _emergencyFeatures;
1487
+ _emergencyPrefix;
1488
+ _maxFeatures;
1489
+ constructor(config = {}) {
1490
+ this._maxFeatures = config.maxFeatures ?? BITMASK_WIDTH;
1491
+ this._emergencyPrefix = config.emergencyPrefix ?? "EMERGENCY_";
1492
+ this._emergencyFeatures = new Set(config.emergencyFeatures ?? []);
1493
+ }
1494
+ /** Current schema version. */
1495
+ get version() {
1496
+ return this._version;
1497
+ }
1498
+ /** Read-only view of feature → bit mapping. */
1499
+ get featureToBit() {
1500
+ return this._featureToBit;
1501
+ }
1502
+ /** Read-only view of bit → features mapping. */
1503
+ get bitToFeatures() {
1504
+ return this._bitToFeatures;
1505
+ }
1506
+ /** Number of actively mapped features. */
1507
+ get activeFeatureCount() {
1508
+ return this._featureToBit.size;
1509
+ }
1510
+ /** Deterministic fingerprint of current schema mapping and version. */
1511
+ get fingerprint() {
1512
+ return this._computeFingerprint(
1513
+ this._version,
1514
+ this._featureToBit,
1515
+ this._emergencyPrefix,
1516
+ this._emergencyFeatures
1517
+ );
1518
+ }
1519
+ /** Check if a feature is an emergency feature. */
1520
+ isEmergency(feature) {
1521
+ return this._emergencyFeatures.has(feature) || feature.startsWith(this._emergencyPrefix);
1522
+ }
1523
+ /**
1524
+ * Register a feature in the schema.
1525
+ * Emergency features are pinned to bits 56-63.
1526
+ * Regular features fill bits 0-55 in order.
1527
+ *
1528
+ * Returns the assigned bit position, or -1 if schema is full.
1529
+ */
1530
+ register(feature) {
1531
+ const existing = this._featureToBit.get(feature);
1532
+ if (existing !== void 0) return existing;
1533
+ if (this._featureToBit.size >= this._maxFeatures) return -1;
1534
+ if (this.isEmergency(feature)) {
1535
+ return this._registerEmergency(feature);
1536
+ }
1537
+ return this._registerRegular(feature);
1538
+ }
1539
+ /**
1540
+ * Register multiple features at once.
1541
+ * Returns a map of feature → assigned bit position.
1542
+ */
1543
+ registerAll(features) {
1544
+ const result = /* @__PURE__ */ new Map();
1545
+ for (const feature of features) {
1546
+ result.set(feature, this.register(feature));
1547
+ }
1548
+ return result;
1549
+ }
1550
+ /**
1551
+ * Record feature activations for frequency tracking.
1552
+ * Call this every coordination round with the observed features.
1553
+ */
1554
+ recordActivations(features) {
1555
+ for (const feature of features) {
1556
+ const count = this._activationCounts.get(feature) ?? 0;
1557
+ this._activationCounts.set(feature, count + 1);
1558
+ this._totalActivations++;
1559
+ }
1560
+ }
1561
+ /**
1562
+ * Frequency-based pruning (Section 3.3).
1563
+ *
1564
+ * Sorts features by activation frequency, retains:
1565
+ * - All emergency features in bits 56-63 (never pruned)
1566
+ * - Top 48 regular features in bits 0-47 (high-frequency)
1567
+ * - Next 8 regular features in bits 48-55 (medium-frequency)
1568
+ *
1569
+ * Increments schema version.
1570
+ */
1571
+ prune() {
1572
+ const knownFeatures = this._collectKnownFeatures();
1573
+ const emergencyList = knownFeatures.filter((feature) => this.isEmergency(feature)).sort((a, b) => this._compareByFrequencyThenName(a, b));
1574
+ const regularList = knownFeatures.filter((feature) => !this.isEmergency(feature)).sort((a, b) => this._compareByFrequencyThenName(a, b));
1575
+ const newFeatureToBit = /* @__PURE__ */ new Map();
1576
+ const newBitToFeatures = /* @__PURE__ */ new Map();
1577
+ const [emergStart] = EMERGENCY_RANGE;
1578
+ for (let i = 0; i < Math.min(emergencyList.length, MAX_EMERGENCY_FEATURES); i++) {
1579
+ const bit = emergStart + i;
1580
+ const feature = emergencyList[i];
1581
+ newFeatureToBit.set(feature, bit);
1582
+ newBitToFeatures.set(bit, [feature]);
1583
+ }
1584
+ const [highStart] = HIGH_FREQ_RANGE;
1585
+ const highCount = HIGH_FREQ_RANGE[1] - HIGH_FREQ_RANGE[0] + 1;
1586
+ for (let i = 0; i < Math.min(regularList.length, highCount); i++) {
1587
+ const bit = highStart + i;
1588
+ const feature = regularList[i];
1589
+ newFeatureToBit.set(feature, bit);
1590
+ newBitToFeatures.set(bit, [feature]);
1591
+ }
1592
+ const [medStart] = MED_FREQ_RANGE;
1593
+ for (let i = highCount; i < Math.min(regularList.length, MAX_REGULAR_FEATURES); i++) {
1594
+ const bit = medStart + (i - highCount);
1595
+ const feature = regularList[i];
1596
+ newFeatureToBit.set(feature, bit);
1597
+ newBitToFeatures.set(bit, [feature]);
1598
+ }
1599
+ const excludedFeatures = [
1600
+ ...regularList.slice(MAX_REGULAR_FEATURES),
1601
+ ...emergencyList.slice(MAX_EMERGENCY_FEATURES)
1602
+ ];
1603
+ const mappingChanged = !this._mapsEqual(this._featureToBit, newFeatureToBit);
1604
+ if (mappingChanged) {
1605
+ this._featureToBit = newFeatureToBit;
1606
+ this._bitToFeatures = newBitToFeatures;
1607
+ this._version++;
1608
+ }
1609
+ return {
1610
+ pruned: excludedFeatures.length,
1611
+ retained: this._featureToBit.size,
1612
+ version: this._version,
1613
+ excludedFeatures
1614
+ };
1615
+ }
1616
+ /** Get a serializable snapshot of the current schema state. */
1617
+ snapshot() {
1618
+ let emergencyCount = 0;
1619
+ for (const [feat] of this._featureToBit) {
1620
+ if (this.isEmergency(feat)) emergencyCount++;
1621
+ }
1622
+ return {
1623
+ version: this._version,
1624
+ featureToBit: new Map(this._featureToBit),
1625
+ bitToFeatures: new Map(
1626
+ [...this._bitToFeatures].map(([k, v]) => [k, [...v]])
1627
+ ),
1628
+ totalTracked: this._activationCounts.size,
1629
+ activeFeatures: this._featureToBit.size,
1630
+ emergencyCount
1631
+ };
1632
+ }
1633
+ /**
1634
+ * Export canonical schema state for distribution.
1635
+ * Entries are sorted by bit (then feature) for deterministic serialization.
1636
+ */
1637
+ exportSchema() {
1638
+ const entries = [...this._featureToBit.entries()].sort((a, b) => {
1639
+ if (a[1] !== b[1]) return a[1] - b[1];
1640
+ return a[0] < b[0] ? -1 : a[0] > b[0] ? 1 : 0;
1641
+ }).map(([feature, bit]) => [feature, bit]);
1642
+ return {
1643
+ version: this._version,
1644
+ entries,
1645
+ emergencyPrefix: this._emergencyPrefix,
1646
+ emergencyFeatures: [...this._emergencyFeatures].sort(
1647
+ (a, b) => a < b ? -1 : a > b ? 1 : 0
1648
+ ),
1649
+ fingerprint: this.fingerprint
1650
+ };
1651
+ }
1652
+ /**
1653
+ * Import an exported schema state.
1654
+ * Replaces active mappings and resets activation counters.
1655
+ */
1656
+ importSchema(schema) {
1657
+ this._assertExportedSchema(schema);
1658
+ const newFeatureToBit = /* @__PURE__ */ new Map();
1659
+ const newBitToFeatures = /* @__PURE__ */ new Map();
1660
+ for (const [feature, bit] of schema.entries) {
1661
+ newFeatureToBit.set(feature, bit);
1662
+ newBitToFeatures.set(bit, [feature]);
1663
+ }
1664
+ const expectedFingerprint = this._computeFingerprint(
1665
+ schema.version,
1666
+ newFeatureToBit,
1667
+ schema.emergencyPrefix,
1668
+ new Set(schema.emergencyFeatures)
1669
+ );
1670
+ if (schema.fingerprint !== expectedFingerprint) {
1671
+ throw new Error(
1672
+ `Schema fingerprint mismatch: expected ${expectedFingerprint}, got ${schema.fingerprint}`
1673
+ );
1674
+ }
1675
+ this._featureToBit = newFeatureToBit;
1676
+ this._bitToFeatures = newBitToFeatures;
1677
+ this._emergencyPrefix = schema.emergencyPrefix;
1678
+ this._emergencyFeatures = new Set(schema.emergencyFeatures);
1679
+ this._activationCounts.clear();
1680
+ this._totalActivations = 0;
1681
+ this._version = schema.version;
1682
+ }
1683
+ /** Get activation frequency for a feature. */
1684
+ getFrequency(feature) {
1685
+ return this._activationCounts.get(feature) ?? 0;
1686
+ }
1687
+ /** Get all features ranked by activation frequency. */
1688
+ getRankedFeatures() {
1689
+ const ranked = [];
1690
+ for (const [feature, count] of this._activationCounts) {
1691
+ ranked.push({
1692
+ feature,
1693
+ count,
1694
+ bit: this._featureToBit.get(feature)
1695
+ });
1696
+ }
1697
+ ranked.sort((a, b) => b.count - a.count);
1698
+ return ranked;
1699
+ }
1700
+ /** Reset all activation counts (but keep schema mappings). */
1701
+ resetCounts() {
1702
+ this._activationCounts.clear();
1703
+ this._totalActivations = 0;
1704
+ }
1705
+ /**
1706
+ * Collision probability for a specific feature:
1707
+ * P(collision) = 1 - (1 - 1/64)^(m - 1)
1708
+ * where m is active feature count.
1709
+ */
1710
+ get theoreticalCollisionRate() {
1711
+ const m = this._featureToBit.size;
1712
+ if (m <= 1) return 0;
1713
+ return 1 - Math.pow(1 - 1 / BITMASK_WIDTH, m - 1);
1714
+ }
1715
+ /**
1716
+ * Expected excluded feature count under uniform assignment:
1717
+ * E[excluded] = m - 64 * (1 - (1 - 1/64)^m)
1718
+ */
1719
+ expectedExcludedFeatures(featureCount = this._featureToBit.size) {
1720
+ if (!Number.isInteger(featureCount) || featureCount < 0) {
1721
+ throw new RangeError(
1722
+ `featureCount must be a non-negative integer, got ${featureCount}`
1723
+ );
1724
+ }
1725
+ if (featureCount === 0) return 0;
1726
+ return featureCount - BITMASK_WIDTH * (1 - Math.pow(1 - 1 / BITMASK_WIDTH, featureCount));
1727
+ }
1728
+ // ── Private ──
1729
+ _registerEmergency(feature) {
1730
+ const [start, end] = EMERGENCY_RANGE;
1731
+ for (let bit = start; bit <= end; bit++) {
1732
+ if (!this._bitToFeatures.has(bit)) {
1733
+ this._featureToBit.set(feature, bit);
1734
+ this._bitToFeatures.set(bit, [feature]);
1735
+ this._version++;
1736
+ return bit;
1737
+ }
1738
+ }
1739
+ return -1;
1740
+ }
1741
+ _registerRegular(feature) {
1742
+ const [highStart, highEnd] = HIGH_FREQ_RANGE;
1743
+ for (let bit = highStart; bit <= highEnd; bit++) {
1744
+ if (!this._bitToFeatures.has(bit)) {
1745
+ this._featureToBit.set(feature, bit);
1746
+ this._bitToFeatures.set(bit, [feature]);
1747
+ this._version++;
1748
+ return bit;
1749
+ }
1750
+ }
1751
+ const [medStart, medEnd] = MED_FREQ_RANGE;
1752
+ for (let bit = medStart; bit <= medEnd; bit++) {
1753
+ if (!this._bitToFeatures.has(bit)) {
1754
+ this._featureToBit.set(feature, bit);
1755
+ this._bitToFeatures.set(bit, [feature]);
1756
+ this._version++;
1757
+ return bit;
1758
+ }
1759
+ }
1760
+ return -1;
1761
+ }
1762
+ _collectKnownFeatures() {
1763
+ const all = /* @__PURE__ */ new Set();
1764
+ for (const feature of this._activationCounts.keys()) all.add(feature);
1765
+ for (const feature of this._featureToBit.keys()) all.add(feature);
1766
+ for (const feature of this._emergencyFeatures) all.add(feature);
1767
+ return [...all];
1768
+ }
1769
+ _compareByFrequencyThenName(a, b) {
1770
+ const countA = this._activationCounts.get(a) ?? 0;
1771
+ const countB = this._activationCounts.get(b) ?? 0;
1772
+ if (countA !== countB) return countB - countA;
1773
+ if (a < b) return -1;
1774
+ if (a > b) return 1;
1775
+ return 0;
1776
+ }
1777
+ _mapsEqual(a, b) {
1778
+ if (a.size !== b.size) return false;
1779
+ for (const [feature, bit] of a) {
1780
+ if (b.get(feature) !== bit) return false;
1781
+ }
1782
+ return true;
1783
+ }
1784
+ _assertExportedSchema(schema) {
1785
+ if (!Number.isInteger(schema.version) || schema.version < 0) {
1786
+ throw new RangeError(`Schema version must be a non-negative integer, got ${schema.version}`);
1787
+ }
1788
+ if (typeof schema.fingerprint !== "string" || schema.fingerprint.length === 0) {
1789
+ throw new TypeError("Schema fingerprint must be a non-empty string");
1790
+ }
1791
+ const seenFeatures = /* @__PURE__ */ new Set();
1792
+ const seenBits = /* @__PURE__ */ new Set();
1793
+ for (const [feature, bit] of schema.entries) {
1794
+ if (!feature || typeof feature !== "string") {
1795
+ throw new TypeError(`Invalid feature name in schema export: ${String(feature)}`);
1796
+ }
1797
+ if (!Number.isInteger(bit) || bit < 0 || bit >= BITMASK_WIDTH) {
1798
+ throw new RangeError(`Invalid bit position in schema export: ${bit}`);
1799
+ }
1800
+ if (seenFeatures.has(feature)) {
1801
+ throw new Error(`Duplicate feature in schema export: ${feature}`);
1802
+ }
1803
+ if (seenBits.has(bit)) {
1804
+ throw new Error(`Duplicate bit in schema export: ${bit}`);
1805
+ }
1806
+ seenFeatures.add(feature);
1807
+ seenBits.add(bit);
1808
+ }
1809
+ }
1810
+ _computeFingerprint(version, mapping, emergencyPrefix, emergencyFeatures) {
1811
+ const canonicalEntries = [...mapping.entries()].sort((a, b) => {
1812
+ if (a[1] !== b[1]) return a[1] - b[1];
1813
+ if (a[0] < b[0]) return -1;
1814
+ if (a[0] > b[0]) return 1;
1815
+ return 0;
1816
+ }).map(([feature, bit]) => `${bit}:${feature}`).join("|");
1817
+ const canonicalEmergency = [...emergencyFeatures].sort((a, b) => a < b ? -1 : a > b ? 1 : 0).join("|");
1818
+ const canonical = `v=${version};ep=${emergencyPrefix};ef=${canonicalEmergency};m=${canonicalEntries}`;
1819
+ let hash = FNV_OFFSET_64;
1820
+ for (const char of canonical) {
1821
+ hash ^= BigInt(char.codePointAt(0) ?? 0);
1822
+ hash = hash * FNV_PRIME_64 & MASK_64;
1823
+ }
1824
+ return hash.toString(16).padStart(16, "0");
1825
+ }
1826
+ };
1827
+
1828
+ // src/coordinator.ts
1829
+ var Coordinator = class {
1830
+ _buffer = [];
1831
+ _seenAgents = /* @__PURE__ */ new Set();
1832
+ _schemaVersion;
1833
+ _deadlineMs;
1834
+ _staleMessagePolicy;
1835
+ _onTelemetry;
1836
+ _roundStartTime = 0;
1837
+ _aggregationCount = 0;
1838
+ _droppedStaleMessages = 0;
1839
+ constructor(config = {}) {
1840
+ this._deadlineMs = config.deadlineMs ?? 15;
1841
+ this._schemaVersion = config.schemaVersion;
1842
+ this._staleMessagePolicy = config.staleMessagePolicy ?? "accept";
1843
+ this._onTelemetry = config.onTelemetry;
1844
+ }
1845
+ /** Number of messages in current buffer. */
1846
+ get bufferedCount() {
1847
+ return this._buffer.length;
1848
+ }
1849
+ /** Total aggregation rounds performed. */
1850
+ get aggregationCount() {
1851
+ return this._aggregationCount;
1852
+ }
1853
+ /** Update expected schema version. */
1854
+ set schemaVersion(version) {
1855
+ this._schemaVersion = version;
1856
+ }
1857
+ /** Start a new coordination round. Clears the buffer. */
1858
+ startRound() {
1859
+ this._buffer = [];
1860
+ this._seenAgents.clear();
1861
+ this._roundStartTime = performance.now();
1862
+ this._droppedStaleMessages = 0;
1863
+ }
1864
+ /**
1865
+ * Receive a message from an agent.
1866
+ * Returns false if the message was dropped (deadline exceeded or duplicate agent).
1867
+ */
1868
+ receive(message) {
1869
+ if (this._roundStartTime > 0) {
1870
+ const elapsed = performance.now() - this._roundStartTime;
1871
+ if (elapsed > this._deadlineMs) {
1872
+ this._emitTelemetry({
1873
+ type: "message_dropped",
1874
+ agentId: message.agentId,
1875
+ reason: "deadline"
1876
+ });
1877
+ return false;
1878
+ }
1879
+ }
1880
+ const isStale = this._schemaVersion !== void 0 && message.schemaVersion !== this._schemaVersion;
1881
+ if (isStale) {
1882
+ if (this._staleMessagePolicy === "drop") {
1883
+ this._droppedStaleMessages++;
1884
+ this._emitTelemetry({
1885
+ type: "message_dropped",
1886
+ agentId: message.agentId,
1887
+ reason: "stale"
1888
+ });
1889
+ return false;
1890
+ }
1891
+ if (this._staleMessagePolicy === "warn") {
1892
+ console.warn(
1893
+ `adaptive-bitmask: stale schema version from agent ${message.agentId} (expected ${this._schemaVersion}, got ${message.schemaVersion})`
1894
+ );
1895
+ }
1896
+ }
1897
+ if (this._seenAgents.has(message.agentId)) {
1898
+ const idx = this._buffer.findIndex((m) => m.agentId === message.agentId);
1899
+ if (idx !== -1) {
1900
+ this._buffer[idx] = message;
1901
+ }
1902
+ this._emitTelemetry({
1903
+ type: "message_accepted",
1904
+ agentId: message.agentId,
1905
+ stale: isStale,
1906
+ replaced: true
1907
+ });
1908
+ return true;
1909
+ }
1910
+ this._seenAgents.add(message.agentId);
1911
+ this._buffer.push(message);
1912
+ this._emitTelemetry({
1913
+ type: "message_accepted",
1914
+ agentId: message.agentId,
1915
+ stale: isStale,
1916
+ replaced: false
1917
+ });
1918
+ return true;
1919
+ }
1920
+ /**
1921
+ * Receive multiple messages at once.
1922
+ * Returns number of messages accepted.
1923
+ */
1924
+ receiveAll(messages) {
1925
+ let accepted = 0;
1926
+ for (const msg of messages) {
1927
+ if (this.receive(msg)) accepted++;
1928
+ }
1929
+ return accepted;
1930
+ }
1931
+ /**
1932
+ * Aggregate all buffered messages into a consensus result.
1933
+ * Clears the buffer after aggregation.
1934
+ */
1935
+ aggregate() {
1936
+ const t0 = performance.now();
1937
+ let aggregated = 0n;
1938
+ const bitVotes = /* @__PURE__ */ new Map();
1939
+ let staleCount = 0;
1940
+ const uniqueAgents = /* @__PURE__ */ new Set();
1941
+ for (const msg of this._buffer) {
1942
+ uniqueAgents.add(msg.agentId);
1943
+ if (this._schemaVersion !== void 0 && msg.schemaVersion !== this._schemaVersion) {
1944
+ staleCount++;
1945
+ }
1946
+ aggregated |= msg.mask;
1947
+ forEachSetBit(msg.mask, (bit) => {
1948
+ bitVotes.set(bit, (bitVotes.get(bit) ?? 0) + 1);
1949
+ });
1950
+ }
1951
+ const messageCount = this._buffer.length;
1952
+ const confidence = /* @__PURE__ */ new Map();
1953
+ for (const [bit, votes] of bitVotes) {
1954
+ confidence.set(bit, messageCount > 0 ? votes / messageCount : 0);
1955
+ }
1956
+ const elapsed = (performance.now() - t0) * 1e3;
1957
+ this._aggregationCount++;
1958
+ this._buffer = [];
1959
+ this._seenAgents.clear();
1960
+ const result = {
1961
+ aggregatedMask: aggregated,
1962
+ confidence,
1963
+ messageCount,
1964
+ uniqueAgents: uniqueAgents.size,
1965
+ staleMessages: staleCount,
1966
+ droppedStaleMessages: this._droppedStaleMessages,
1967
+ aggregationTimeUs: elapsed
1968
+ };
1969
+ this._emitTelemetry({ type: "round_aggregated", result });
1970
+ return result;
1971
+ }
1972
+ _emitTelemetry(event) {
1973
+ this._onTelemetry?.(event);
1974
+ }
1975
+ };
1976
+
1977
+ // src/arbiter.ts
1978
+ var Arbiter = class {
1979
+ _weights;
1980
+ _weightSum;
1981
+ _executeThreshold;
1982
+ _synthesizeThreshold;
1983
+ _emergencyOverride;
1984
+ _onTelemetry;
1985
+ _decisionCount = 0;
1986
+ constructor(config = {}) {
1987
+ this._executeThreshold = config.executeThreshold ?? 0.55;
1988
+ this._synthesizeThreshold = config.synthesizeThreshold ?? 0.4;
1989
+ this._emergencyOverride = config.emergencyOverride ?? true;
1990
+ this._onTelemetry = config.onTelemetry;
1991
+ this._weights = new Float64Array(BITMASK_WIDTH);
1992
+ if (config.weights) {
1993
+ if (config.weights.length !== BITMASK_WIDTH) {
1994
+ throw new Error(`Weight vector must have ${BITMASK_WIDTH} elements, got ${config.weights.length}`);
1995
+ }
1996
+ this._weights.set(config.weights);
1997
+ } else {
1998
+ this._weights.fill(1);
1999
+ }
2000
+ this._weightSum = this._weights.reduce((sum, w) => sum + w, 0);
2001
+ }
2002
+ /** Number of decisions made since creation. */
2003
+ get decisionCount() {
2004
+ return this._decisionCount;
2005
+ }
2006
+ /** Set the weight for a specific bit position. */
2007
+ setWeight(position, weight) {
2008
+ if (position < 0 || position >= BITMASK_WIDTH) {
2009
+ throw new RangeError(`Position ${position} out of range`);
2010
+ }
2011
+ this._weightSum -= this._weights[position];
2012
+ this._weights[position] = weight;
2013
+ this._weightSum += weight;
2014
+ }
2015
+ /** Get the current weight vector (copy). */
2016
+ get weights() {
2017
+ return Array.from(this._weights);
2018
+ }
2019
+ /**
2020
+ * Score an aggregated bitmask and produce a decision.
2021
+ *
2022
+ * @param aggregatedMask — OR-aggregated mask from all agents
2023
+ * @param confidence — Optional per-bit confidence (fraction of agents that set each bit)
2024
+ */
2025
+ score(aggregatedMask, confidence) {
2026
+ const t0 = performance.now();
2027
+ const active = activeBits(aggregatedMask);
2028
+ const emergency = hasEmergency(aggregatedMask);
2029
+ if (emergency && this._emergencyOverride) {
2030
+ const elapsed2 = (performance.now() - t0) * 1e3;
2031
+ this._decisionCount++;
2032
+ const result2 = {
2033
+ decision: "REJECT",
2034
+ rawScore: 0,
2035
+ confidenceScore: 0,
2036
+ finalScore: 0,
2037
+ activeBitCount: active.length,
2038
+ hasEmergency: true,
2039
+ scoringTimeUs: elapsed2
2040
+ };
2041
+ this._emitTelemetry({ type: "decision", result: result2 });
2042
+ return result2;
2043
+ }
2044
+ let numerator = 0;
2045
+ for (const bit of active) {
2046
+ numerator += this._weights[bit];
2047
+ }
2048
+ const rawScore = this._weightSum > 0 ? numerator / this._weightSum : 0;
2049
+ let confidenceScore = rawScore;
2050
+ if (confidence && confidence.size > 0) {
2051
+ let confNumerator = 0;
2052
+ let confDenominator = 0;
2053
+ for (const bit of active) {
2054
+ const conf = confidence.get(bit) ?? 0;
2055
+ confNumerator += conf * this._weights[bit];
2056
+ confDenominator += this._weights[bit];
2057
+ }
2058
+ confidenceScore = confDenominator > 0 ? confNumerator / confDenominator : rawScore;
2059
+ }
2060
+ const finalScore = Math.min(1, rawScore * 0.6 + confidenceScore * 0.4);
2061
+ let decision;
2062
+ if (finalScore >= this._executeThreshold) {
2063
+ decision = "EXECUTE";
2064
+ } else if (finalScore >= this._synthesizeThreshold) {
2065
+ decision = "SYNTHESIZE";
2066
+ } else {
2067
+ decision = "REJECT";
2068
+ }
2069
+ const elapsed = (performance.now() - t0) * 1e3;
2070
+ this._decisionCount++;
2071
+ const result = {
2072
+ decision,
2073
+ rawScore,
2074
+ confidenceScore,
2075
+ finalScore,
2076
+ activeBitCount: active.length,
2077
+ hasEmergency: emergency,
2078
+ scoringTimeUs: elapsed
2079
+ };
2080
+ this._emitTelemetry({ type: "decision", result });
2081
+ return result;
2082
+ }
2083
+ /**
2084
+ * Convenience: aggregate messages then score.
2085
+ * OR-merges all message masks, computes per-bit confidence,
2086
+ * validates schema versions, then runs scoring.
2087
+ */
2088
+ scoreMessages(messages, expectedSchemaVersion) {
2089
+ if (messages.length === 0) {
2090
+ return {
2091
+ decision: "REJECT",
2092
+ rawScore: 0,
2093
+ confidenceScore: 0,
2094
+ finalScore: 0,
2095
+ activeBitCount: 0,
2096
+ hasEmergency: false,
2097
+ scoringTimeUs: 0,
2098
+ staleCount: 0
2099
+ };
2100
+ }
2101
+ let aggregated = 0n;
2102
+ const bitVotes = /* @__PURE__ */ new Map();
2103
+ let staleCount = 0;
2104
+ for (const msg of messages) {
2105
+ if (expectedSchemaVersion !== void 0 && msg.schemaVersion !== expectedSchemaVersion) {
2106
+ staleCount++;
2107
+ }
2108
+ aggregated |= msg.mask;
2109
+ forEachSetBit(msg.mask, (bit) => {
2110
+ bitVotes.set(bit, (bitVotes.get(bit) ?? 0) + 1);
2111
+ });
2112
+ }
2113
+ const confidence = /* @__PURE__ */ new Map();
2114
+ for (const [bit, votes] of bitVotes) {
2115
+ confidence.set(bit, votes / messages.length);
2116
+ }
2117
+ const result = this.score(aggregated, confidence);
2118
+ this._emitTelemetry({
2119
+ type: "score_messages",
2120
+ messageCount: messages.length,
2121
+ staleCount,
2122
+ result
2123
+ });
2124
+ return { ...result, staleCount };
2125
+ }
2126
+ /**
2127
+ * Paper-canonical strategy ranking and decision logic (Section 6).
2128
+ * Uses ŝ_final = 0.6*ŝ_raw + 0.4*c, then applies:
2129
+ * - EXECUTE if lead > 15 points
2130
+ * - SYNTHESIZE if top strategies are within threshold
2131
+ * - REJECT if top score < 40%
2132
+ */
2133
+ scoreStrategies(candidates, options = {}) {
2134
+ if (candidates.length === 0) {
2135
+ const emptyResult = {
2136
+ decision: "REJECT",
2137
+ leadScore: 0,
2138
+ rankings: []
2139
+ };
2140
+ this._emitTelemetry({ type: "strategy_decision", result: emptyResult });
2141
+ return emptyResult;
2142
+ }
2143
+ const leadThreshold = options.leadThreshold ?? 0.15;
2144
+ const rejectThreshold = options.rejectThreshold ?? 0.4;
2145
+ const rankings = candidates.map((candidate) => {
2146
+ const confidence = candidate.confidence ?? options.globalConfidence;
2147
+ const scored = this._scoreMaskComponents(candidate.mask, confidence);
2148
+ return {
2149
+ id: candidate.id,
2150
+ mask: candidate.mask,
2151
+ rawScore: scored.rawScore,
2152
+ confidenceScore: scored.confidenceScore,
2153
+ finalScore: scored.finalScore
2154
+ };
2155
+ }).sort((a, b) => {
2156
+ if (b.finalScore !== a.finalScore) return b.finalScore - a.finalScore;
2157
+ if (a.id < b.id) return -1;
2158
+ if (a.id > b.id) return 1;
2159
+ return 0;
2160
+ });
2161
+ const top1 = rankings[0];
2162
+ const top2 = rankings[1];
2163
+ const leadScore = top2 ? top1.finalScore - top2.finalScore : top1.finalScore;
2164
+ let result;
2165
+ if (top1.finalScore < rejectThreshold) {
2166
+ result = {
2167
+ decision: "REJECT",
2168
+ leadScore,
2169
+ rankings
2170
+ };
2171
+ } else if (leadScore > leadThreshold) {
2172
+ result = {
2173
+ decision: "EXECUTE",
2174
+ selectedStrategyId: top1.id,
2175
+ leadScore,
2176
+ rankings
2177
+ };
2178
+ } else {
2179
+ const contenders = rankings.slice(0, Math.min(3, rankings.length)).filter((candidate) => top1.finalScore - candidate.finalScore <= leadThreshold);
2180
+ const contenderMasks = contenders.length >= 2 ? contenders.map((candidate) => candidate.mask) : rankings.slice(0, Math.min(2, rankings.length)).map((candidate) => candidate.mask);
2181
+ result = {
2182
+ decision: "SYNTHESIZE",
2183
+ synthesizedMask: this._synthesizeMask(contenderMasks),
2184
+ leadScore,
2185
+ rankings
2186
+ };
2187
+ }
2188
+ this._decisionCount++;
2189
+ this._emitTelemetry({ type: "strategy_decision", result });
2190
+ return result;
2191
+ }
2192
+ _scoreMaskComponents(mask, confidence) {
2193
+ if (hasEmergency(mask) && this._emergencyOverride) {
2194
+ return { rawScore: 0, confidenceScore: 0, finalScore: 0 };
2195
+ }
2196
+ const active = activeBits(mask);
2197
+ let numerator = 0;
2198
+ for (const bit of active) {
2199
+ numerator += this._weights[bit];
2200
+ }
2201
+ const rawScore = this._weightSum > 0 ? numerator / this._weightSum : 0;
2202
+ let confidenceScore = rawScore;
2203
+ if (confidence && confidence.size > 0) {
2204
+ let confNumerator = 0;
2205
+ let confDenominator = 0;
2206
+ for (const bit of active) {
2207
+ const conf = confidence.get(bit) ?? 0;
2208
+ confNumerator += conf * this._weights[bit];
2209
+ confDenominator += this._weights[bit];
2210
+ }
2211
+ confidenceScore = confDenominator > 0 ? confNumerator / confDenominator : rawScore;
2212
+ }
2213
+ return {
2214
+ rawScore,
2215
+ confidenceScore,
2216
+ finalScore: Math.min(1, rawScore * 0.6 + confidenceScore * 0.4)
2217
+ };
2218
+ }
2219
+ _synthesizeMask(masks) {
2220
+ if (masks.length === 0) return 0n;
2221
+ const votes = /* @__PURE__ */ new Map();
2222
+ for (const mask of masks) {
2223
+ forEachSetBit(mask, (bit) => {
2224
+ votes.set(bit, (votes.get(bit) ?? 0) + 1);
2225
+ });
2226
+ }
2227
+ const requiredVotes = Math.floor(masks.length / 2) + 1;
2228
+ let synthesized = 0n;
2229
+ for (const [bit, count] of votes) {
2230
+ if (count >= requiredVotes) {
2231
+ synthesized |= 1n << BigInt(bit);
2232
+ }
2233
+ }
2234
+ return synthesized;
2235
+ }
2236
+ _emitTelemetry(event) {
2237
+ this._onTelemetry?.(event);
2238
+ }
2239
+ };
2240
+ function createFinancialArbiter(overrides) {
2241
+ const weights = new Array(BITMASK_WIDTH).fill(0.08);
2242
+ weights[0] = 0.25;
2243
+ weights[1] = 0.25;
2244
+ weights[2] = 0.2;
2245
+ weights[3] = 0.2;
2246
+ weights[8] = 0.2;
2247
+ weights[10] = 0.18;
2248
+ weights[12] = 0.15;
2249
+ weights[13] = 0.22;
2250
+ for (let i = 48; i < 56; i++) weights[i] = 0.12;
2251
+ for (let i = 56; i < 64; i++) weights[i] = 0.45;
2252
+ return new Arbiter({ weights, ...overrides });
2253
+ }
2254
+ function createRoboticArbiter(overrides) {
2255
+ const weights = new Array(BITMASK_WIDTH).fill(0.1);
2256
+ weights[0] = 0.3;
2257
+ weights[4] = 0.25;
2258
+ weights[10] = 0.2;
2259
+ for (let i = 56; i < 64; i++) weights[i] = 0.45;
2260
+ return new Arbiter({ weights, emergencyOverride: true, ...overrides });
2261
+ }
2262
+
2263
+ // src/SharedCognition.ts
2264
+ var SharedCognition = class {
2265
+ schema;
2266
+ coordinator;
2267
+ arbiter;
2268
+ autoRegister;
2269
+ constructor(config = {}) {
2270
+ this.schema = new SchemaManager(config.schema);
2271
+ this.coordinator = new Coordinator(config.coordinator);
2272
+ this.arbiter = new Arbiter(config.arbiter);
2273
+ this.autoRegister = config.autoRegister ?? true;
2274
+ }
2275
+ /**
2276
+ * Process a single coordination tick across the entire swarm.
2277
+ *
2278
+ * 1. Encodes all agent string observations into 64-bit masks
2279
+ * 2. Routes through the Meta-Coordinator
2280
+ * 3. Arbitrates a final decision
2281
+ *
2282
+ * @param agentObservations Array of string feature arrays, one per agent
2283
+ * @returns The unified swarm decision and latency
2284
+ */
2285
+ processSwarmTick(agentObservations) {
2286
+ const startMs = performance.now();
2287
+ if (this.autoRegister) {
2288
+ const uniqueFeatures = /* @__PURE__ */ new Set();
2289
+ for (const obs of agentObservations) {
2290
+ for (const feature of obs) {
2291
+ uniqueFeatures.add(feature);
2292
+ }
2293
+ }
2294
+ this.schema.registerAll(Array.from(uniqueFeatures));
2295
+ }
2296
+ for (const obs of agentObservations) {
2297
+ this.schema.recordActivations(obs);
2298
+ }
2299
+ this.coordinator.startRound();
2300
+ this.coordinator.schemaVersion = this.schema.version;
2301
+ let agentId = 0;
2302
+ for (const obs of agentObservations) {
2303
+ const { mask } = encode(obs, this.schema.featureToBit);
2304
+ const msg = BitmaskMessage.now(mask, agentId++, this.schema.version);
2305
+ this.coordinator.receive(msg);
2306
+ }
2307
+ const { aggregatedMask, confidence } = this.coordinator.aggregate();
2308
+ const arbiterResult = this.arbiter.score(aggregatedMask, confidence);
2309
+ const activeFeatures = decode(aggregatedMask, this.schema.bitToFeatures);
2310
+ const latencyMs = performance.now() - startMs;
2311
+ return {
2312
+ decision: arbiterResult.decision,
2313
+ finalScore: arbiterResult.finalScore,
2314
+ latencyMs,
2315
+ activeFeatures,
2316
+ arbiterResult
2317
+ };
2318
+ }
2319
+ };
2320
+
2321
+ // src/envelope.ts
2322
+ function createEnvelope(message, schemaFingerprint, roundId) {
2323
+ if (!schemaFingerprint) {
2324
+ throw new Error("schemaFingerprint is required");
2325
+ }
2326
+ return {
2327
+ schemaFingerprint,
2328
+ roundId,
2329
+ payload: message.toBytes()
2330
+ };
2331
+ }
2332
+ function decodeEnvelope(envelope, expectedSchemaFingerprint) {
2333
+ if (!envelope.schemaFingerprint) {
2334
+ throw new Error("Envelope schemaFingerprint is required");
2335
+ }
2336
+ if (expectedSchemaFingerprint && envelope.schemaFingerprint !== expectedSchemaFingerprint) {
2337
+ throw new Error(
2338
+ `Schema fingerprint mismatch: expected ${expectedSchemaFingerprint}, got ${envelope.schemaFingerprint}`
2339
+ );
2340
+ }
2341
+ return BitmaskMessage.deserialize(envelope.payload);
2342
+ }
2343
+
2344
+ export {
2345
+ MESSAGE_SIZE_BYTES,
2346
+ BitmaskMessage,
2347
+ AdaptiveBitmaskError,
2348
+ ValidationError,
2349
+ SchemaValidationError,
2350
+ MessageValidationError,
2351
+ RuntimeError,
2352
+ CoordinatorError,
2353
+ ArbiterError,
2354
+ NetworkError,
2355
+ TimeoutError,
2356
+ SystemError,
2357
+ MemoryError,
2358
+ Validator,
2359
+ CircuitBreaker,
2360
+ TimeoutManager,
2361
+ RecoveryManager,
2362
+ WebSocketTransport,
2363
+ createWebSocketTransport,
2364
+ HttpTransport,
2365
+ createHttpTransport,
2366
+ LogLevel,
2367
+ Logger,
2368
+ MetricsCollector,
2369
+ HealthChecker,
2370
+ Profiler,
2371
+ createProductionLogger,
2372
+ createMetricsCollector,
2373
+ BITMASK_WIDTH,
2374
+ EMERGENCY_RANGE,
2375
+ HIGH_FREQ_RANGE,
2376
+ MED_FREQ_RANGE,
2377
+ empty,
2378
+ setBit,
2379
+ clearBit,
2380
+ testBit,
2381
+ popcount,
2382
+ activeBits,
2383
+ forEachSetBit,
2384
+ merge,
2385
+ intersect,
2386
+ delta,
2387
+ hammingDistance,
2388
+ hasEmergency,
2389
+ emergencyBits,
2390
+ toBytes,
2391
+ fromBytes,
2392
+ encode,
2393
+ decode,
2394
+ SchemaManager,
2395
+ Coordinator,
2396
+ Arbiter,
2397
+ createFinancialArbiter,
2398
+ createRoboticArbiter,
2399
+ SharedCognition,
2400
+ createEnvelope,
2401
+ decodeEnvelope
2402
+ };