@multi-agent-protocol/sdk 0.0.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js ADDED
@@ -0,0 +1,4353 @@
1
+ export { monotonicFactory, ulid } from 'ulid';
2
+ import { z } from 'zod';
3
+
4
+ // src/types/index.ts
5
+ function isOrphanedAgent(agent) {
6
+ return agent.ownerId === null;
7
+ }
8
+ var EVENT_TYPES = {
9
+ // Agent lifecycle events
10
+ AGENT_REGISTERED: "agent_registered",
11
+ AGENT_UNREGISTERED: "agent_unregistered",
12
+ AGENT_STATE_CHANGED: "agent_state_changed",
13
+ AGENT_ORPHANED: "agent_orphaned",
14
+ // Participant lifecycle events
15
+ PARTICIPANT_CONNECTED: "participant_connected",
16
+ PARTICIPANT_DISCONNECTED: "participant_disconnected",
17
+ // Message events
18
+ MESSAGE_SENT: "message_sent",
19
+ MESSAGE_DELIVERED: "message_delivered",
20
+ MESSAGE_FAILED: "message_failed",
21
+ // Scope events
22
+ SCOPE_CREATED: "scope_created",
23
+ SCOPE_DELETED: "scope_deleted",
24
+ SCOPE_MEMBER_JOINED: "scope_member_joined",
25
+ SCOPE_MEMBER_LEFT: "scope_member_left",
26
+ // Permission events
27
+ PERMISSIONS_CLIENT_UPDATED: "permissions_client_updated",
28
+ PERMISSIONS_AGENT_UPDATED: "permissions_agent_updated",
29
+ // System events
30
+ SYSTEM_ERROR: "system_error",
31
+ // Federation events
32
+ FEDERATION_CONNECTED: "federation_connected",
33
+ FEDERATION_DISCONNECTED: "federation_disconnected"
34
+ };
35
+ function createEvent(input) {
36
+ return {
37
+ id: `evt-${Date.now()}-${Math.random().toString(36).slice(2, 8)}`,
38
+ timestamp: input.timestamp ?? Date.now(),
39
+ type: input.type,
40
+ source: input.source,
41
+ data: input.data,
42
+ causedBy: input.causedBy,
43
+ _meta: input._meta
44
+ };
45
+ }
46
+ var JSONRPC_VERSION = "2.0";
47
+ var CORE_METHODS = {
48
+ CONNECT: "map/connect",
49
+ DISCONNECT: "map/disconnect",
50
+ SEND: "map/send",
51
+ SUBSCRIBE: "map/subscribe",
52
+ UNSUBSCRIBE: "map/unsubscribe",
53
+ REPLAY: "map/replay"
54
+ };
55
+ var OBSERVATION_METHODS = {
56
+ AGENTS_LIST: "map/agents/list",
57
+ AGENTS_GET: "map/agents/get",
58
+ SCOPES_LIST: "map/scopes/list",
59
+ SCOPES_GET: "map/scopes/get",
60
+ SCOPES_MEMBERS: "map/scopes/members",
61
+ STRUCTURE_GRAPH: "map/structure/graph"
62
+ };
63
+ var LIFECYCLE_METHODS = {
64
+ AGENTS_REGISTER: "map/agents/register",
65
+ AGENTS_UNREGISTER: "map/agents/unregister",
66
+ AGENTS_SPAWN: "map/agents/spawn"
67
+ };
68
+ var STATE_METHODS = {
69
+ AGENTS_UPDATE: "map/agents/update",
70
+ AGENTS_SUSPEND: "map/agents/suspend",
71
+ AGENTS_RESUME: "map/agents/resume",
72
+ AGENTS_STOP: "map/agents/stop"
73
+ };
74
+ var STEERING_METHODS = {
75
+ INJECT: "map/inject"
76
+ };
77
+ var SCOPE_METHODS = {
78
+ SCOPES_CREATE: "map/scopes/create",
79
+ SCOPES_DELETE: "map/scopes/delete",
80
+ SCOPES_JOIN: "map/scopes/join",
81
+ SCOPES_LEAVE: "map/scopes/leave"
82
+ };
83
+ var SESSION_METHODS = {
84
+ SESSION_LIST: "map/session/list",
85
+ SESSION_LOAD: "map/session/load",
86
+ SESSION_CLOSE: "map/session/close"
87
+ };
88
+ var AUTH_METHODS = {
89
+ AUTH_REFRESH: "map/auth/refresh"
90
+ };
91
+ var PERMISSION_METHODS = {
92
+ PERMISSIONS_UPDATE: "map/permissions/update"
93
+ };
94
+ var FEDERATION_METHODS = {
95
+ FEDERATION_CONNECT: "map/federation/connect",
96
+ FEDERATION_ROUTE: "map/federation/route"
97
+ };
98
+ var NOTIFICATION_METHODS = {
99
+ EVENT: "map/event",
100
+ MESSAGE: "map/message",
101
+ /** Client acknowledges received events (for backpressure) */
102
+ SUBSCRIBE_ACK: "map/subscribe.ack"
103
+ };
104
+ var MAP_METHODS = {
105
+ ...CORE_METHODS,
106
+ ...OBSERVATION_METHODS,
107
+ ...LIFECYCLE_METHODS,
108
+ ...STATE_METHODS,
109
+ ...STEERING_METHODS,
110
+ ...SCOPE_METHODS,
111
+ ...SESSION_METHODS,
112
+ ...AUTH_METHODS,
113
+ ...PERMISSION_METHODS,
114
+ ...FEDERATION_METHODS
115
+ };
116
+ var STRUCTURE_METHODS = {
117
+ ...LIFECYCLE_METHODS,
118
+ ...STATE_METHODS,
119
+ ...SCOPE_METHODS,
120
+ STRUCTURE_GRAPH: OBSERVATION_METHODS.STRUCTURE_GRAPH
121
+ };
122
+ var EXTENSION_METHODS = {
123
+ ...STEERING_METHODS,
124
+ ...FEDERATION_METHODS
125
+ };
126
+ var PROTOCOL_ERROR_CODES = {
127
+ PARSE_ERROR: -32700,
128
+ INVALID_REQUEST: -32600,
129
+ METHOD_NOT_FOUND: -32601,
130
+ INVALID_PARAMS: -32602,
131
+ INTERNAL_ERROR: -32603
132
+ };
133
+ var AUTH_ERROR_CODES = {
134
+ AUTH_REQUIRED: 1e3,
135
+ AUTH_FAILED: 1001,
136
+ TOKEN_EXPIRED: 1002,
137
+ PERMISSION_DENIED: 1003
138
+ };
139
+ var ROUTING_ERROR_CODES = {
140
+ ADDRESS_NOT_FOUND: 2e3,
141
+ AGENT_NOT_FOUND: 2001,
142
+ SCOPE_NOT_FOUND: 2002,
143
+ DELIVERY_FAILED: 2003,
144
+ ADDRESS_AMBIGUOUS: 2004
145
+ };
146
+ var AGENT_ERROR_CODES = {
147
+ AGENT_EXISTS: 3e3,
148
+ STATE_INVALID: 3001,
149
+ NOT_RESPONDING: 3002,
150
+ TERMINATED: 3003,
151
+ SPAWN_FAILED: 3004
152
+ };
153
+ var RESOURCE_ERROR_CODES = {
154
+ EXHAUSTED: 4e3,
155
+ RATE_LIMITED: 4001,
156
+ QUOTA_EXCEEDED: 4002
157
+ };
158
+ var FEDERATION_ERROR_CODES = {
159
+ FEDERATION_UNAVAILABLE: 5e3,
160
+ FEDERATION_SYSTEM_NOT_FOUND: 5001,
161
+ FEDERATION_AUTH_FAILED: 5002,
162
+ FEDERATION_ROUTE_REJECTED: 5003,
163
+ /** Message has already visited this system (loop detected) */
164
+ FEDERATION_LOOP_DETECTED: 5010,
165
+ /** Message exceeded maximum hop count */
166
+ FEDERATION_MAX_HOPS_EXCEEDED: 5011
167
+ };
168
+ var ERROR_CODES = {
169
+ ...PROTOCOL_ERROR_CODES,
170
+ ...AUTH_ERROR_CODES,
171
+ ...ROUTING_ERROR_CODES,
172
+ ...AGENT_ERROR_CODES,
173
+ ...RESOURCE_ERROR_CODES,
174
+ ...FEDERATION_ERROR_CODES
175
+ };
176
+ var PROTOCOL_VERSION = 1;
177
+ var CAPABILITY_REQUIREMENTS = {
178
+ // Core
179
+ [CORE_METHODS.CONNECT]: [],
180
+ [CORE_METHODS.DISCONNECT]: [],
181
+ [CORE_METHODS.SEND]: ["messaging.canSend"],
182
+ [CORE_METHODS.SUBSCRIBE]: ["observation.canObserve"],
183
+ [CORE_METHODS.UNSUBSCRIBE]: ["observation.canObserve"],
184
+ // Observation
185
+ [OBSERVATION_METHODS.AGENTS_LIST]: ["observation.canQuery"],
186
+ [OBSERVATION_METHODS.AGENTS_GET]: ["observation.canQuery"],
187
+ [OBSERVATION_METHODS.SCOPES_LIST]: ["observation.canQuery"],
188
+ [OBSERVATION_METHODS.SCOPES_GET]: ["observation.canQuery"],
189
+ [OBSERVATION_METHODS.SCOPES_MEMBERS]: ["observation.canQuery"],
190
+ [OBSERVATION_METHODS.STRUCTURE_GRAPH]: ["observation.canQuery"],
191
+ // Lifecycle
192
+ [LIFECYCLE_METHODS.AGENTS_REGISTER]: ["lifecycle.canRegister"],
193
+ [LIFECYCLE_METHODS.AGENTS_UNREGISTER]: ["lifecycle.canUnregister"],
194
+ [LIFECYCLE_METHODS.AGENTS_SPAWN]: ["lifecycle.canSpawn"],
195
+ // State
196
+ [STATE_METHODS.AGENTS_UPDATE]: ["lifecycle.canRegister"],
197
+ [STATE_METHODS.AGENTS_SUSPEND]: ["lifecycle.canStop"],
198
+ [STATE_METHODS.AGENTS_RESUME]: ["lifecycle.canStop"],
199
+ [STATE_METHODS.AGENTS_STOP]: ["lifecycle.canStop"],
200
+ // Steering
201
+ [STEERING_METHODS.INJECT]: ["lifecycle.canSteer"],
202
+ // Scopes
203
+ [SCOPE_METHODS.SCOPES_CREATE]: ["scopes.canCreateScopes"],
204
+ [SCOPE_METHODS.SCOPES_DELETE]: ["scopes.canManageScopes"],
205
+ [SCOPE_METHODS.SCOPES_JOIN]: [],
206
+ [SCOPE_METHODS.SCOPES_LEAVE]: [],
207
+ // Session
208
+ [SESSION_METHODS.SESSION_LIST]: [],
209
+ [SESSION_METHODS.SESSION_LOAD]: [],
210
+ [SESSION_METHODS.SESSION_CLOSE]: [],
211
+ // Auth
212
+ [AUTH_METHODS.AUTH_REFRESH]: [],
213
+ // Permissions (system-only, no capability check - enforced by participant type)
214
+ [PERMISSION_METHODS.PERMISSIONS_UPDATE]: [],
215
+ // Federation
216
+ [FEDERATION_METHODS.FEDERATION_CONNECT]: ["federation.canFederate"],
217
+ [FEDERATION_METHODS.FEDERATION_ROUTE]: ["federation.canFederate"]
218
+ };
219
+ function isSuccessResponse(response) {
220
+ return "result" in response;
221
+ }
222
+ function isDirectAddress(address) {
223
+ return typeof address === "object" && "agent" in address && !("system" in address);
224
+ }
225
+ function isFederatedAddress(address) {
226
+ return typeof address === "object" && "system" in address && "agent" in address;
227
+ }
228
+ function isScopeAddress(address) {
229
+ return typeof address === "object" && "scope" in address;
230
+ }
231
+ function isBroadcastAddress(address) {
232
+ return typeof address === "object" && "broadcast" in address;
233
+ }
234
+ function isHierarchicalAddress(address) {
235
+ return typeof address === "object" && ("parent" in address || "children" in address || "ancestors" in address || "descendants" in address || "siblings" in address);
236
+ }
237
+
238
+ // src/jsonrpc/index.ts
239
+ function isRequest(message) {
240
+ return typeof message === "object" && message !== null && "jsonrpc" in message && message.jsonrpc === "2.0" && "id" in message && "method" in message;
241
+ }
242
+ function isNotification(message) {
243
+ return typeof message === "object" && message !== null && "jsonrpc" in message && message.jsonrpc === "2.0" && "method" in message && !("id" in message);
244
+ }
245
+ function isResponse(message) {
246
+ return typeof message === "object" && message !== null && "jsonrpc" in message && message.jsonrpc === "2.0" && "id" in message && !("method" in message);
247
+ }
248
+ function isErrorResponse(response) {
249
+ return "error" in response;
250
+ }
251
+ function createRequest(id, method, params) {
252
+ const request = {
253
+ jsonrpc: "2.0",
254
+ id,
255
+ method
256
+ };
257
+ if (params !== void 0) {
258
+ request.params = params;
259
+ }
260
+ return request;
261
+ }
262
+ function createNotification(method, params) {
263
+ const notification = {
264
+ jsonrpc: "2.0",
265
+ method
266
+ };
267
+ if (params !== void 0) {
268
+ notification.params = params;
269
+ }
270
+ return notification;
271
+ }
272
+ function createSuccessResponse(id, result) {
273
+ return {
274
+ jsonrpc: "2.0",
275
+ id,
276
+ result
277
+ };
278
+ }
279
+ function createErrorResponse(id, error) {
280
+ return {
281
+ jsonrpc: "2.0",
282
+ id,
283
+ error
284
+ };
285
+ }
286
+
287
+ // src/errors/index.ts
288
+ var MAPRequestError = class _MAPRequestError extends Error {
289
+ code;
290
+ data;
291
+ constructor(code, message, data) {
292
+ super(message);
293
+ this.name = "MAPRequestError";
294
+ this.code = code;
295
+ this.data = data;
296
+ }
297
+ /**
298
+ * Convert to MAP error object
299
+ */
300
+ toError() {
301
+ const error = {
302
+ code: this.code,
303
+ message: this.message
304
+ };
305
+ if (this.data) {
306
+ error.data = this.data;
307
+ }
308
+ return error;
309
+ }
310
+ /**
311
+ * Convert to JSON-RPC error response
312
+ */
313
+ toResponse(id) {
314
+ return createErrorResponse(id, this.toError());
315
+ }
316
+ // ==========================================================================
317
+ // Protocol Errors (-32xxx)
318
+ // ==========================================================================
319
+ static parseError(details) {
320
+ return new _MAPRequestError(
321
+ PROTOCOL_ERROR_CODES.PARSE_ERROR,
322
+ details ?? "Parse error",
323
+ { category: "protocol" }
324
+ );
325
+ }
326
+ static invalidRequest(details) {
327
+ return new _MAPRequestError(
328
+ PROTOCOL_ERROR_CODES.INVALID_REQUEST,
329
+ details ?? "Invalid request",
330
+ { category: "protocol" }
331
+ );
332
+ }
333
+ static methodNotFound(method) {
334
+ return new _MAPRequestError(
335
+ PROTOCOL_ERROR_CODES.METHOD_NOT_FOUND,
336
+ `Method not found: ${method}`,
337
+ { category: "protocol" }
338
+ );
339
+ }
340
+ static invalidParams(details) {
341
+ return new _MAPRequestError(
342
+ PROTOCOL_ERROR_CODES.INVALID_PARAMS,
343
+ "Invalid params",
344
+ { category: "protocol", details }
345
+ );
346
+ }
347
+ static internalError(details) {
348
+ return new _MAPRequestError(
349
+ PROTOCOL_ERROR_CODES.INTERNAL_ERROR,
350
+ details ?? "Internal error",
351
+ { category: "internal" }
352
+ );
353
+ }
354
+ // ==========================================================================
355
+ // Auth Errors (1xxx)
356
+ // ==========================================================================
357
+ static authRequired() {
358
+ return new _MAPRequestError(
359
+ AUTH_ERROR_CODES.AUTH_REQUIRED,
360
+ "Authentication required",
361
+ { category: "auth" }
362
+ );
363
+ }
364
+ static authFailed(details) {
365
+ return new _MAPRequestError(
366
+ AUTH_ERROR_CODES.AUTH_FAILED,
367
+ details ?? "Authentication failed",
368
+ { category: "auth" }
369
+ );
370
+ }
371
+ static tokenExpired() {
372
+ return new _MAPRequestError(
373
+ AUTH_ERROR_CODES.TOKEN_EXPIRED,
374
+ "Token expired",
375
+ { category: "auth", retryable: true }
376
+ );
377
+ }
378
+ static permissionDenied(required) {
379
+ return new _MAPRequestError(
380
+ AUTH_ERROR_CODES.PERMISSION_DENIED,
381
+ required ? `Permission denied: ${required}` : "Permission denied",
382
+ { category: "auth" }
383
+ );
384
+ }
385
+ // ==========================================================================
386
+ // Routing Errors (2xxx)
387
+ // ==========================================================================
388
+ static addressNotFound(address) {
389
+ return new _MAPRequestError(
390
+ ROUTING_ERROR_CODES.ADDRESS_NOT_FOUND,
391
+ `Address not found: ${address}`,
392
+ { category: "routing" }
393
+ );
394
+ }
395
+ static agentNotFound(agentId) {
396
+ return new _MAPRequestError(
397
+ ROUTING_ERROR_CODES.AGENT_NOT_FOUND,
398
+ `Agent not found: ${agentId}`,
399
+ { category: "routing" }
400
+ );
401
+ }
402
+ static scopeNotFound(scopeId) {
403
+ return new _MAPRequestError(
404
+ ROUTING_ERROR_CODES.SCOPE_NOT_FOUND,
405
+ `Scope not found: ${scopeId}`,
406
+ { category: "routing" }
407
+ );
408
+ }
409
+ static deliveryFailed(details) {
410
+ return new _MAPRequestError(
411
+ ROUTING_ERROR_CODES.DELIVERY_FAILED,
412
+ details ?? "Message delivery failed",
413
+ { category: "routing", retryable: true }
414
+ );
415
+ }
416
+ static addressAmbiguous(address) {
417
+ return new _MAPRequestError(
418
+ ROUTING_ERROR_CODES.ADDRESS_AMBIGUOUS,
419
+ `Address is ambiguous: ${address}`,
420
+ { category: "routing" }
421
+ );
422
+ }
423
+ // ==========================================================================
424
+ // Agent Errors (3xxx)
425
+ // ==========================================================================
426
+ static agentExists(agentId) {
427
+ return new _MAPRequestError(
428
+ AGENT_ERROR_CODES.AGENT_EXISTS,
429
+ `Agent already exists: ${agentId}`,
430
+ { category: "agent" }
431
+ );
432
+ }
433
+ static stateInvalid(currentState, requestedAction) {
434
+ return new _MAPRequestError(
435
+ AGENT_ERROR_CODES.STATE_INVALID,
436
+ `Cannot ${requestedAction} agent in state: ${currentState}`,
437
+ { category: "agent" }
438
+ );
439
+ }
440
+ static agentNotResponding(agentId) {
441
+ return new _MAPRequestError(
442
+ AGENT_ERROR_CODES.NOT_RESPONDING,
443
+ `Agent not responding: ${agentId}`,
444
+ { category: "agent", retryable: true }
445
+ );
446
+ }
447
+ static agentTerminated(agentId) {
448
+ return new _MAPRequestError(
449
+ AGENT_ERROR_CODES.TERMINATED,
450
+ `Agent terminated: ${agentId}`,
451
+ { category: "agent" }
452
+ );
453
+ }
454
+ static spawnFailed(details) {
455
+ return new _MAPRequestError(
456
+ AGENT_ERROR_CODES.SPAWN_FAILED,
457
+ details ?? "Failed to spawn agent",
458
+ { category: "agent" }
459
+ );
460
+ }
461
+ // ==========================================================================
462
+ // Resource Errors (4xxx)
463
+ // ==========================================================================
464
+ static resourceExhausted(resource) {
465
+ return new _MAPRequestError(
466
+ RESOURCE_ERROR_CODES.EXHAUSTED,
467
+ resource ? `Resource exhausted: ${resource}` : "Resource exhausted",
468
+ { category: "resource", retryable: true }
469
+ );
470
+ }
471
+ static rateLimited(retryAfterMs) {
472
+ return new _MAPRequestError(
473
+ RESOURCE_ERROR_CODES.RATE_LIMITED,
474
+ "Rate limited",
475
+ { category: "resource", retryable: true, retryAfterMs }
476
+ );
477
+ }
478
+ static quotaExceeded(quota) {
479
+ return new _MAPRequestError(
480
+ RESOURCE_ERROR_CODES.QUOTA_EXCEEDED,
481
+ quota ? `Quota exceeded: ${quota}` : "Quota exceeded",
482
+ { category: "resource" }
483
+ );
484
+ }
485
+ // ==========================================================================
486
+ // Federation Errors (5xxx)
487
+ // ==========================================================================
488
+ static federationUnavailable(systemId) {
489
+ return new _MAPRequestError(
490
+ FEDERATION_ERROR_CODES.FEDERATION_UNAVAILABLE,
491
+ systemId ? `Federation unavailable: ${systemId}` : "Federation unavailable",
492
+ { category: "federation", retryable: true }
493
+ );
494
+ }
495
+ static federationSystemNotFound(systemId) {
496
+ return new _MAPRequestError(
497
+ FEDERATION_ERROR_CODES.FEDERATION_SYSTEM_NOT_FOUND,
498
+ `System not found: ${systemId}`,
499
+ { category: "federation" }
500
+ );
501
+ }
502
+ static federationAuthFailed(systemId) {
503
+ return new _MAPRequestError(
504
+ FEDERATION_ERROR_CODES.FEDERATION_AUTH_FAILED,
505
+ `Federation authentication failed: ${systemId}`,
506
+ { category: "federation" }
507
+ );
508
+ }
509
+ static federationRouteRejected(systemId, reason) {
510
+ return new _MAPRequestError(
511
+ FEDERATION_ERROR_CODES.FEDERATION_ROUTE_REJECTED,
512
+ reason ? `Route rejected by ${systemId}: ${reason}` : `Route rejected by ${systemId}`,
513
+ { category: "federation" }
514
+ );
515
+ }
516
+ // ==========================================================================
517
+ // Utility
518
+ // ==========================================================================
519
+ /**
520
+ * Create from a MAP error object
521
+ */
522
+ static fromError(error) {
523
+ return new _MAPRequestError(error.code, error.message, error.data);
524
+ }
525
+ /**
526
+ * Check if this error is retryable
527
+ */
528
+ get retryable() {
529
+ return this.data?.retryable ?? false;
530
+ }
531
+ /**
532
+ * Get retry delay in milliseconds, if specified
533
+ */
534
+ get retryAfterMs() {
535
+ return this.data?.retryAfterMs;
536
+ }
537
+ /**
538
+ * Get error category
539
+ */
540
+ get category() {
541
+ return this.data?.category;
542
+ }
543
+ };
544
+ var MAPConnectionError = class _MAPConnectionError extends Error {
545
+ constructor(message) {
546
+ super(message);
547
+ this.name = "MAPConnectionError";
548
+ }
549
+ static closed() {
550
+ return new _MAPConnectionError("Connection closed");
551
+ }
552
+ static timeout() {
553
+ return new _MAPConnectionError("Connection timeout");
554
+ }
555
+ };
556
+ var MAPTimeoutError = class extends Error {
557
+ timeoutMs;
558
+ constructor(operation, timeoutMs) {
559
+ super(`Operation timed out after ${timeoutMs}ms: ${operation}`);
560
+ this.name = "MAPTimeoutError";
561
+ this.timeoutMs = timeoutMs;
562
+ }
563
+ };
564
+
565
+ // src/stream/index.ts
566
+ function ndJsonStream(readable, writable) {
567
+ const encoder = new TextEncoder();
568
+ const decoder = new TextDecoder();
569
+ let buffer = "";
570
+ const messageReadable = new ReadableStream({
571
+ async start(controller) {
572
+ const reader = readable.getReader();
573
+ try {
574
+ while (true) {
575
+ const { done, value } = await reader.read();
576
+ if (done) {
577
+ if (buffer.trim()) {
578
+ try {
579
+ const message = JSON.parse(buffer.trim());
580
+ controller.enqueue(message);
581
+ } catch {
582
+ console.error("MAP: Failed to parse final message:", buffer);
583
+ }
584
+ }
585
+ controller.close();
586
+ break;
587
+ }
588
+ buffer += decoder.decode(value, { stream: true });
589
+ const lines = buffer.split("\n");
590
+ buffer = lines.pop() ?? "";
591
+ for (const line of lines) {
592
+ const trimmed = line.trim();
593
+ if (trimmed) {
594
+ try {
595
+ const message = JSON.parse(trimmed);
596
+ controller.enqueue(message);
597
+ } catch {
598
+ console.error("MAP: Failed to parse message:", trimmed);
599
+ }
600
+ }
601
+ }
602
+ }
603
+ } catch (error) {
604
+ controller.error(error);
605
+ } finally {
606
+ reader.releaseLock();
607
+ }
608
+ }
609
+ });
610
+ const messageWritable = new WritableStream({
611
+ async write(message) {
612
+ const writer = writable.getWriter();
613
+ try {
614
+ const json = JSON.stringify(message) + "\n";
615
+ await writer.write(encoder.encode(json));
616
+ } finally {
617
+ writer.releaseLock();
618
+ }
619
+ },
620
+ async close() {
621
+ await writable.close();
622
+ },
623
+ abort(reason) {
624
+ writable.abort(reason);
625
+ }
626
+ });
627
+ return {
628
+ readable: messageReadable,
629
+ writable: messageWritable
630
+ };
631
+ }
632
+ function websocketStream(ws) {
633
+ const messageQueue = [];
634
+ let messageResolver = null;
635
+ let closed = false;
636
+ let closeError = null;
637
+ ws.addEventListener("message", (event) => {
638
+ try {
639
+ const message = JSON.parse(event.data);
640
+ if (messageResolver) {
641
+ messageResolver({ value: message, done: false });
642
+ messageResolver = null;
643
+ } else {
644
+ messageQueue.push(message);
645
+ }
646
+ } catch {
647
+ console.error("MAP: Failed to parse WebSocket message:", event.data);
648
+ }
649
+ });
650
+ ws.addEventListener("close", () => {
651
+ closed = true;
652
+ if (messageResolver) {
653
+ messageResolver({ value: void 0, done: true });
654
+ messageResolver = null;
655
+ }
656
+ });
657
+ ws.addEventListener("error", () => {
658
+ closeError = new Error("WebSocket error");
659
+ closed = true;
660
+ if (messageResolver) {
661
+ messageResolver({ value: void 0, done: true });
662
+ messageResolver = null;
663
+ }
664
+ });
665
+ const readable = new ReadableStream({
666
+ async pull(controller) {
667
+ if (messageQueue.length > 0) {
668
+ controller.enqueue(messageQueue.shift());
669
+ return;
670
+ }
671
+ if (closed) {
672
+ if (closeError) {
673
+ controller.error(closeError);
674
+ } else {
675
+ controller.close();
676
+ }
677
+ return;
678
+ }
679
+ await new Promise((resolve) => {
680
+ messageResolver = resolve;
681
+ }).then((result) => {
682
+ if (result.done) {
683
+ controller.close();
684
+ } else {
685
+ controller.enqueue(result.value);
686
+ }
687
+ });
688
+ }
689
+ });
690
+ const writable = new WritableStream({
691
+ async write(message) {
692
+ if (ws.readyState === WebSocket.CONNECTING) {
693
+ await new Promise((resolve, reject) => {
694
+ const onOpen = () => {
695
+ ws.removeEventListener("error", onError);
696
+ resolve();
697
+ };
698
+ const onError = () => {
699
+ ws.removeEventListener("open", onOpen);
700
+ reject(new Error("WebSocket failed to connect"));
701
+ };
702
+ ws.addEventListener("open", onOpen, { once: true });
703
+ ws.addEventListener("error", onError, { once: true });
704
+ });
705
+ }
706
+ if (ws.readyState !== WebSocket.OPEN) {
707
+ throw new Error("WebSocket is not open");
708
+ }
709
+ ws.send(JSON.stringify(message));
710
+ },
711
+ close() {
712
+ ws.close();
713
+ },
714
+ abort() {
715
+ ws.close();
716
+ }
717
+ });
718
+ return { readable, writable };
719
+ }
720
+ function createStreamPair() {
721
+ const clientToServer = [];
722
+ const serverToClient = [];
723
+ let clientToServerResolver = null;
724
+ let serverToClientResolver = null;
725
+ let clientToServerClosed = false;
726
+ let serverToClientClosed = false;
727
+ function createReadable(queue, _getResolver, setResolver, isClosed) {
728
+ return new ReadableStream({
729
+ async pull(controller) {
730
+ if (queue.length > 0) {
731
+ controller.enqueue(queue.shift());
732
+ return;
733
+ }
734
+ if (isClosed()) {
735
+ controller.close();
736
+ return;
737
+ }
738
+ const message = await new Promise((resolve) => {
739
+ setResolver((msg) => {
740
+ setResolver(null);
741
+ resolve(msg);
742
+ });
743
+ });
744
+ if (message === null) {
745
+ controller.close();
746
+ } else {
747
+ controller.enqueue(message);
748
+ }
749
+ }
750
+ });
751
+ }
752
+ function createWritable(queue, getResolver, setClosed) {
753
+ return new WritableStream({
754
+ write(message) {
755
+ const resolver = getResolver();
756
+ if (resolver) {
757
+ resolver(message);
758
+ } else {
759
+ queue.push(message);
760
+ }
761
+ },
762
+ close() {
763
+ setClosed();
764
+ const resolver = getResolver();
765
+ if (resolver) {
766
+ resolver(null);
767
+ }
768
+ }
769
+ });
770
+ }
771
+ const clientStream = {
772
+ // Client writes to server
773
+ writable: createWritable(
774
+ clientToServer,
775
+ () => clientToServerResolver,
776
+ () => {
777
+ clientToServerClosed = true;
778
+ }
779
+ ),
780
+ // Client reads from server
781
+ readable: createReadable(
782
+ serverToClient,
783
+ () => serverToClientResolver,
784
+ (r) => {
785
+ serverToClientResolver = r;
786
+ },
787
+ () => serverToClientClosed
788
+ )
789
+ };
790
+ const serverStream = {
791
+ // Server writes to client
792
+ writable: createWritable(
793
+ serverToClient,
794
+ () => serverToClientResolver,
795
+ () => {
796
+ serverToClientClosed = true;
797
+ }
798
+ ),
799
+ // Server reads from client
800
+ readable: createReadable(
801
+ clientToServer,
802
+ () => clientToServerResolver,
803
+ (r) => {
804
+ clientToServerResolver = r;
805
+ },
806
+ () => clientToServerClosed
807
+ )
808
+ };
809
+ return [clientStream, serverStream];
810
+ }
811
+
812
+ // src/subscription/index.ts
813
+ var Subscription = class {
814
+ id;
815
+ filter;
816
+ #eventHandlers = /* @__PURE__ */ new Set();
817
+ #overflowHandlers = /* @__PURE__ */ new Set();
818
+ #eventQueue = [];
819
+ #bufferSize;
820
+ #unsubscribe;
821
+ #sendAck;
822
+ // Deduplication tracking
823
+ #seenEventIds = /* @__PURE__ */ new Set();
824
+ #seenEventIdOrder = [];
825
+ // For LRU eviction
826
+ #maxSeenEventIds;
827
+ #eventResolver = null;
828
+ #pauseResolver = null;
829
+ #state = "active";
830
+ #lastSequenceNumber = -1;
831
+ #lastEventId;
832
+ #lastTimestamp;
833
+ // Overflow tracking
834
+ #totalDropped = 0;
835
+ #oldestDroppedId;
836
+ #newestDroppedId;
837
+ // Ack support
838
+ #serverSupportsAck = false;
839
+ constructor(id, unsubscribe, options = {}, sendAck) {
840
+ this.id = id;
841
+ this.filter = options.filter;
842
+ this.#bufferSize = options.bufferSize ?? 1e3;
843
+ this.#maxSeenEventIds = options.maxSeenEventIds ?? 1e4;
844
+ this.#unsubscribe = unsubscribe;
845
+ this.#sendAck = sendAck;
846
+ }
847
+ /**
848
+ * Current subscription state
849
+ */
850
+ get state() {
851
+ return this.#state;
852
+ }
853
+ /**
854
+ * Whether the subscription is closed
855
+ */
856
+ get isClosed() {
857
+ return this.#state === "closed";
858
+ }
859
+ /**
860
+ * Whether the subscription is paused
861
+ */
862
+ get isPaused() {
863
+ return this.#state === "paused";
864
+ }
865
+ /**
866
+ * Last received sequence number (for ordering verification)
867
+ */
868
+ get lastSequenceNumber() {
869
+ return this.#lastSequenceNumber;
870
+ }
871
+ /**
872
+ * Last received eventId (for replay positioning)
873
+ */
874
+ get lastEventId() {
875
+ return this.#lastEventId;
876
+ }
877
+ /**
878
+ * Last received server timestamp
879
+ */
880
+ get lastTimestamp() {
881
+ return this.#lastTimestamp;
882
+ }
883
+ /**
884
+ * Number of events currently buffered
885
+ */
886
+ get bufferedCount() {
887
+ return this.#eventQueue.length;
888
+ }
889
+ /**
890
+ * Number of eventIds being tracked for deduplication
891
+ */
892
+ get trackedEventIdCount() {
893
+ return this.#seenEventIds.size;
894
+ }
895
+ /**
896
+ * Total number of events dropped due to buffer overflow
897
+ */
898
+ get totalDropped() {
899
+ return this.#totalDropped;
900
+ }
901
+ /**
902
+ * Whether the server supports acknowledgments
903
+ */
904
+ get supportsAck() {
905
+ return this.#serverSupportsAck && !!this.#sendAck;
906
+ }
907
+ /**
908
+ * Pause event delivery from the async iterator.
909
+ * Events are still buffered but not yielded until resume() is called.
910
+ * Event handlers (on('event', ...)) still receive events while paused.
911
+ */
912
+ pause() {
913
+ if (this.#state === "closed") return;
914
+ this.#state = "paused";
915
+ }
916
+ /**
917
+ * Resume event delivery from the async iterator.
918
+ * Any events buffered during pause will be yielded.
919
+ */
920
+ resume() {
921
+ if (this.#state === "closed") return;
922
+ this.#state = "active";
923
+ if (this.#pauseResolver) {
924
+ this.#pauseResolver();
925
+ this.#pauseResolver = null;
926
+ }
927
+ if (this.#eventResolver && this.#eventQueue.length > 0) {
928
+ const event = this.#eventQueue.shift();
929
+ this.#eventResolver(event);
930
+ this.#eventResolver = null;
931
+ }
932
+ }
933
+ /**
934
+ * Acknowledge events up to a sequence number.
935
+ * No-op if server doesn't support acks.
936
+ *
937
+ * @param upToSequence - Acknowledge all events up to and including this sequence.
938
+ * If omitted, acknowledges up to lastSequenceNumber.
939
+ */
940
+ ack(upToSequence) {
941
+ if (!this.supportsAck) return;
942
+ const seq = upToSequence ?? this.#lastSequenceNumber;
943
+ if (seq < 0) return;
944
+ this.#sendAck({
945
+ subscriptionId: this.id,
946
+ upToSequence: seq
947
+ });
948
+ }
949
+ on(type, handler) {
950
+ if (type === "event") {
951
+ this.#eventHandlers.add(handler);
952
+ } else if (type === "overflow") {
953
+ this.#overflowHandlers.add(handler);
954
+ }
955
+ return this;
956
+ }
957
+ off(type, handler) {
958
+ if (type === "event") {
959
+ this.#eventHandlers.delete(handler);
960
+ } else if (type === "overflow") {
961
+ this.#overflowHandlers.delete(handler);
962
+ }
963
+ return this;
964
+ }
965
+ /**
966
+ * Register a one-time event handler
967
+ */
968
+ once(type, handler) {
969
+ if (type === "event") {
970
+ const wrapper = (event) => {
971
+ this.off("event", wrapper);
972
+ handler(event);
973
+ };
974
+ this.on("event", wrapper);
975
+ }
976
+ return this;
977
+ }
978
+ /**
979
+ * Unsubscribe and close the subscription
980
+ */
981
+ async unsubscribe() {
982
+ if (this.#state === "closed") return;
983
+ this.#state = "closed";
984
+ if (this.#eventResolver) {
985
+ this.#eventResolver(null);
986
+ this.#eventResolver = null;
987
+ }
988
+ if (this.#pauseResolver) {
989
+ this.#pauseResolver();
990
+ this.#pauseResolver = null;
991
+ }
992
+ this.#eventHandlers.clear();
993
+ this.#overflowHandlers.clear();
994
+ this.#seenEventIds.clear();
995
+ this.#seenEventIdOrder.length = 0;
996
+ await this.#unsubscribe();
997
+ }
998
+ /**
999
+ * Set whether server supports acknowledgments.
1000
+ * Called by connection after capability negotiation.
1001
+ * @internal
1002
+ */
1003
+ _setServerSupportsAck(supports) {
1004
+ this.#serverSupportsAck = supports;
1005
+ }
1006
+ /**
1007
+ * Push an event to the subscription (called by connection)
1008
+ * @internal
1009
+ */
1010
+ _pushEvent(params) {
1011
+ if (this.#state === "closed") return;
1012
+ const { sequenceNumber, eventId, timestamp, event } = params;
1013
+ if (eventId) {
1014
+ if (this.#seenEventIds.has(eventId)) {
1015
+ return;
1016
+ }
1017
+ this.#seenEventIds.add(eventId);
1018
+ this.#seenEventIdOrder.push(eventId);
1019
+ while (this.#seenEventIds.size > this.#maxSeenEventIds) {
1020
+ const oldestId = this.#seenEventIdOrder.shift();
1021
+ if (oldestId) {
1022
+ this.#seenEventIds.delete(oldestId);
1023
+ }
1024
+ }
1025
+ this.#lastEventId = eventId;
1026
+ }
1027
+ if (timestamp !== void 0) {
1028
+ this.#lastTimestamp = timestamp;
1029
+ }
1030
+ if (this.#lastSequenceNumber >= 0 && sequenceNumber !== this.#lastSequenceNumber + 1) {
1031
+ console.warn(
1032
+ `MAP: Subscription ${this.id} sequence gap: expected ${this.#lastSequenceNumber + 1}, got ${sequenceNumber}`
1033
+ );
1034
+ }
1035
+ this.#lastSequenceNumber = sequenceNumber;
1036
+ for (const handler of this.#eventHandlers) {
1037
+ try {
1038
+ handler(event);
1039
+ } catch (error) {
1040
+ console.error("MAP: Event handler error:", error);
1041
+ }
1042
+ }
1043
+ if (this.#eventResolver && this.#state === "active") {
1044
+ this.#eventResolver(event);
1045
+ this.#eventResolver = null;
1046
+ return;
1047
+ }
1048
+ if (this.#eventQueue.length < this.#bufferSize) {
1049
+ this.#eventQueue.push(event);
1050
+ } else {
1051
+ this.#totalDropped++;
1052
+ if (eventId) {
1053
+ if (this.#oldestDroppedId === void 0) {
1054
+ this.#oldestDroppedId = eventId;
1055
+ }
1056
+ this.#newestDroppedId = eventId;
1057
+ }
1058
+ const info = {
1059
+ eventsDropped: 1,
1060
+ oldestDroppedId: this.#oldestDroppedId,
1061
+ newestDroppedId: this.#newestDroppedId,
1062
+ timestamp: Date.now(),
1063
+ totalDropped: this.#totalDropped
1064
+ };
1065
+ for (const handler of this.#overflowHandlers) {
1066
+ try {
1067
+ handler(info);
1068
+ } catch (error) {
1069
+ console.error("MAP: Overflow handler error:", error);
1070
+ }
1071
+ }
1072
+ console.warn(`MAP: Subscription ${this.id} buffer full, dropping event`);
1073
+ }
1074
+ }
1075
+ /**
1076
+ * Mark the subscription as closed (called by connection)
1077
+ * @internal
1078
+ */
1079
+ _close() {
1080
+ this.#state = "closed";
1081
+ if (this.#eventResolver) {
1082
+ this.#eventResolver(null);
1083
+ this.#eventResolver = null;
1084
+ }
1085
+ if (this.#pauseResolver) {
1086
+ this.#pauseResolver();
1087
+ this.#pauseResolver = null;
1088
+ }
1089
+ }
1090
+ /**
1091
+ * Async iterator implementation
1092
+ */
1093
+ async *[Symbol.asyncIterator]() {
1094
+ while (!this.isClosed) {
1095
+ while (this.isPaused) {
1096
+ await new Promise((resolve) => {
1097
+ this.#pauseResolver = resolve;
1098
+ });
1099
+ if (this.isClosed) {
1100
+ while (this.#eventQueue.length > 0) {
1101
+ yield this.#eventQueue.shift();
1102
+ }
1103
+ return;
1104
+ }
1105
+ }
1106
+ if (this.#eventQueue.length > 0) {
1107
+ yield this.#eventQueue.shift();
1108
+ continue;
1109
+ }
1110
+ const event = await new Promise((resolve) => {
1111
+ this.#eventResolver = resolve;
1112
+ });
1113
+ if (event === null) {
1114
+ break;
1115
+ }
1116
+ yield event;
1117
+ }
1118
+ while (this.#eventQueue.length > 0) {
1119
+ yield this.#eventQueue.shift();
1120
+ }
1121
+ }
1122
+ };
1123
+ function createSubscription(id, unsubscribe, options, sendAck) {
1124
+ return new Subscription(id, unsubscribe, options, sendAck);
1125
+ }
1126
+
1127
+ // src/connection/base.ts
1128
+ var BaseConnection = class {
1129
+ #stream;
1130
+ #pendingResponses = /* @__PURE__ */ new Map();
1131
+ #abortController = new AbortController();
1132
+ #closedPromise;
1133
+ #defaultTimeout;
1134
+ #stateChangeHandlers = /* @__PURE__ */ new Set();
1135
+ #nextRequestId = 1;
1136
+ #writeQueue = Promise.resolve();
1137
+ #requestHandler = null;
1138
+ #notificationHandler = null;
1139
+ #closed = false;
1140
+ #closeResolver;
1141
+ #state = "initial";
1142
+ constructor(stream, options = {}) {
1143
+ this.#stream = stream;
1144
+ this.#defaultTimeout = options.defaultTimeout ?? 3e4;
1145
+ this.#closedPromise = new Promise((resolve) => {
1146
+ this.#closeResolver = resolve;
1147
+ });
1148
+ void this.#startReceiving();
1149
+ }
1150
+ /**
1151
+ * AbortSignal that triggers when the connection closes.
1152
+ * Useful for cancelling operations tied to this connection.
1153
+ */
1154
+ get signal() {
1155
+ return this.#abortController.signal;
1156
+ }
1157
+ /**
1158
+ * Promise that resolves when the connection is closed.
1159
+ */
1160
+ get closed() {
1161
+ return this.#closedPromise;
1162
+ }
1163
+ /**
1164
+ * Whether the connection is closed
1165
+ */
1166
+ get isClosed() {
1167
+ return this.#closed;
1168
+ }
1169
+ /**
1170
+ * Set the handler for incoming requests
1171
+ */
1172
+ setRequestHandler(handler) {
1173
+ this.#requestHandler = handler;
1174
+ }
1175
+ /**
1176
+ * Set the handler for incoming notifications
1177
+ */
1178
+ setNotificationHandler(handler) {
1179
+ this.#notificationHandler = handler;
1180
+ }
1181
+ /**
1182
+ * Current connection state
1183
+ */
1184
+ get state() {
1185
+ return this.#state;
1186
+ }
1187
+ /**
1188
+ * Register a handler for state changes.
1189
+ *
1190
+ * @param handler - Function called when state changes
1191
+ * @returns Unsubscribe function to remove the handler
1192
+ */
1193
+ onStateChange(handler) {
1194
+ this.#stateChangeHandlers.add(handler);
1195
+ return () => this.#stateChangeHandlers.delete(handler);
1196
+ }
1197
+ /**
1198
+ * Transition to a new state and notify handlers.
1199
+ * @internal
1200
+ */
1201
+ _transitionTo(newState) {
1202
+ if (this.#state === newState) return;
1203
+ const oldState = this.#state;
1204
+ this.#state = newState;
1205
+ for (const handler of this.#stateChangeHandlers) {
1206
+ try {
1207
+ handler(newState, oldState);
1208
+ } catch (error) {
1209
+ console.error("MAP: State change handler error:", error);
1210
+ }
1211
+ }
1212
+ }
1213
+ /**
1214
+ * Reconnect with a new stream.
1215
+ *
1216
+ * This method is used by role-specific connections to replace the
1217
+ * underlying transport after a disconnect.
1218
+ *
1219
+ * @param newStream - The new stream to use
1220
+ * @throws If the connection is permanently closed
1221
+ */
1222
+ async reconnect(newStream) {
1223
+ if (this.#state === "closed") {
1224
+ throw new Error("Cannot reconnect a permanently closed connection");
1225
+ }
1226
+ this.#stream = newStream;
1227
+ this.#closed = false;
1228
+ this.#writeQueue = Promise.resolve();
1229
+ void this.#startReceiving();
1230
+ this._transitionTo("connected");
1231
+ }
1232
+ /**
1233
+ * Send a request and wait for response
1234
+ */
1235
+ async sendRequest(method, params, options) {
1236
+ if (this.#closed) {
1237
+ throw MAPConnectionError.closed();
1238
+ }
1239
+ const id = this.#nextRequestId++;
1240
+ const request = createRequest(id, method, params);
1241
+ const responsePromise = new Promise((resolve, reject) => {
1242
+ const pending = { resolve, reject };
1243
+ const timeout = options?.timeout ?? this.#defaultTimeout;
1244
+ if (timeout > 0) {
1245
+ pending.timeoutId = setTimeout(() => {
1246
+ this.#pendingResponses.delete(id);
1247
+ reject(new MAPTimeoutError(method, timeout));
1248
+ }, timeout);
1249
+ }
1250
+ this.#pendingResponses.set(id, pending);
1251
+ });
1252
+ await this.#sendMessage(request);
1253
+ return responsePromise;
1254
+ }
1255
+ /**
1256
+ * Send a notification (no response expected)
1257
+ */
1258
+ async sendNotification(method, params) {
1259
+ if (this.#closed) {
1260
+ throw MAPConnectionError.closed();
1261
+ }
1262
+ const notification = createNotification(method, params);
1263
+ await this.#sendMessage(notification);
1264
+ }
1265
+ /**
1266
+ * Send a response to a request
1267
+ */
1268
+ async sendResponse(id, result) {
1269
+ if (this.#closed) {
1270
+ throw MAPConnectionError.closed();
1271
+ }
1272
+ const response = createSuccessResponse(id, result);
1273
+ await this.#sendMessage(response);
1274
+ }
1275
+ /**
1276
+ * Send an error response to a request
1277
+ */
1278
+ async sendErrorResponse(id, error) {
1279
+ if (this.#closed) {
1280
+ throw MAPConnectionError.closed();
1281
+ }
1282
+ const response = createErrorResponse(id, error);
1283
+ await this.#sendMessage(response);
1284
+ }
1285
+ /**
1286
+ * Close the connection
1287
+ */
1288
+ async close() {
1289
+ if (this.#closed) return;
1290
+ this.#closed = true;
1291
+ this._transitionTo("closed");
1292
+ this.#abortController.abort();
1293
+ for (const [, pending] of this.#pendingResponses) {
1294
+ if (pending.timeoutId) {
1295
+ clearTimeout(pending.timeoutId);
1296
+ }
1297
+ pending.reject(MAPConnectionError.closed());
1298
+ }
1299
+ this.#pendingResponses.clear();
1300
+ try {
1301
+ const writer = this.#stream.writable.getWriter();
1302
+ await writer.close();
1303
+ writer.releaseLock();
1304
+ } catch {
1305
+ }
1306
+ this.#closeResolver();
1307
+ }
1308
+ /**
1309
+ * Start receiving messages from the stream
1310
+ */
1311
+ async #startReceiving() {
1312
+ const reader = this.#stream.readable.getReader();
1313
+ try {
1314
+ while (!this.#closed) {
1315
+ const { done, value } = await reader.read();
1316
+ if (done) {
1317
+ break;
1318
+ }
1319
+ await this.#handleMessage(value);
1320
+ }
1321
+ } catch (error) {
1322
+ if (!this.#closed) {
1323
+ console.error("MAP: Error receiving message:", error);
1324
+ }
1325
+ } finally {
1326
+ reader.releaseLock();
1327
+ await this.close();
1328
+ }
1329
+ }
1330
+ /**
1331
+ * Handle an incoming message
1332
+ */
1333
+ async #handleMessage(message) {
1334
+ try {
1335
+ if (isRequest(message)) {
1336
+ await this.#handleRequest(message);
1337
+ } else if (isNotification(message)) {
1338
+ await this.#handleNotification(message);
1339
+ } else if (isResponse(message)) {
1340
+ this.#handleResponse(message);
1341
+ } else {
1342
+ console.error("MAP: Unknown message type:", message);
1343
+ }
1344
+ } catch (error) {
1345
+ console.error("MAP: Error handling message:", error);
1346
+ }
1347
+ }
1348
+ /**
1349
+ * Handle an incoming request
1350
+ */
1351
+ async #handleRequest(request) {
1352
+ const { id, method, params } = request;
1353
+ if (!this.#requestHandler) {
1354
+ await this.sendErrorResponse(
1355
+ id,
1356
+ MAPRequestError.methodNotFound(method).toError()
1357
+ );
1358
+ return;
1359
+ }
1360
+ try {
1361
+ const result = await this.#requestHandler(method, params);
1362
+ await this.sendResponse(id, result ?? null);
1363
+ } catch (error) {
1364
+ if (error instanceof MAPRequestError) {
1365
+ await this.sendErrorResponse(id, error.toError());
1366
+ } else {
1367
+ const message = error instanceof Error ? error.message : "Unknown error";
1368
+ await this.sendErrorResponse(
1369
+ id,
1370
+ MAPRequestError.internalError(message).toError()
1371
+ );
1372
+ }
1373
+ }
1374
+ }
1375
+ /**
1376
+ * Handle an incoming notification
1377
+ */
1378
+ async #handleNotification(notification) {
1379
+ const { method, params } = notification;
1380
+ if (!this.#notificationHandler) {
1381
+ console.warn("MAP: No notification handler for:", method);
1382
+ return;
1383
+ }
1384
+ try {
1385
+ await this.#notificationHandler(method, params);
1386
+ } catch (error) {
1387
+ console.error("MAP: Error handling notification:", method, error);
1388
+ }
1389
+ }
1390
+ /**
1391
+ * Handle an incoming response
1392
+ */
1393
+ #handleResponse(response) {
1394
+ const { id } = response;
1395
+ const pending = this.#pendingResponses.get(id);
1396
+ if (!pending) {
1397
+ console.warn("MAP: Received response for unknown request:", id);
1398
+ return;
1399
+ }
1400
+ this.#pendingResponses.delete(id);
1401
+ if (pending.timeoutId) {
1402
+ clearTimeout(pending.timeoutId);
1403
+ }
1404
+ if (isErrorResponse(response)) {
1405
+ pending.reject(MAPRequestError.fromError(response.error));
1406
+ } else {
1407
+ pending.resolve(response.result);
1408
+ }
1409
+ }
1410
+ /**
1411
+ * Send a message through the stream with write queue serialization
1412
+ */
1413
+ async #sendMessage(message) {
1414
+ this.#writeQueue = this.#writeQueue.then(async () => {
1415
+ if (this.#closed) return;
1416
+ const writer = this.#stream.writable.getWriter();
1417
+ try {
1418
+ await writer.write(message);
1419
+ } finally {
1420
+ writer.releaseLock();
1421
+ }
1422
+ }).catch((error) => {
1423
+ console.error("MAP: Write error:", error);
1424
+ });
1425
+ return this.#writeQueue;
1426
+ }
1427
+ };
1428
+ var ENCODING = "0123456789ABCDEFGHJKMNPQRSTVWXYZ";
1429
+ var ENCODING_LEN = ENCODING.length;
1430
+ function ulidTimestamp(id) {
1431
+ if (id.length !== 26) {
1432
+ throw new Error(`Invalid ULID: expected 26 characters, got ${id.length}`);
1433
+ }
1434
+ let time = 0;
1435
+ for (let i = 0; i < 10; i++) {
1436
+ const char = id[i].toUpperCase();
1437
+ const idx = ENCODING.indexOf(char);
1438
+ if (idx === -1) {
1439
+ throw new Error(`Invalid ULID character: ${char}`);
1440
+ }
1441
+ time = time * ENCODING_LEN + idx;
1442
+ }
1443
+ return time;
1444
+ }
1445
+ function compareUlid(a, b) {
1446
+ return a.localeCompare(b);
1447
+ }
1448
+ function isValidUlid(id) {
1449
+ if (typeof id !== "string" || id.length !== 26) {
1450
+ return false;
1451
+ }
1452
+ for (let i = 0; i < 26; i++) {
1453
+ if (ENCODING.indexOf(id[i].toUpperCase()) === -1) {
1454
+ return false;
1455
+ }
1456
+ }
1457
+ return true;
1458
+ }
1459
+
1460
+ // src/utils/retry.ts
1461
+ var DEFAULT_RETRY_POLICY = {
1462
+ maxRetries: 10,
1463
+ baseDelayMs: 1e3,
1464
+ maxDelayMs: 3e4,
1465
+ jitter: true
1466
+ };
1467
+ function calculateDelay(attempt, policy) {
1468
+ let delay = Math.min(
1469
+ policy.baseDelayMs * Math.pow(2, attempt - 1),
1470
+ policy.maxDelayMs
1471
+ );
1472
+ if (policy.jitter) {
1473
+ delay = delay * (0.5 + Math.random());
1474
+ }
1475
+ return Math.floor(delay);
1476
+ }
1477
+ async function withRetry(operation, policy = DEFAULT_RETRY_POLICY, callbacks) {
1478
+ let lastError;
1479
+ for (let attempt = 1; attempt <= policy.maxRetries + 1; attempt++) {
1480
+ try {
1481
+ const result = await operation();
1482
+ callbacks?.onSuccess?.(result, attempt);
1483
+ return result;
1484
+ } catch (error) {
1485
+ lastError = error;
1486
+ const isRetryable = policy.isRetryable?.(lastError) ?? true;
1487
+ const hasMoreAttempts = attempt <= policy.maxRetries;
1488
+ if (!isRetryable || !hasMoreAttempts) {
1489
+ break;
1490
+ }
1491
+ const delay = calculateDelay(attempt, policy);
1492
+ callbacks?.onRetry?.({
1493
+ attempt,
1494
+ nextDelayMs: delay,
1495
+ lastError
1496
+ });
1497
+ await sleep(delay);
1498
+ }
1499
+ }
1500
+ callbacks?.onFailure?.(lastError, policy.maxRetries + 1);
1501
+ throw lastError;
1502
+ }
1503
+ function retryable(fn, policy = DEFAULT_RETRY_POLICY) {
1504
+ return (...args) => withRetry(() => fn(...args), policy);
1505
+ }
1506
+ function createRetryPolicy(options = {}) {
1507
+ return { ...DEFAULT_RETRY_POLICY, ...options };
1508
+ }
1509
+ function sleep(ms) {
1510
+ return new Promise((resolve) => setTimeout(resolve, ms));
1511
+ }
1512
+
1513
+ // src/utils/causal-buffer.ts
1514
+ var CausalEventBuffer = class {
1515
+ #options;
1516
+ /** Events seen (by eventId) - used to check if predecessors exist */
1517
+ #seen = /* @__PURE__ */ new Set();
1518
+ /** Events waiting for predecessors */
1519
+ #pending = /* @__PURE__ */ new Map();
1520
+ /** Map from eventId to events waiting for it */
1521
+ #waitingFor = /* @__PURE__ */ new Map();
1522
+ constructor(options = {}) {
1523
+ this.#options = {
1524
+ maxWaitTime: options.maxWaitTime ?? 5e3,
1525
+ maxBufferSize: options.maxBufferSize ?? 1e3,
1526
+ onForcedRelease: options.onForcedRelease
1527
+ };
1528
+ }
1529
+ /**
1530
+ * Push an event into the buffer.
1531
+ *
1532
+ * @param event - The event to buffer
1533
+ * @returns Events that are ready to be processed (in causal order)
1534
+ */
1535
+ push(event) {
1536
+ const ready = [];
1537
+ if (this.#seen.has(event.eventId)) {
1538
+ return { ready, pending: this.#pending.size };
1539
+ }
1540
+ this.#seen.add(event.eventId);
1541
+ if (!event.receivedAt) {
1542
+ event = { ...event, receivedAt: Date.now() };
1543
+ }
1544
+ const missingPredecessors = this.#getMissingPredecessors(event);
1545
+ if (missingPredecessors.length === 0) {
1546
+ ready.push(event);
1547
+ this.#releaseWaiting(event.eventId, ready);
1548
+ } else {
1549
+ this.#pending.set(event.eventId, event);
1550
+ for (const predecessorId of missingPredecessors) {
1551
+ if (!this.#waitingFor.has(predecessorId)) {
1552
+ this.#waitingFor.set(predecessorId, /* @__PURE__ */ new Set());
1553
+ }
1554
+ this.#waitingFor.get(predecessorId).add(event.eventId);
1555
+ }
1556
+ }
1557
+ this.#handleBufferOverflow(ready);
1558
+ this.#handleTimeouts(ready);
1559
+ return { ready, pending: this.#pending.size };
1560
+ }
1561
+ /**
1562
+ * Get the number of events waiting for predecessors
1563
+ */
1564
+ get pendingCount() {
1565
+ return this.#pending.size;
1566
+ }
1567
+ /**
1568
+ * Get the number of unique events seen
1569
+ */
1570
+ get seenCount() {
1571
+ return this.#seen.size;
1572
+ }
1573
+ /**
1574
+ * Check if a specific event has been seen
1575
+ */
1576
+ hasSeen(eventId) {
1577
+ return this.#seen.has(eventId);
1578
+ }
1579
+ /**
1580
+ * Force release all pending events, regardless of missing predecessors.
1581
+ * Useful for cleanup or when you know no more events are coming.
1582
+ *
1583
+ * @returns All pending events in the order they would be released
1584
+ */
1585
+ flush() {
1586
+ const ready = [];
1587
+ const pendingList = Array.from(this.#pending.values()).sort(
1588
+ (a, b) => (a.receivedAt ?? 0) - (b.receivedAt ?? 0)
1589
+ );
1590
+ for (const event of pendingList) {
1591
+ const missingPredecessors = this.#getMissingPredecessors(event);
1592
+ if (missingPredecessors.length > 0) {
1593
+ this.#options.onForcedRelease?.(event, missingPredecessors);
1594
+ }
1595
+ ready.push(event);
1596
+ }
1597
+ this.#pending.clear();
1598
+ this.#waitingFor.clear();
1599
+ return ready;
1600
+ }
1601
+ /**
1602
+ * Clear all state (seen events, pending events)
1603
+ */
1604
+ clear() {
1605
+ this.#seen.clear();
1606
+ this.#pending.clear();
1607
+ this.#waitingFor.clear();
1608
+ }
1609
+ /**
1610
+ * Get missing predecessors for an event.
1611
+ * A predecessor is considered "missing" if it hasn't been released yet
1612
+ * (either not seen at all, or seen but still pending).
1613
+ */
1614
+ #getMissingPredecessors(event) {
1615
+ if (!event.causedBy || event.causedBy.length === 0) {
1616
+ return [];
1617
+ }
1618
+ return event.causedBy.filter((predecessorId) => {
1619
+ return !this.#seen.has(predecessorId) || this.#pending.has(predecessorId);
1620
+ });
1621
+ }
1622
+ /**
1623
+ * Release events that were waiting for a specific predecessor
1624
+ */
1625
+ #releaseWaiting(predecessorId, ready) {
1626
+ const waitingEventIds = this.#waitingFor.get(predecessorId);
1627
+ if (!waitingEventIds) return;
1628
+ this.#waitingFor.delete(predecessorId);
1629
+ for (const waitingEventId of waitingEventIds) {
1630
+ const waitingEvent = this.#pending.get(waitingEventId);
1631
+ if (!waitingEvent) continue;
1632
+ const stillMissing = this.#getMissingPredecessors(waitingEvent);
1633
+ if (stillMissing.length === 0) {
1634
+ this.#pending.delete(waitingEventId);
1635
+ ready.push(waitingEvent);
1636
+ this.#releaseWaiting(waitingEventId, ready);
1637
+ }
1638
+ }
1639
+ }
1640
+ /**
1641
+ * Handle buffer overflow by force-releasing oldest events
1642
+ */
1643
+ #handleBufferOverflow(ready) {
1644
+ while (this.#pending.size > this.#options.maxBufferSize) {
1645
+ let oldest = null;
1646
+ for (const event of this.#pending.values()) {
1647
+ if (!oldest || (event.receivedAt ?? 0) < (oldest.receivedAt ?? 0)) {
1648
+ oldest = event;
1649
+ }
1650
+ }
1651
+ if (oldest) {
1652
+ this.#forceRelease(oldest, ready);
1653
+ }
1654
+ }
1655
+ }
1656
+ /**
1657
+ * Handle events that have been waiting too long
1658
+ */
1659
+ #handleTimeouts(ready) {
1660
+ if (this.#options.maxWaitTime <= 0 || this.#options.maxWaitTime === Infinity) {
1661
+ return;
1662
+ }
1663
+ const now = Date.now();
1664
+ const toRelease = [];
1665
+ for (const event of this.#pending.values()) {
1666
+ const waitTime = now - (event.receivedAt ?? now);
1667
+ if (waitTime >= this.#options.maxWaitTime) {
1668
+ toRelease.push(event);
1669
+ }
1670
+ }
1671
+ for (const event of toRelease) {
1672
+ this.#forceRelease(event, ready);
1673
+ }
1674
+ }
1675
+ /**
1676
+ * Force release an event despite missing predecessors
1677
+ */
1678
+ #forceRelease(event, ready) {
1679
+ const missingPredecessors = this.#getMissingPredecessors(event);
1680
+ this.#pending.delete(event.eventId);
1681
+ for (const predecessorId of event.causedBy ?? []) {
1682
+ const waiting = this.#waitingFor.get(predecessorId);
1683
+ if (waiting) {
1684
+ waiting.delete(event.eventId);
1685
+ if (waiting.size === 0) {
1686
+ this.#waitingFor.delete(predecessorId);
1687
+ }
1688
+ }
1689
+ }
1690
+ if (missingPredecessors.length > 0) {
1691
+ this.#options.onForcedRelease?.(event, missingPredecessors);
1692
+ }
1693
+ ready.push(event);
1694
+ this.#releaseWaiting(event.eventId, ready);
1695
+ }
1696
+ };
1697
+ function validateCausalOrder(events) {
1698
+ const seen = /* @__PURE__ */ new Set();
1699
+ for (const event of events) {
1700
+ if (event.causedBy) {
1701
+ for (const predecessorId of event.causedBy) {
1702
+ if (!seen.has(predecessorId)) {
1703
+ return false;
1704
+ }
1705
+ }
1706
+ }
1707
+ seen.add(event.eventId);
1708
+ }
1709
+ return true;
1710
+ }
1711
+ function sortCausalOrder(events) {
1712
+ const eventMap = /* @__PURE__ */ new Map();
1713
+ for (const event of events) {
1714
+ eventMap.set(event.eventId, event);
1715
+ }
1716
+ const result = [];
1717
+ const visited = /* @__PURE__ */ new Set();
1718
+ const visiting = /* @__PURE__ */ new Set();
1719
+ function visit(eventId) {
1720
+ if (visited.has(eventId)) return;
1721
+ if (visiting.has(eventId)) {
1722
+ throw new Error(`Cycle detected involving event: ${eventId}`);
1723
+ }
1724
+ const event = eventMap.get(eventId);
1725
+ if (!event) {
1726
+ throw new Error(`Missing event: ${eventId}`);
1727
+ }
1728
+ visiting.add(eventId);
1729
+ if (event.causedBy) {
1730
+ for (const predecessorId of event.causedBy) {
1731
+ if (!eventMap.has(predecessorId)) {
1732
+ throw new Error(`Missing predecessor: ${predecessorId} for event: ${eventId}`);
1733
+ }
1734
+ visit(predecessorId);
1735
+ }
1736
+ }
1737
+ visiting.delete(eventId);
1738
+ visited.add(eventId);
1739
+ result.push(event);
1740
+ }
1741
+ for (const event of events) {
1742
+ visit(event.eventId);
1743
+ }
1744
+ return result;
1745
+ }
1746
+
1747
+ // src/connection/client.ts
1748
+ var ClientConnection = class {
1749
+ #connection;
1750
+ #subscriptions = /* @__PURE__ */ new Map();
1751
+ #subscriptionStates = /* @__PURE__ */ new Map();
1752
+ #reconnectionHandlers = /* @__PURE__ */ new Set();
1753
+ #options;
1754
+ #sessionId = null;
1755
+ #serverCapabilities = null;
1756
+ #connected = false;
1757
+ #lastConnectOptions;
1758
+ #isReconnecting = false;
1759
+ constructor(stream, options = {}) {
1760
+ this.#connection = new BaseConnection(stream, options);
1761
+ this.#options = options;
1762
+ this.#connection.setNotificationHandler(this.#handleNotification.bind(this));
1763
+ if (options.reconnection?.enabled && options.createStream) {
1764
+ this.#connection.onStateChange((newState) => {
1765
+ if (newState === "closed" && this.#connected && !this.#isReconnecting) {
1766
+ void this.#handleDisconnect();
1767
+ }
1768
+ });
1769
+ }
1770
+ }
1771
+ // ===========================================================================
1772
+ // Connection Lifecycle
1773
+ // ===========================================================================
1774
+ /**
1775
+ * Connect to the MAP system
1776
+ */
1777
+ async connect(options) {
1778
+ const params = {
1779
+ protocolVersion: PROTOCOL_VERSION,
1780
+ participantType: "client",
1781
+ name: this.#options.name,
1782
+ capabilities: this.#options.capabilities,
1783
+ sessionId: options?.sessionId,
1784
+ auth: options?.auth
1785
+ };
1786
+ const result = await this.#connection.sendRequest(CORE_METHODS.CONNECT, params);
1787
+ this.#sessionId = result.sessionId;
1788
+ this.#serverCapabilities = result.capabilities;
1789
+ this.#connected = true;
1790
+ this.#connection._transitionTo("connected");
1791
+ this.#lastConnectOptions = options;
1792
+ return result;
1793
+ }
1794
+ /**
1795
+ * Disconnect from the MAP system
1796
+ */
1797
+ async disconnect(reason) {
1798
+ if (!this.#connected) return;
1799
+ try {
1800
+ await this.#connection.sendRequest(
1801
+ CORE_METHODS.DISCONNECT,
1802
+ reason ? { reason } : void 0
1803
+ );
1804
+ } finally {
1805
+ for (const subscription of this.#subscriptions.values()) {
1806
+ subscription._close();
1807
+ }
1808
+ this.#subscriptions.clear();
1809
+ await this.#connection.close();
1810
+ this.#connected = false;
1811
+ }
1812
+ }
1813
+ /**
1814
+ * Whether the client is connected
1815
+ */
1816
+ get isConnected() {
1817
+ return this.#connected && !this.#connection.isClosed;
1818
+ }
1819
+ /**
1820
+ * Current session ID
1821
+ */
1822
+ get sessionId() {
1823
+ return this.#sessionId;
1824
+ }
1825
+ /**
1826
+ * Server capabilities
1827
+ */
1828
+ get serverCapabilities() {
1829
+ return this.#serverCapabilities;
1830
+ }
1831
+ /**
1832
+ * AbortSignal that triggers when the connection closes
1833
+ */
1834
+ get signal() {
1835
+ return this.#connection.signal;
1836
+ }
1837
+ /**
1838
+ * Promise that resolves when the connection closes
1839
+ */
1840
+ get closed() {
1841
+ return this.#connection.closed;
1842
+ }
1843
+ // ===========================================================================
1844
+ // Session Management
1845
+ // ===========================================================================
1846
+ /**
1847
+ * List available sessions
1848
+ */
1849
+ async listSessions() {
1850
+ return this.#connection.sendRequest(SESSION_METHODS.SESSION_LIST);
1851
+ }
1852
+ /**
1853
+ * Load an existing session
1854
+ */
1855
+ async loadSession(sessionId) {
1856
+ return this.#connection.sendRequest(SESSION_METHODS.SESSION_LOAD, { sessionId });
1857
+ }
1858
+ /**
1859
+ * Close the current session
1860
+ */
1861
+ async closeSession(sessionId) {
1862
+ return this.#connection.sendRequest(SESSION_METHODS.SESSION_CLOSE, { sessionId });
1863
+ }
1864
+ // ===========================================================================
1865
+ // Agent Queries
1866
+ // ===========================================================================
1867
+ /**
1868
+ * List agents with optional filters
1869
+ */
1870
+ async listAgents(options) {
1871
+ return this.#connection.sendRequest(OBSERVATION_METHODS.AGENTS_LIST, options);
1872
+ }
1873
+ /**
1874
+ * Get a single agent by ID
1875
+ */
1876
+ async getAgent(agentId, options) {
1877
+ const params = { agentId, ...options };
1878
+ return this.#connection.sendRequest(
1879
+ OBSERVATION_METHODS.AGENTS_GET,
1880
+ params
1881
+ );
1882
+ }
1883
+ /**
1884
+ * Get the agent structure/hierarchy graph
1885
+ */
1886
+ async getStructureGraph(options) {
1887
+ return this.#connection.sendRequest(OBSERVATION_METHODS.STRUCTURE_GRAPH, options);
1888
+ }
1889
+ // ===========================================================================
1890
+ // Scope Queries
1891
+ // ===========================================================================
1892
+ /**
1893
+ * List scopes
1894
+ */
1895
+ async listScopes(options) {
1896
+ return this.#connection.sendRequest(OBSERVATION_METHODS.SCOPES_LIST, options);
1897
+ }
1898
+ /**
1899
+ * Get a single scope by ID
1900
+ */
1901
+ async getScope(scopeId) {
1902
+ const result = await this.#connection.sendRequest(OBSERVATION_METHODS.SCOPES_GET, { scopeId });
1903
+ return result.scope;
1904
+ }
1905
+ /**
1906
+ * List members of a scope
1907
+ */
1908
+ async getScopeMembers(scopeId, options) {
1909
+ return this.#connection.sendRequest(OBSERVATION_METHODS.SCOPES_MEMBERS, {
1910
+ scopeId,
1911
+ ...options
1912
+ });
1913
+ }
1914
+ // ===========================================================================
1915
+ // Messaging
1916
+ // ===========================================================================
1917
+ /**
1918
+ * Send a message to an address
1919
+ */
1920
+ async send(to, payload, meta) {
1921
+ const params = { to };
1922
+ if (payload !== void 0) params.payload = payload;
1923
+ if (meta) params.meta = meta;
1924
+ return this.#connection.sendRequest(CORE_METHODS.SEND, params);
1925
+ }
1926
+ /**
1927
+ * Send a message to a specific agent
1928
+ */
1929
+ async sendToAgent(agentId, payload, meta) {
1930
+ return this.send({ agent: agentId }, payload, meta);
1931
+ }
1932
+ /**
1933
+ * Send a message to all agents in a scope
1934
+ */
1935
+ async sendToScope(scopeId, payload, meta) {
1936
+ return this.send({ scope: scopeId }, payload, meta);
1937
+ }
1938
+ /**
1939
+ * Send a message to agents with a specific role
1940
+ */
1941
+ async sendToRole(role, payload, meta, withinScope) {
1942
+ return this.send({ role, within: withinScope }, payload, meta);
1943
+ }
1944
+ /**
1945
+ * Broadcast a message to all agents
1946
+ */
1947
+ async broadcast(payload, meta) {
1948
+ return this.send({ broadcast: true }, payload, meta);
1949
+ }
1950
+ /**
1951
+ * Send a request and wait for a correlated response
1952
+ *
1953
+ * This is a higher-level pattern for request/response messaging.
1954
+ * A correlationId is automatically generated.
1955
+ */
1956
+ async request(to, payload, options) {
1957
+ const correlationId = `req-${Date.now()}-${Math.random().toString(36).slice(2)}`;
1958
+ const responseSub = await this.subscribe({
1959
+ // We'll filter in the handler since subscription filters don't support correlationId
1960
+ });
1961
+ try {
1962
+ await this.send(to, payload, {
1963
+ ...options?.meta,
1964
+ expectsResponse: true,
1965
+ correlationId
1966
+ });
1967
+ const timeout = options?.timeout ?? 3e4;
1968
+ const timeoutPromise = new Promise((_, reject) => {
1969
+ setTimeout(() => reject(new Error(`Request timed out after ${timeout}ms`)), timeout);
1970
+ });
1971
+ const responsePromise = (async () => {
1972
+ for await (const event of responseSub) {
1973
+ if (event.type === "message_delivered" && event.data && event.data.correlationId === correlationId) {
1974
+ return event.data.message;
1975
+ }
1976
+ }
1977
+ throw new Error("Subscription closed before response received");
1978
+ })();
1979
+ return await Promise.race([responsePromise, timeoutPromise]);
1980
+ } finally {
1981
+ await responseSub.unsubscribe();
1982
+ }
1983
+ }
1984
+ // ===========================================================================
1985
+ // Subscriptions
1986
+ // ===========================================================================
1987
+ /**
1988
+ * Subscribe to events
1989
+ */
1990
+ async subscribe(filter) {
1991
+ const params = {};
1992
+ if (filter) params.filter = filter;
1993
+ const result = await this.#connection.sendRequest(CORE_METHODS.SUBSCRIBE, params);
1994
+ const serverSupportsAck = this.#serverCapabilities?.streaming?.supportsAck === true;
1995
+ const sendAck = serverSupportsAck ? (ackParams) => {
1996
+ this.#connection.sendNotification(NOTIFICATION_METHODS.SUBSCRIBE_ACK, ackParams);
1997
+ } : void 0;
1998
+ const subscription = createSubscription(
1999
+ result.subscriptionId,
2000
+ () => this.unsubscribe(result.subscriptionId),
2001
+ { filter },
2002
+ sendAck
2003
+ );
2004
+ if (serverSupportsAck) {
2005
+ subscription._setServerSupportsAck(true);
2006
+ }
2007
+ this.#subscriptions.set(result.subscriptionId, subscription);
2008
+ if (this.#options.reconnection?.restoreSubscriptions !== false) {
2009
+ this.#subscriptionStates.set(result.subscriptionId, {
2010
+ filter,
2011
+ handlers: /* @__PURE__ */ new Set()
2012
+ });
2013
+ const originalPushEvent = subscription._pushEvent.bind(subscription);
2014
+ subscription._pushEvent = (event) => {
2015
+ const state = this.#subscriptionStates.get(result.subscriptionId);
2016
+ if (state && event.eventId) {
2017
+ state.lastEventId = event.eventId;
2018
+ }
2019
+ originalPushEvent(event);
2020
+ };
2021
+ }
2022
+ return subscription;
2023
+ }
2024
+ /**
2025
+ * Unsubscribe from events
2026
+ */
2027
+ async unsubscribe(subscriptionId) {
2028
+ const subscription = this.#subscriptions.get(subscriptionId);
2029
+ if (subscription) {
2030
+ subscription._close();
2031
+ this.#subscriptions.delete(subscriptionId);
2032
+ }
2033
+ this.#subscriptionStates.delete(subscriptionId);
2034
+ await this.#connection.sendRequest(CORE_METHODS.UNSUBSCRIBE, { subscriptionId });
2035
+ }
2036
+ // ===========================================================================
2037
+ // Event Replay
2038
+ // ===========================================================================
2039
+ /**
2040
+ * Replay historical events.
2041
+ *
2042
+ * Uses keyset pagination - pass the last eventId from the previous
2043
+ * response to get the next page.
2044
+ *
2045
+ * @example
2046
+ * ```typescript
2047
+ * // Replay all events from the last hour
2048
+ * const result = await client.replay({
2049
+ * fromTimestamp: Date.now() - 3600000,
2050
+ * filter: { eventTypes: ['agent.registered'] },
2051
+ * limit: 100
2052
+ * });
2053
+ *
2054
+ * // Paginate through results
2055
+ * let afterEventId: string | undefined;
2056
+ * do {
2057
+ * const page = await client.replay({ afterEventId, limit: 100 });
2058
+ * for (const item of page.events) {
2059
+ * console.log(item.eventId, item.event);
2060
+ * }
2061
+ * afterEventId = page.events.at(-1)?.eventId;
2062
+ * } while (page.hasMore);
2063
+ * ```
2064
+ */
2065
+ async replay(params = {}) {
2066
+ const limit = Math.min(params.limit ?? 100, 1e3);
2067
+ return this.#connection.sendRequest(
2068
+ CORE_METHODS.REPLAY,
2069
+ { ...params, limit }
2070
+ );
2071
+ }
2072
+ /**
2073
+ * Replay all events matching filter, handling pagination automatically.
2074
+ *
2075
+ * Returns an async generator for streaming through all results.
2076
+ *
2077
+ * @example
2078
+ * ```typescript
2079
+ * for await (const item of client.replayAll({
2080
+ * filter: { eventTypes: ['agent.registered'] }
2081
+ * })) {
2082
+ * console.log(item.eventId, item.event);
2083
+ * }
2084
+ * ```
2085
+ */
2086
+ async *replayAll(params = {}) {
2087
+ let afterEventId;
2088
+ let hasMore = true;
2089
+ while (hasMore) {
2090
+ const result = await this.replay({ ...params, afterEventId });
2091
+ for (const item of result.events) {
2092
+ yield item;
2093
+ }
2094
+ hasMore = result.hasMore;
2095
+ afterEventId = result.events.at(-1)?.eventId;
2096
+ if (result.events.length === 0) {
2097
+ break;
2098
+ }
2099
+ }
2100
+ }
2101
+ // ===========================================================================
2102
+ // Steering (requires canSteer capability)
2103
+ // ===========================================================================
2104
+ /**
2105
+ * Inject context into a running agent
2106
+ */
2107
+ async inject(agentId, content, delivery) {
2108
+ const params = { agentId, content };
2109
+ if (delivery) params.delivery = delivery;
2110
+ return this.#connection.sendRequest(STEERING_METHODS.INJECT, params);
2111
+ }
2112
+ // ===========================================================================
2113
+ // Lifecycle Control (requires canStop capability)
2114
+ // ===========================================================================
2115
+ /**
2116
+ * Request an agent to stop
2117
+ */
2118
+ async stopAgent(agentId, options) {
2119
+ return this.#connection.sendRequest(STATE_METHODS.AGENTS_STOP, {
2120
+ agentId,
2121
+ ...options
2122
+ });
2123
+ }
2124
+ /**
2125
+ * Suspend an agent
2126
+ */
2127
+ async suspendAgent(agentId, reason) {
2128
+ return this.#connection.sendRequest(STATE_METHODS.AGENTS_SUSPEND, {
2129
+ agentId,
2130
+ reason
2131
+ });
2132
+ }
2133
+ /**
2134
+ * Resume a suspended agent
2135
+ */
2136
+ async resumeAgent(agentId) {
2137
+ return this.#connection.sendRequest(STATE_METHODS.AGENTS_RESUME, { agentId });
2138
+ }
2139
+ // ===========================================================================
2140
+ // Reconnection
2141
+ // ===========================================================================
2142
+ /**
2143
+ * Current connection state
2144
+ */
2145
+ get state() {
2146
+ return this.#connection.state;
2147
+ }
2148
+ /**
2149
+ * Whether the connection is currently reconnecting
2150
+ */
2151
+ get isReconnecting() {
2152
+ return this.#isReconnecting;
2153
+ }
2154
+ /**
2155
+ * Register a handler for reconnection events.
2156
+ *
2157
+ * @param handler - Function called when reconnection events occur
2158
+ * @returns Unsubscribe function to remove the handler
2159
+ *
2160
+ * @example
2161
+ * ```typescript
2162
+ * const unsubscribe = client.onReconnection((event) => {
2163
+ * switch (event.type) {
2164
+ * case 'disconnected':
2165
+ * console.log('Connection lost');
2166
+ * break;
2167
+ * case 'reconnecting':
2168
+ * console.log(`Reconnecting, attempt ${event.attempt}`);
2169
+ * break;
2170
+ * case 'reconnected':
2171
+ * console.log('Reconnected successfully');
2172
+ * break;
2173
+ * case 'reconnectFailed':
2174
+ * console.log('Failed to reconnect:', event.error);
2175
+ * break;
2176
+ * }
2177
+ * });
2178
+ * ```
2179
+ */
2180
+ onReconnection(handler) {
2181
+ this.#reconnectionHandlers.add(handler);
2182
+ return () => this.#reconnectionHandlers.delete(handler);
2183
+ }
2184
+ /**
2185
+ * Register a handler for connection state changes.
2186
+ *
2187
+ * @param handler - Function called when state changes
2188
+ * @returns Unsubscribe function to remove the handler
2189
+ */
2190
+ onStateChange(handler) {
2191
+ return this.#connection.onStateChange(handler);
2192
+ }
2193
+ // ===========================================================================
2194
+ // Internal
2195
+ // ===========================================================================
2196
+ /**
2197
+ * Handle incoming notifications
2198
+ */
2199
+ async #handleNotification(method, params) {
2200
+ switch (method) {
2201
+ case NOTIFICATION_METHODS.EVENT: {
2202
+ const eventParams = params;
2203
+ const subscription = this.#subscriptions.get(eventParams.subscriptionId);
2204
+ if (subscription) {
2205
+ subscription._pushEvent(eventParams);
2206
+ } else {
2207
+ console.warn("MAP: Event for unknown subscription:", eventParams.subscriptionId);
2208
+ }
2209
+ break;
2210
+ }
2211
+ case NOTIFICATION_METHODS.MESSAGE: {
2212
+ break;
2213
+ }
2214
+ default:
2215
+ console.warn("MAP: Unknown notification:", method);
2216
+ }
2217
+ }
2218
+ /**
2219
+ * Emit a reconnection event to all registered handlers
2220
+ */
2221
+ #emitReconnectionEvent(event) {
2222
+ for (const handler of this.#reconnectionHandlers) {
2223
+ try {
2224
+ handler(event);
2225
+ } catch (error) {
2226
+ console.error("MAP: Reconnection event handler error:", error);
2227
+ }
2228
+ }
2229
+ }
2230
+ /**
2231
+ * Handle disconnect when auto-reconnect is enabled
2232
+ */
2233
+ async #handleDisconnect() {
2234
+ this.#isReconnecting = true;
2235
+ this.#connected = false;
2236
+ this.#emitReconnectionEvent({ type: "disconnected" });
2237
+ try {
2238
+ await this.#attemptReconnect();
2239
+ } catch (error) {
2240
+ this.#isReconnecting = false;
2241
+ this.#emitReconnectionEvent({
2242
+ type: "reconnectFailed",
2243
+ error: error instanceof Error ? error : new Error(String(error))
2244
+ });
2245
+ }
2246
+ }
2247
+ /**
2248
+ * Attempt to reconnect with retry logic
2249
+ */
2250
+ async #attemptReconnect() {
2251
+ const options = this.#options.reconnection;
2252
+ const createStream = this.#options.createStream;
2253
+ const retryPolicy = {
2254
+ maxRetries: options.maxRetries ?? DEFAULT_RETRY_POLICY.maxRetries,
2255
+ baseDelayMs: options.baseDelayMs ?? DEFAULT_RETRY_POLICY.baseDelayMs,
2256
+ maxDelayMs: options.maxDelayMs ?? DEFAULT_RETRY_POLICY.maxDelayMs,
2257
+ jitter: options.jitter ?? DEFAULT_RETRY_POLICY.jitter
2258
+ };
2259
+ await withRetry(
2260
+ async () => {
2261
+ const newStream = await createStream();
2262
+ await this.#connection.reconnect(newStream);
2263
+ const connectResult = await this.connect(this.#lastConnectOptions);
2264
+ this.#sessionId = connectResult.sessionId;
2265
+ this.#serverCapabilities = connectResult.capabilities;
2266
+ },
2267
+ retryPolicy,
2268
+ {
2269
+ onRetry: (state) => {
2270
+ this.#emitReconnectionEvent({
2271
+ type: "reconnecting",
2272
+ attempt: state.attempt,
2273
+ delay: state.nextDelayMs,
2274
+ error: state.lastError
2275
+ });
2276
+ }
2277
+ }
2278
+ );
2279
+ this.#isReconnecting = false;
2280
+ this.#emitReconnectionEvent({ type: "reconnected" });
2281
+ if (options.restoreSubscriptions !== false) {
2282
+ await this.#restoreSubscriptions();
2283
+ }
2284
+ }
2285
+ /**
2286
+ * Restore subscriptions after reconnection
2287
+ */
2288
+ async #restoreSubscriptions() {
2289
+ const options = this.#options.reconnection;
2290
+ const subscriptionEntries = Array.from(this.#subscriptionStates.entries());
2291
+ this.#subscriptions.clear();
2292
+ this.#subscriptionStates.clear();
2293
+ for (const [oldId, state] of subscriptionEntries) {
2294
+ try {
2295
+ const newSubscription = await this.subscribe(state.filter);
2296
+ const newId = newSubscription.id;
2297
+ if (options.replayOnRestore !== false && state.lastEventId) {
2298
+ const maxEvents = options.maxReplayEventsPerSubscription ?? 1e3;
2299
+ try {
2300
+ let replayedCount = 0;
2301
+ let afterEventId = state.lastEventId;
2302
+ let hasMore = true;
2303
+ while (hasMore && replayedCount < maxEvents) {
2304
+ const result = await this.replay({
2305
+ afterEventId,
2306
+ filter: state.filter,
2307
+ limit: Math.min(100, maxEvents - replayedCount)
2308
+ });
2309
+ for (const replayedEvent of result.events) {
2310
+ if (replayedCount >= maxEvents) break;
2311
+ newSubscription._pushEvent({
2312
+ subscriptionId: newId,
2313
+ sequenceNumber: replayedCount + 1,
2314
+ eventId: replayedEvent.eventId,
2315
+ timestamp: replayedEvent.timestamp,
2316
+ event: replayedEvent.event
2317
+ });
2318
+ replayedCount++;
2319
+ }
2320
+ hasMore = result.hasMore;
2321
+ afterEventId = result.events.at(-1)?.eventId;
2322
+ if (result.events.length === 0) {
2323
+ break;
2324
+ }
2325
+ }
2326
+ } catch (replayError) {
2327
+ console.warn("MAP: Failed to replay events for subscription:", oldId, replayError);
2328
+ }
2329
+ }
2330
+ this.#emitReconnectionEvent({
2331
+ type: "subscriptionRestored",
2332
+ subscriptionId: oldId,
2333
+ newSubscriptionId: newId
2334
+ });
2335
+ } catch (error) {
2336
+ this.#emitReconnectionEvent({
2337
+ type: "subscriptionRestoreFailed",
2338
+ subscriptionId: oldId,
2339
+ error: error instanceof Error ? error : new Error(String(error))
2340
+ });
2341
+ }
2342
+ }
2343
+ }
2344
+ };
2345
+
2346
+ // src/connection/agent.ts
2347
+ var AgentConnection = class {
2348
+ #connection;
2349
+ #subscriptions = /* @__PURE__ */ new Map();
2350
+ #options;
2351
+ #messageHandlers = /* @__PURE__ */ new Set();
2352
+ #reconnectionHandlers = /* @__PURE__ */ new Set();
2353
+ #scopeMemberships = /* @__PURE__ */ new Set();
2354
+ #agentId = null;
2355
+ #sessionId = null;
2356
+ #serverCapabilities = null;
2357
+ #currentState = "registered";
2358
+ #connected = false;
2359
+ #lastConnectOptions;
2360
+ #isReconnecting = false;
2361
+ constructor(stream, options = {}) {
2362
+ this.#connection = new BaseConnection(stream, options);
2363
+ this.#options = options;
2364
+ this.#connection.setNotificationHandler(this.#handleNotification.bind(this));
2365
+ if (options.reconnection?.enabled && options.createStream) {
2366
+ this.#connection.onStateChange((newState) => {
2367
+ if (newState === "closed" && this.#connected && !this.#isReconnecting) {
2368
+ void this.#handleDisconnect();
2369
+ }
2370
+ });
2371
+ }
2372
+ }
2373
+ // ===========================================================================
2374
+ // Connection Lifecycle
2375
+ // ===========================================================================
2376
+ /**
2377
+ * Connect and register with the MAP system
2378
+ */
2379
+ async connect(options) {
2380
+ const connectParams = {
2381
+ protocolVersion: PROTOCOL_VERSION,
2382
+ participantType: "agent",
2383
+ participantId: options?.agentId,
2384
+ name: this.#options.name,
2385
+ capabilities: this.#options.capabilities,
2386
+ auth: options?.auth
2387
+ };
2388
+ const connectResult = await this.#connection.sendRequest(CORE_METHODS.CONNECT, connectParams);
2389
+ this.#sessionId = connectResult.sessionId;
2390
+ this.#serverCapabilities = connectResult.capabilities;
2391
+ this.#connected = true;
2392
+ this.#lastConnectOptions = options;
2393
+ const registerParams = {
2394
+ agentId: options?.agentId,
2395
+ name: this.#options.name,
2396
+ role: this.#options.role,
2397
+ parent: this.#options.parent,
2398
+ scopes: this.#options.scopes,
2399
+ visibility: this.#options.visibility,
2400
+ capabilities: this.#options.capabilities
2401
+ };
2402
+ const registerResult = await this.#connection.sendRequest(LIFECYCLE_METHODS.AGENTS_REGISTER, registerParams);
2403
+ this.#agentId = registerResult.agent.id;
2404
+ this.#currentState = registerResult.agent.state;
2405
+ this.#connection._transitionTo("connected");
2406
+ return { connection: connectResult, agent: registerResult.agent };
2407
+ }
2408
+ /**
2409
+ * Disconnect from the MAP system
2410
+ */
2411
+ async disconnect(reason) {
2412
+ if (!this.#connected) return;
2413
+ try {
2414
+ if (this.#agentId) {
2415
+ await this.#connection.sendRequest(LIFECYCLE_METHODS.AGENTS_UNREGISTER, {
2416
+ agentId: this.#agentId,
2417
+ reason
2418
+ });
2419
+ }
2420
+ await this.#connection.sendRequest(
2421
+ CORE_METHODS.DISCONNECT,
2422
+ reason ? { reason } : void 0
2423
+ );
2424
+ } finally {
2425
+ for (const subscription of this.#subscriptions.values()) {
2426
+ subscription._close();
2427
+ }
2428
+ this.#subscriptions.clear();
2429
+ await this.#connection.close();
2430
+ this.#connected = false;
2431
+ }
2432
+ }
2433
+ /**
2434
+ * Whether the agent is connected
2435
+ */
2436
+ get isConnected() {
2437
+ return this.#connected && !this.#connection.isClosed;
2438
+ }
2439
+ /**
2440
+ * This agent's ID
2441
+ */
2442
+ get agentId() {
2443
+ return this.#agentId;
2444
+ }
2445
+ /**
2446
+ * Current session ID
2447
+ */
2448
+ get sessionId() {
2449
+ return this.#sessionId;
2450
+ }
2451
+ /**
2452
+ * Server capabilities
2453
+ */
2454
+ get serverCapabilities() {
2455
+ return this.#serverCapabilities;
2456
+ }
2457
+ /**
2458
+ * Current agent state
2459
+ */
2460
+ get state() {
2461
+ return this.#currentState;
2462
+ }
2463
+ /**
2464
+ * AbortSignal that triggers when the connection closes
2465
+ */
2466
+ get signal() {
2467
+ return this.#connection.signal;
2468
+ }
2469
+ /**
2470
+ * Promise that resolves when the connection closes
2471
+ */
2472
+ get closed() {
2473
+ return this.#connection.closed;
2474
+ }
2475
+ // ===========================================================================
2476
+ // Message Handling
2477
+ // ===========================================================================
2478
+ /**
2479
+ * Register a handler for incoming messages
2480
+ */
2481
+ onMessage(handler) {
2482
+ this.#messageHandlers.add(handler);
2483
+ return this;
2484
+ }
2485
+ /**
2486
+ * Remove a message handler
2487
+ */
2488
+ offMessage(handler) {
2489
+ this.#messageHandlers.delete(handler);
2490
+ return this;
2491
+ }
2492
+ // ===========================================================================
2493
+ // State Management
2494
+ // ===========================================================================
2495
+ /**
2496
+ * Update this agent's state
2497
+ */
2498
+ async updateState(state) {
2499
+ if (!this.#agentId) {
2500
+ throw new Error("Agent not registered");
2501
+ }
2502
+ const result = await this.#connection.sendRequest(STATE_METHODS.AGENTS_UPDATE, {
2503
+ agentId: this.#agentId,
2504
+ state
2505
+ });
2506
+ this.#currentState = result.agent.state;
2507
+ return result.agent;
2508
+ }
2509
+ /**
2510
+ * Update this agent's metadata
2511
+ */
2512
+ async updateMetadata(metadata) {
2513
+ if (!this.#agentId) {
2514
+ throw new Error("Agent not registered");
2515
+ }
2516
+ const result = await this.#connection.sendRequest(STATE_METHODS.AGENTS_UPDATE, {
2517
+ agentId: this.#agentId,
2518
+ metadata
2519
+ });
2520
+ return result.agent;
2521
+ }
2522
+ /**
2523
+ * Mark this agent as busy
2524
+ */
2525
+ async busy() {
2526
+ return this.updateState("busy");
2527
+ }
2528
+ /**
2529
+ * Mark this agent as idle
2530
+ */
2531
+ async idle() {
2532
+ return this.updateState("idle");
2533
+ }
2534
+ /**
2535
+ * Mark this agent as done/stopped
2536
+ */
2537
+ async done(result) {
2538
+ if (!this.#agentId) {
2539
+ throw new Error("Agent not registered");
2540
+ }
2541
+ await this.updateState("stopped");
2542
+ if (result) {
2543
+ await this.updateMetadata({
2544
+ exitCode: result.exitCode,
2545
+ exitReason: result.exitReason
2546
+ });
2547
+ }
2548
+ }
2549
+ // ===========================================================================
2550
+ // Child Agent Management
2551
+ // ===========================================================================
2552
+ /**
2553
+ * Spawn a child agent
2554
+ */
2555
+ async spawn(options) {
2556
+ if (!this.#agentId) {
2557
+ throw new Error("Agent not registered");
2558
+ }
2559
+ const params = {
2560
+ ...options,
2561
+ parent: this.#agentId
2562
+ };
2563
+ return this.#connection.sendRequest(LIFECYCLE_METHODS.AGENTS_SPAWN, params);
2564
+ }
2565
+ // ===========================================================================
2566
+ // Messaging
2567
+ // ===========================================================================
2568
+ /**
2569
+ * Send a message to an address
2570
+ */
2571
+ async send(to, payload, meta) {
2572
+ const params = { to };
2573
+ if (payload !== void 0) params.payload = payload;
2574
+ if (meta) params.meta = meta;
2575
+ return this.#connection.sendRequest(CORE_METHODS.SEND, params);
2576
+ }
2577
+ /**
2578
+ * Send a message to the parent agent
2579
+ */
2580
+ async sendToParent(payload, meta) {
2581
+ return this.send({ parent: true }, payload, {
2582
+ ...meta,
2583
+ relationship: "child-to-parent"
2584
+ });
2585
+ }
2586
+ /**
2587
+ * Send a message to child agents
2588
+ */
2589
+ async sendToChildren(payload, meta) {
2590
+ return this.send({ children: true }, payload, {
2591
+ ...meta,
2592
+ relationship: "parent-to-child"
2593
+ });
2594
+ }
2595
+ /**
2596
+ * Send a message to a specific agent
2597
+ */
2598
+ async sendToAgent(agentId, payload, meta) {
2599
+ return this.send({ agent: agentId }, payload, meta);
2600
+ }
2601
+ /**
2602
+ * Send a message to all agents in a scope
2603
+ */
2604
+ async sendToScope(scopeId, payload, meta) {
2605
+ return this.send({ scope: scopeId }, payload, meta);
2606
+ }
2607
+ /**
2608
+ * Send a message to sibling agents
2609
+ */
2610
+ async sendToSiblings(payload, meta) {
2611
+ return this.send({ siblings: true }, payload, {
2612
+ ...meta,
2613
+ relationship: "peer"
2614
+ });
2615
+ }
2616
+ /**
2617
+ * Reply to a message (uses correlationId from original)
2618
+ */
2619
+ async reply(originalMessage, payload, meta) {
2620
+ return this.send({ agent: originalMessage.from }, payload, {
2621
+ ...meta,
2622
+ correlationId: originalMessage.meta?.correlationId ?? originalMessage.id,
2623
+ isResult: true
2624
+ });
2625
+ }
2626
+ // ===========================================================================
2627
+ // Scope Management
2628
+ // ===========================================================================
2629
+ /**
2630
+ * Create a new scope
2631
+ */
2632
+ async createScope(options) {
2633
+ const result = await this.#connection.sendRequest(SCOPE_METHODS.SCOPES_CREATE, options);
2634
+ return result.scope;
2635
+ }
2636
+ /**
2637
+ * Join a scope
2638
+ */
2639
+ async joinScope(scopeId) {
2640
+ if (!this.#agentId) {
2641
+ throw new Error("Agent not registered");
2642
+ }
2643
+ const result = await this.#connection.sendRequest(SCOPE_METHODS.SCOPES_JOIN, {
2644
+ scopeId,
2645
+ agentId: this.#agentId
2646
+ });
2647
+ this.#scopeMemberships.add(scopeId);
2648
+ return result;
2649
+ }
2650
+ /**
2651
+ * Leave a scope
2652
+ */
2653
+ async leaveScope(scopeId) {
2654
+ if (!this.#agentId) {
2655
+ throw new Error("Agent not registered");
2656
+ }
2657
+ const result = await this.#connection.sendRequest(SCOPE_METHODS.SCOPES_LEAVE, {
2658
+ scopeId,
2659
+ agentId: this.#agentId
2660
+ });
2661
+ this.#scopeMemberships.delete(scopeId);
2662
+ return result;
2663
+ }
2664
+ // ===========================================================================
2665
+ // Subscriptions
2666
+ // ===========================================================================
2667
+ /**
2668
+ * Subscribe to events
2669
+ */
2670
+ async subscribe(filter) {
2671
+ const params = {};
2672
+ if (filter) params.filter = filter;
2673
+ const result = await this.#connection.sendRequest(CORE_METHODS.SUBSCRIBE, params);
2674
+ const subscription = createSubscription(
2675
+ result.subscriptionId,
2676
+ () => this.unsubscribe(result.subscriptionId),
2677
+ { filter }
2678
+ );
2679
+ this.#subscriptions.set(result.subscriptionId, subscription);
2680
+ return subscription;
2681
+ }
2682
+ /**
2683
+ * Unsubscribe from events
2684
+ */
2685
+ async unsubscribe(subscriptionId) {
2686
+ const subscription = this.#subscriptions.get(subscriptionId);
2687
+ if (subscription) {
2688
+ subscription._close();
2689
+ this.#subscriptions.delete(subscriptionId);
2690
+ }
2691
+ await this.#connection.sendRequest(CORE_METHODS.UNSUBSCRIBE, { subscriptionId });
2692
+ }
2693
+ // ===========================================================================
2694
+ // Reconnection
2695
+ // ===========================================================================
2696
+ /**
2697
+ * Current connection state
2698
+ */
2699
+ get connectionState() {
2700
+ return this.#connection.state;
2701
+ }
2702
+ /**
2703
+ * Whether the connection is currently reconnecting
2704
+ */
2705
+ get isReconnecting() {
2706
+ return this.#isReconnecting;
2707
+ }
2708
+ /**
2709
+ * Register a handler for reconnection events.
2710
+ *
2711
+ * @param handler - Function called when reconnection events occur
2712
+ * @returns Unsubscribe function to remove the handler
2713
+ */
2714
+ onReconnection(handler) {
2715
+ this.#reconnectionHandlers.add(handler);
2716
+ return () => this.#reconnectionHandlers.delete(handler);
2717
+ }
2718
+ /**
2719
+ * Register a handler for connection state changes.
2720
+ *
2721
+ * @param handler - Function called when state changes
2722
+ * @returns Unsubscribe function to remove the handler
2723
+ */
2724
+ onStateChange(handler) {
2725
+ return this.#connection.onStateChange(handler);
2726
+ }
2727
+ // ===========================================================================
2728
+ // Internal
2729
+ // ===========================================================================
2730
+ /**
2731
+ * Handle incoming notifications
2732
+ */
2733
+ async #handleNotification(method, params) {
2734
+ switch (method) {
2735
+ case NOTIFICATION_METHODS.EVENT: {
2736
+ const eventParams = params;
2737
+ const subscription = this.#subscriptions.get(eventParams.subscriptionId);
2738
+ if (subscription) {
2739
+ subscription._pushEvent(eventParams);
2740
+ }
2741
+ break;
2742
+ }
2743
+ case NOTIFICATION_METHODS.MESSAGE: {
2744
+ const messageParams = params;
2745
+ for (const handler of this.#messageHandlers) {
2746
+ try {
2747
+ await handler(messageParams.message);
2748
+ } catch (error) {
2749
+ console.error("MAP: Message handler error:", error);
2750
+ }
2751
+ }
2752
+ break;
2753
+ }
2754
+ default:
2755
+ console.warn("MAP: Unknown notification:", method);
2756
+ }
2757
+ }
2758
+ /**
2759
+ * Emit a reconnection event to all registered handlers
2760
+ */
2761
+ #emitReconnectionEvent(event) {
2762
+ for (const handler of this.#reconnectionHandlers) {
2763
+ try {
2764
+ handler(event);
2765
+ } catch (error) {
2766
+ console.error("MAP: Reconnection event handler error:", error);
2767
+ }
2768
+ }
2769
+ }
2770
+ /**
2771
+ * Handle disconnect when auto-reconnect is enabled
2772
+ */
2773
+ async #handleDisconnect() {
2774
+ this.#isReconnecting = true;
2775
+ this.#connected = false;
2776
+ this.#emitReconnectionEvent({ type: "disconnected" });
2777
+ try {
2778
+ await this.#attemptReconnect();
2779
+ } catch (error) {
2780
+ this.#isReconnecting = false;
2781
+ this.#emitReconnectionEvent({
2782
+ type: "reconnectFailed",
2783
+ error: error instanceof Error ? error : new Error(String(error))
2784
+ });
2785
+ }
2786
+ }
2787
+ /**
2788
+ * Attempt to reconnect with retry logic
2789
+ */
2790
+ async #attemptReconnect() {
2791
+ const options = this.#options.reconnection;
2792
+ const createStream = this.#options.createStream;
2793
+ const retryPolicy = {
2794
+ maxRetries: options.maxRetries ?? DEFAULT_RETRY_POLICY.maxRetries,
2795
+ baseDelayMs: options.baseDelayMs ?? DEFAULT_RETRY_POLICY.baseDelayMs,
2796
+ maxDelayMs: options.maxDelayMs ?? DEFAULT_RETRY_POLICY.maxDelayMs,
2797
+ jitter: options.jitter ?? DEFAULT_RETRY_POLICY.jitter
2798
+ };
2799
+ const scopesToRestore = Array.from(this.#scopeMemberships);
2800
+ await withRetry(
2801
+ async () => {
2802
+ const newStream = await createStream();
2803
+ await this.#connection.reconnect(newStream);
2804
+ const result = await this.connect({
2805
+ agentId: this.#agentId ?? this.#lastConnectOptions?.agentId,
2806
+ auth: this.#lastConnectOptions?.auth
2807
+ });
2808
+ this.#agentId = result.agent.id;
2809
+ this.#sessionId = result.connection.sessionId;
2810
+ this.#serverCapabilities = result.connection.capabilities;
2811
+ this.#currentState = result.agent.state;
2812
+ },
2813
+ retryPolicy,
2814
+ {
2815
+ onRetry: (state) => {
2816
+ this.#emitReconnectionEvent({
2817
+ type: "reconnecting",
2818
+ attempt: state.attempt,
2819
+ delay: state.nextDelayMs,
2820
+ error: state.lastError
2821
+ });
2822
+ }
2823
+ }
2824
+ );
2825
+ this.#isReconnecting = false;
2826
+ this.#emitReconnectionEvent({ type: "reconnected" });
2827
+ if (options.restoreScopeMemberships !== false) {
2828
+ await this.#restoreScopeMemberships(scopesToRestore);
2829
+ }
2830
+ }
2831
+ /**
2832
+ * Restore scope memberships after reconnection
2833
+ */
2834
+ async #restoreScopeMemberships(scopes) {
2835
+ this.#scopeMemberships.clear();
2836
+ for (const scopeId of scopes) {
2837
+ try {
2838
+ await this.joinScope(scopeId);
2839
+ } catch (error) {
2840
+ console.warn("MAP: Failed to restore scope membership:", scopeId, error);
2841
+ }
2842
+ }
2843
+ }
2844
+ };
2845
+
2846
+ // src/federation/envelope.ts
2847
+ function createFederationEnvelope(payload, sourceSystem, targetSystem, options) {
2848
+ return {
2849
+ payload,
2850
+ federation: {
2851
+ sourceSystem,
2852
+ targetSystem,
2853
+ hopCount: 0,
2854
+ maxHops: options?.maxHops,
2855
+ path: options?.trackPath ? [sourceSystem] : void 0,
2856
+ originTimestamp: Date.now(),
2857
+ correlationId: options?.correlationId
2858
+ }
2859
+ };
2860
+ }
2861
+ function processFederationEnvelope(envelope, config) {
2862
+ const { federation } = envelope;
2863
+ const maxHops = federation.maxHops ?? config.maxHops ?? 10;
2864
+ if (federation.hopCount >= maxHops) {
2865
+ return {
2866
+ success: false,
2867
+ errorCode: ERROR_CODES.FEDERATION_MAX_HOPS_EXCEEDED,
2868
+ errorMessage: `Message exceeded maximum hop count of ${maxHops}`
2869
+ };
2870
+ }
2871
+ if (federation.path?.includes(config.systemId)) {
2872
+ return {
2873
+ success: false,
2874
+ errorCode: ERROR_CODES.FEDERATION_LOOP_DETECTED,
2875
+ errorMessage: `Loop detected: message already visited ${config.systemId}`
2876
+ };
2877
+ }
2878
+ if (config.allowedSources && !config.allowedSources.includes(federation.sourceSystem)) {
2879
+ return {
2880
+ success: false,
2881
+ errorCode: ERROR_CODES.FEDERATION_ROUTE_REJECTED,
2882
+ errorMessage: `Source system ${federation.sourceSystem} not in allowed sources`
2883
+ };
2884
+ }
2885
+ if (config.allowedTargets && !config.allowedTargets.includes(federation.targetSystem)) {
2886
+ return {
2887
+ success: false,
2888
+ errorCode: ERROR_CODES.FEDERATION_ROUTE_REJECTED,
2889
+ errorMessage: `Target system ${federation.targetSystem} not in allowed targets`
2890
+ };
2891
+ }
2892
+ return {
2893
+ success: true,
2894
+ envelope: {
2895
+ payload: envelope.payload,
2896
+ federation: {
2897
+ ...federation,
2898
+ hopCount: federation.hopCount + 1,
2899
+ path: config.trackPath ? [...federation.path ?? [], config.systemId] : federation.path
2900
+ }
2901
+ }
2902
+ };
2903
+ }
2904
+ function isEnvelopeAtDestination(envelope, currentSystemId) {
2905
+ return envelope.federation.targetSystem === currentSystemId;
2906
+ }
2907
+ function unwrapEnvelope(envelope) {
2908
+ return envelope.payload;
2909
+ }
2910
+ function getEnvelopeRoutingInfo(envelope) {
2911
+ const { federation } = envelope;
2912
+ return {
2913
+ source: federation.sourceSystem,
2914
+ target: federation.targetSystem,
2915
+ hops: federation.hopCount,
2916
+ path: federation.path,
2917
+ age: Date.now() - federation.originTimestamp,
2918
+ correlationId: federation.correlationId
2919
+ };
2920
+ }
2921
+ function isValidEnvelope(obj) {
2922
+ if (!obj || typeof obj !== "object") return false;
2923
+ const envelope = obj;
2924
+ if (!("payload" in envelope) || !("federation" in envelope)) return false;
2925
+ const federation = envelope.federation;
2926
+ if (!federation || typeof federation !== "object") return false;
2927
+ return typeof federation.sourceSystem === "string" && typeof federation.targetSystem === "string" && typeof federation.hopCount === "number" && typeof federation.originTimestamp === "number";
2928
+ }
2929
+ function withPayload(envelope, newPayload) {
2930
+ return {
2931
+ payload: newPayload,
2932
+ federation: envelope.federation
2933
+ };
2934
+ }
2935
+
2936
+ // src/federation/buffer.ts
2937
+ var DEFAULT_CONFIG = {
2938
+ enabled: true,
2939
+ maxMessages: 1e3,
2940
+ maxBytes: 10 * 1024 * 1024,
2941
+ // 10MB
2942
+ retentionMs: 60 * 60 * 1e3,
2943
+ // 1 hour
2944
+ overflowStrategy: "drop-oldest"
2945
+ };
2946
+ var FederationOutageBuffer = class {
2947
+ #config;
2948
+ #buffers = /* @__PURE__ */ new Map();
2949
+ constructor(config) {
2950
+ this.#config = { ...DEFAULT_CONFIG, ...config };
2951
+ }
2952
+ /**
2953
+ * Whether buffering is enabled.
2954
+ */
2955
+ get enabled() {
2956
+ return this.#config.enabled;
2957
+ }
2958
+ /**
2959
+ * Get the configuration.
2960
+ */
2961
+ get config() {
2962
+ return this.#config;
2963
+ }
2964
+ /**
2965
+ * Enqueue a message for a peer.
2966
+ *
2967
+ * @param peerId - Target peer system ID
2968
+ * @param envelope - Message envelope to buffer
2969
+ * @returns true if message was buffered, false if rejected
2970
+ */
2971
+ enqueue(peerId, envelope) {
2972
+ if (!this.#config.enabled) return false;
2973
+ let buffer = this.#buffers.get(peerId);
2974
+ if (!buffer) {
2975
+ buffer = { messages: [], totalEnqueued: 0, totalDropped: 0, totalBytes: 0 };
2976
+ this.#buffers.set(peerId, buffer);
2977
+ }
2978
+ this.#evictExpired(buffer);
2979
+ const messageSize = this.#estimateSize(envelope);
2980
+ while (buffer.totalBytes + messageSize > this.#config.maxBytes && buffer.messages.length > 0) {
2981
+ const removed = buffer.messages.shift();
2982
+ buffer.totalBytes -= removed.size;
2983
+ buffer.totalDropped++;
2984
+ }
2985
+ if (buffer.messages.length >= this.#config.maxMessages) {
2986
+ switch (this.#config.overflowStrategy) {
2987
+ case "drop-oldest": {
2988
+ const removed = buffer.messages.shift();
2989
+ buffer.totalBytes -= removed.size;
2990
+ buffer.totalDropped++;
2991
+ break;
2992
+ }
2993
+ case "drop-newest":
2994
+ buffer.totalDropped++;
2995
+ return false;
2996
+ case "reject":
2997
+ return false;
2998
+ }
2999
+ }
3000
+ buffer.messages.push({
3001
+ envelope,
3002
+ enqueuedAt: Date.now(),
3003
+ size: messageSize
3004
+ });
3005
+ buffer.totalEnqueued++;
3006
+ buffer.totalBytes += messageSize;
3007
+ return true;
3008
+ }
3009
+ /**
3010
+ * Drain all buffered messages for a peer.
3011
+ *
3012
+ * Returns messages in FIFO order and clears the buffer.
3013
+ *
3014
+ * @param peerId - Target peer system ID
3015
+ * @returns Array of buffered envelopes
3016
+ */
3017
+ drain(peerId) {
3018
+ const buffer = this.#buffers.get(peerId);
3019
+ if (!buffer) return [];
3020
+ this.#evictExpired(buffer);
3021
+ const messages = buffer.messages.map((m) => m.envelope);
3022
+ buffer.messages = [];
3023
+ buffer.totalBytes = 0;
3024
+ return messages;
3025
+ }
3026
+ /**
3027
+ * Peek at buffered messages without removing them.
3028
+ *
3029
+ * @param peerId - Target peer system ID
3030
+ * @returns Array of buffered envelopes (still in buffer)
3031
+ */
3032
+ peek(peerId) {
3033
+ const buffer = this.#buffers.get(peerId);
3034
+ if (!buffer) return [];
3035
+ this.#evictExpired(buffer);
3036
+ return buffer.messages.map((m) => m.envelope);
3037
+ }
3038
+ /**
3039
+ * Get statistics for all peer buffers.
3040
+ *
3041
+ * @returns Map of peer ID to buffer stats
3042
+ */
3043
+ stats() {
3044
+ const result = /* @__PURE__ */ new Map();
3045
+ const now = Date.now();
3046
+ for (const [peerId, buffer] of this.#buffers) {
3047
+ this.#evictExpired(buffer);
3048
+ const oldestAge = buffer.messages.length > 0 ? now - buffer.messages[0].enqueuedAt : 0;
3049
+ result.set(peerId, {
3050
+ count: buffer.messages.length,
3051
+ oldestAge,
3052
+ totalEnqueued: buffer.totalEnqueued,
3053
+ totalDropped: buffer.totalDropped,
3054
+ totalBytes: buffer.totalBytes
3055
+ });
3056
+ }
3057
+ return result;
3058
+ }
3059
+ /**
3060
+ * Get count for a specific peer.
3061
+ *
3062
+ * @param peerId - Target peer system ID
3063
+ * @returns Number of buffered messages
3064
+ */
3065
+ count(peerId) {
3066
+ const buffer = this.#buffers.get(peerId);
3067
+ if (!buffer) return 0;
3068
+ this.#evictExpired(buffer);
3069
+ return buffer.messages.length;
3070
+ }
3071
+ /**
3072
+ * Check if buffer has messages for a peer.
3073
+ *
3074
+ * @param peerId - Target peer system ID
3075
+ * @returns true if there are buffered messages
3076
+ */
3077
+ has(peerId) {
3078
+ return this.count(peerId) > 0;
3079
+ }
3080
+ /**
3081
+ * Clear buffer for a specific peer.
3082
+ *
3083
+ * @param peerId - Target peer system ID
3084
+ */
3085
+ clear(peerId) {
3086
+ this.#buffers.delete(peerId);
3087
+ }
3088
+ /**
3089
+ * Clear all buffers.
3090
+ */
3091
+ clearAll() {
3092
+ this.#buffers.clear();
3093
+ }
3094
+ /**
3095
+ * Get list of peers with buffered messages.
3096
+ *
3097
+ * @returns Array of peer IDs
3098
+ */
3099
+ peers() {
3100
+ const result = [];
3101
+ for (const [peerId, buffer] of this.#buffers) {
3102
+ this.#evictExpired(buffer);
3103
+ if (buffer.messages.length > 0) {
3104
+ result.push(peerId);
3105
+ }
3106
+ }
3107
+ return result;
3108
+ }
3109
+ /**
3110
+ * Evict expired messages from a buffer.
3111
+ */
3112
+ #evictExpired(buffer) {
3113
+ const cutoff = Date.now() - this.#config.retentionMs;
3114
+ let removed = 0;
3115
+ let bytesRemoved = 0;
3116
+ while (buffer.messages.length > 0 && buffer.messages[0].enqueuedAt < cutoff) {
3117
+ const msg = buffer.messages.shift();
3118
+ bytesRemoved += msg.size;
3119
+ removed++;
3120
+ }
3121
+ buffer.totalDropped += removed;
3122
+ buffer.totalBytes -= bytesRemoved;
3123
+ }
3124
+ /**
3125
+ * Estimate the size of an envelope in bytes.
3126
+ */
3127
+ #estimateSize(envelope) {
3128
+ try {
3129
+ return JSON.stringify(envelope).length * 2;
3130
+ } catch {
3131
+ return 1024;
3132
+ }
3133
+ }
3134
+ };
3135
+
3136
+ // src/connection/gateway.ts
3137
+ var GatewayConnection = class {
3138
+ #connection;
3139
+ #options;
3140
+ #connectedSystems = /* @__PURE__ */ new Map();
3141
+ #reconnectionHandlers = /* @__PURE__ */ new Set();
3142
+ #outageBuffer;
3143
+ #lastSyncTimestamps = /* @__PURE__ */ new Map();
3144
+ #sessionId = null;
3145
+ #serverCapabilities = null;
3146
+ #connected = false;
3147
+ #isReconnecting = false;
3148
+ #lastConnectOptions;
3149
+ constructor(stream, options = {}) {
3150
+ this.#connection = new BaseConnection(stream, options);
3151
+ this.#options = options;
3152
+ this.#outageBuffer = options.buffer?.enabled ? new FederationOutageBuffer(options.buffer) : null;
3153
+ if (options.reconnection?.enabled && options.createStream) {
3154
+ this.#connection.onStateChange((newState) => {
3155
+ if (newState === "closed" && this.#connected && !this.#isReconnecting) {
3156
+ void this.#handleDisconnect();
3157
+ }
3158
+ });
3159
+ }
3160
+ }
3161
+ // ===========================================================================
3162
+ // Connection Lifecycle
3163
+ // ===========================================================================
3164
+ /**
3165
+ * Connect to the local MAP system
3166
+ */
3167
+ async connect(options) {
3168
+ const params = {
3169
+ protocolVersion: PROTOCOL_VERSION,
3170
+ participantType: "gateway",
3171
+ name: this.#options.name,
3172
+ capabilities: this.#options.capabilities,
3173
+ auth: options?.auth
3174
+ };
3175
+ const result = await this.#connection.sendRequest(CORE_METHODS.CONNECT, params);
3176
+ this.#sessionId = result.sessionId;
3177
+ this.#serverCapabilities = result.capabilities;
3178
+ this.#connected = true;
3179
+ this.#connection._transitionTo("connected");
3180
+ this.#lastConnectOptions = options;
3181
+ return result;
3182
+ }
3183
+ /**
3184
+ * Disconnect from the local MAP system
3185
+ */
3186
+ async disconnect(reason) {
3187
+ if (!this.#connected) return;
3188
+ try {
3189
+ await this.#connection.sendRequest(
3190
+ CORE_METHODS.DISCONNECT,
3191
+ reason ? { reason } : void 0
3192
+ );
3193
+ } finally {
3194
+ await this.#connection.close();
3195
+ this.#connected = false;
3196
+ }
3197
+ }
3198
+ /**
3199
+ * Whether the gateway is connected to the local system
3200
+ */
3201
+ get isConnected() {
3202
+ return this.#connected && !this.#connection.isClosed;
3203
+ }
3204
+ /**
3205
+ * Current session ID
3206
+ */
3207
+ get sessionId() {
3208
+ return this.#sessionId;
3209
+ }
3210
+ /**
3211
+ * Server capabilities
3212
+ */
3213
+ get serverCapabilities() {
3214
+ return this.#serverCapabilities;
3215
+ }
3216
+ /**
3217
+ * List of connected remote systems
3218
+ */
3219
+ get connectedSystems() {
3220
+ return new Map(this.#connectedSystems);
3221
+ }
3222
+ /**
3223
+ * AbortSignal that triggers when the connection closes
3224
+ */
3225
+ get signal() {
3226
+ return this.#connection.signal;
3227
+ }
3228
+ /**
3229
+ * Promise that resolves when the connection closes
3230
+ */
3231
+ get closed() {
3232
+ return this.#connection.closed;
3233
+ }
3234
+ // ===========================================================================
3235
+ // Federation
3236
+ // ===========================================================================
3237
+ /**
3238
+ * Connect to a remote MAP system
3239
+ */
3240
+ async connectToSystem(systemId, endpoint, auth) {
3241
+ const params = {
3242
+ systemId,
3243
+ endpoint,
3244
+ auth
3245
+ };
3246
+ const result = await this.#connection.sendRequest(FEDERATION_METHODS.FEDERATION_CONNECT, params);
3247
+ if (result.connected && result.systemInfo) {
3248
+ this.#connectedSystems.set(systemId, {
3249
+ name: result.systemInfo.name,
3250
+ version: result.systemInfo.version
3251
+ });
3252
+ }
3253
+ return result;
3254
+ }
3255
+ /**
3256
+ * Route a message to a remote system.
3257
+ *
3258
+ * If routing config is provided, wraps the message in a federation envelope
3259
+ * with proper metadata for multi-hop routing. Otherwise, sends raw message
3260
+ * for backwards compatibility.
3261
+ *
3262
+ * During reconnection, messages are buffered if buffer is configured.
3263
+ */
3264
+ async routeToSystem(systemId, message) {
3265
+ let envelope;
3266
+ if (this.#options.routing) {
3267
+ envelope = createFederationEnvelope(
3268
+ message,
3269
+ this.#options.routing.systemId,
3270
+ systemId,
3271
+ {
3272
+ maxHops: this.#options.routing.maxHops,
3273
+ trackPath: this.#options.routing.trackPath
3274
+ }
3275
+ );
3276
+ }
3277
+ if (this.#isReconnecting && this.#outageBuffer && envelope) {
3278
+ const buffered = this.#outageBuffer.enqueue(systemId, envelope);
3279
+ if (!buffered) {
3280
+ this.#emitReconnectionEvent({
3281
+ type: "bufferOverflow",
3282
+ peerId: systemId,
3283
+ messagesBuffered: this.#outageBuffer.count(systemId)
3284
+ });
3285
+ }
3286
+ return { routed: false };
3287
+ }
3288
+ const params = { systemId };
3289
+ if (envelope) {
3290
+ params.envelope = envelope;
3291
+ } else {
3292
+ params.message = message;
3293
+ }
3294
+ const result = await this.#connection.sendRequest(FEDERATION_METHODS.FEDERATION_ROUTE, params);
3295
+ if (result.routed) {
3296
+ this.#lastSyncTimestamps.set(systemId, Date.now());
3297
+ }
3298
+ return result;
3299
+ }
3300
+ /**
3301
+ * Check if a remote system is connected
3302
+ */
3303
+ isSystemConnected(systemId) {
3304
+ return this.#connectedSystems.has(systemId);
3305
+ }
3306
+ // ===========================================================================
3307
+ // Reconnection
3308
+ // ===========================================================================
3309
+ /**
3310
+ * Current connection state
3311
+ */
3312
+ get state() {
3313
+ return this.#connection.state;
3314
+ }
3315
+ /**
3316
+ * Whether the connection is currently reconnecting
3317
+ */
3318
+ get isReconnecting() {
3319
+ return this.#isReconnecting;
3320
+ }
3321
+ /**
3322
+ * Get the outage buffer (for advanced use)
3323
+ */
3324
+ get outageBuffer() {
3325
+ return this.#outageBuffer;
3326
+ }
3327
+ /**
3328
+ * Get last sync timestamp for a peer
3329
+ */
3330
+ getLastSyncTimestamp(peerId) {
3331
+ return this.#lastSyncTimestamps.get(peerId);
3332
+ }
3333
+ /**
3334
+ * Register a handler for reconnection events.
3335
+ *
3336
+ * @param handler - Function called when reconnection events occur
3337
+ * @returns Unsubscribe function to remove the handler
3338
+ */
3339
+ onReconnection(handler) {
3340
+ this.#reconnectionHandlers.add(handler);
3341
+ return () => this.#reconnectionHandlers.delete(handler);
3342
+ }
3343
+ /**
3344
+ * Register a handler for connection state changes.
3345
+ *
3346
+ * @param handler - Function called when state changes
3347
+ * @returns Unsubscribe function to remove the handler
3348
+ */
3349
+ onStateChange(handler) {
3350
+ return this.#connection.onStateChange(handler);
3351
+ }
3352
+ // ===========================================================================
3353
+ // Internal
3354
+ // ===========================================================================
3355
+ /**
3356
+ * Emit a reconnection event to all registered handlers
3357
+ */
3358
+ #emitReconnectionEvent(event) {
3359
+ for (const handler of this.#reconnectionHandlers) {
3360
+ try {
3361
+ handler(event);
3362
+ } catch (error) {
3363
+ console.error("MAP: Gateway reconnection event handler error:", error);
3364
+ }
3365
+ }
3366
+ }
3367
+ /**
3368
+ * Handle disconnect when auto-reconnect is enabled
3369
+ */
3370
+ async #handleDisconnect() {
3371
+ this.#isReconnecting = true;
3372
+ this.#connected = false;
3373
+ this.#emitReconnectionEvent({ type: "disconnected" });
3374
+ try {
3375
+ await this.#attemptReconnect();
3376
+ } catch (error) {
3377
+ this.#isReconnecting = false;
3378
+ this.#emitReconnectionEvent({
3379
+ type: "reconnectFailed",
3380
+ error: error instanceof Error ? error : new Error(String(error))
3381
+ });
3382
+ }
3383
+ }
3384
+ /**
3385
+ * Attempt to reconnect with retry logic
3386
+ */
3387
+ async #attemptReconnect() {
3388
+ const options = this.#options.reconnection;
3389
+ const createStream = this.#options.createStream;
3390
+ const retryPolicy = {
3391
+ maxRetries: options.maxRetries ?? DEFAULT_RETRY_POLICY.maxRetries,
3392
+ baseDelayMs: options.baseDelayMs ?? DEFAULT_RETRY_POLICY.baseDelayMs,
3393
+ maxDelayMs: options.maxDelayMs ?? DEFAULT_RETRY_POLICY.maxDelayMs,
3394
+ jitter: options.jitter ?? DEFAULT_RETRY_POLICY.jitter
3395
+ };
3396
+ await withRetry(
3397
+ async () => {
3398
+ const newStream = await createStream();
3399
+ await this.#connection.reconnect(newStream);
3400
+ const connectResult = await this.connect(this.#lastConnectOptions);
3401
+ this.#sessionId = connectResult.sessionId;
3402
+ this.#serverCapabilities = connectResult.capabilities;
3403
+ },
3404
+ retryPolicy,
3405
+ {
3406
+ onRetry: (state) => {
3407
+ this.#emitReconnectionEvent({
3408
+ type: "reconnecting",
3409
+ attempt: state.attempt,
3410
+ delay: state.nextDelayMs,
3411
+ error: state.lastError
3412
+ });
3413
+ }
3414
+ }
3415
+ );
3416
+ this.#isReconnecting = false;
3417
+ this.#emitReconnectionEvent({ type: "reconnected" });
3418
+ await this.#drainBufferedMessages();
3419
+ await this.#replayFromPeers();
3420
+ }
3421
+ /**
3422
+ * Drain buffered messages after reconnection
3423
+ */
3424
+ async #drainBufferedMessages() {
3425
+ if (!this.#outageBuffer) return;
3426
+ const peers = this.#outageBuffer.peers();
3427
+ for (const peerId of peers) {
3428
+ const messages = this.#outageBuffer.drain(peerId);
3429
+ if (messages.length === 0) continue;
3430
+ for (const envelope of messages) {
3431
+ try {
3432
+ const params = {
3433
+ systemId: peerId,
3434
+ envelope
3435
+ };
3436
+ await this.#connection.sendRequest(FEDERATION_METHODS.FEDERATION_ROUTE, params);
3437
+ } catch (error) {
3438
+ console.warn("MAP: Failed to send buffered message to", peerId, error);
3439
+ }
3440
+ }
3441
+ this.#emitReconnectionEvent({
3442
+ type: "bufferDrained",
3443
+ peerId,
3444
+ messagesDrained: messages.length
3445
+ });
3446
+ }
3447
+ }
3448
+ /**
3449
+ * Replay missed events from peers after reconnection
3450
+ */
3451
+ async #replayFromPeers() {
3452
+ const replayOptions = this.#options.replay;
3453
+ if (!replayOptions?.enabled) return;
3454
+ const peersToReplay = Array.from(this.#lastSyncTimestamps.entries());
3455
+ if (peersToReplay.length === 0) return;
3456
+ for (const [peerId, lastSync] of peersToReplay) {
3457
+ try {
3458
+ await this.#replayFromPeer(peerId, lastSync);
3459
+ } catch (error) {
3460
+ console.warn("MAP: Failed to replay events from peer", peerId, error);
3461
+ }
3462
+ }
3463
+ }
3464
+ /**
3465
+ * Replay missed events from a single peer
3466
+ */
3467
+ async #replayFromPeer(peerId, lastSyncTimestamp) {
3468
+ const replayOptions = this.#options.replay;
3469
+ const maxAge = replayOptions.maxAgeMs ?? 60 * 60 * 1e3;
3470
+ const cutoff = Date.now() - maxAge;
3471
+ if (lastSyncTimestamp < cutoff) {
3472
+ return;
3473
+ }
3474
+ this.#emitReconnectionEvent({ type: "replayStarted", peerId });
3475
+ let totalReplayed = 0;
3476
+ const maxEvents = replayOptions.maxEvents ?? 1e3;
3477
+ let afterEventId;
3478
+ let hasMore = true;
3479
+ while (hasMore && totalReplayed < maxEvents) {
3480
+ const params = {
3481
+ limit: Math.min(100, maxEvents - totalReplayed)
3482
+ };
3483
+ if (afterEventId) {
3484
+ params.afterEventId = afterEventId;
3485
+ } else {
3486
+ params.fromTimestamp = lastSyncTimestamp;
3487
+ }
3488
+ if (replayOptions.eventTypes) {
3489
+ params.filter = { eventTypes: replayOptions.eventTypes };
3490
+ }
3491
+ const result = await this.#connection.sendRequest(CORE_METHODS.REPLAY, params);
3492
+ totalReplayed += result.events.length;
3493
+ hasMore = result.hasMore;
3494
+ afterEventId = result.events.at(-1)?.eventId;
3495
+ if (result.events.length === 0) {
3496
+ break;
3497
+ }
3498
+ const lastEvent = result.events.at(-1);
3499
+ if (lastEvent) {
3500
+ this.#lastSyncTimestamps.set(peerId, lastEvent.timestamp);
3501
+ }
3502
+ }
3503
+ this.#emitReconnectionEvent({
3504
+ type: "replayCompleted",
3505
+ peerId,
3506
+ eventsReplayed: totalReplayed
3507
+ });
3508
+ }
3509
+ };
3510
+ var JsonRpcVersionSchema = z.literal("2.0");
3511
+ var RequestIdSchema = z.union([z.string(), z.number().int()]);
3512
+ var ProtocolVersionSchema = z.literal(1);
3513
+ var TimestampSchema = z.number().int();
3514
+ var MetaSchema = z.record(z.unknown()).optional();
3515
+ var ParticipantIdSchema = z.string();
3516
+ var AgentIdSchema = z.string();
3517
+ var ScopeIdSchema = z.string();
3518
+ var SessionIdSchema = z.string();
3519
+ var MessageIdSchema = z.string();
3520
+ var SubscriptionIdSchema = z.string();
3521
+ var CorrelationIdSchema = z.string();
3522
+ var ParticipantTypeSchema = z.enum(["agent", "client", "system", "gateway"]);
3523
+ var TransportTypeSchema = z.enum(["websocket", "stdio", "inprocess", "http-sse"]);
3524
+ var ErrorCategorySchema = z.enum([
3525
+ "protocol",
3526
+ "auth",
3527
+ "routing",
3528
+ "agent",
3529
+ "resource",
3530
+ "federation",
3531
+ "internal"
3532
+ ]);
3533
+ var AgentVisibilitySchema = z.enum(["public", "parent-only", "scope", "system"]);
3534
+ var AgentStateSchema = z.union([
3535
+ z.enum(["registered", "idle", "busy", "waiting", "stopping", "stopped", "error"]),
3536
+ z.string().regex(/^x-/)
3537
+ ]);
3538
+ var MessagePrioritySchema = z.enum(["low", "normal", "high", "urgent"]);
3539
+ var DeliverySemanticsSchema = z.enum(["at-most-once", "at-least-once", "exactly-once"]);
3540
+ var MessageRelationshipSchema = z.enum([
3541
+ "peer",
3542
+ "parent-to-child",
3543
+ "child-to-parent",
3544
+ "supervisor-to-supervised",
3545
+ "broadcast"
3546
+ ]);
3547
+ var EventTypeSchema = z.enum([
3548
+ "agent.registered",
3549
+ "agent.unregistered",
3550
+ "agent.state-changed",
3551
+ "agent.spawned",
3552
+ "scope.created",
3553
+ "scope.deleted",
3554
+ "scope.joined",
3555
+ "scope.left",
3556
+ "message.sent",
3557
+ "message.delivered",
3558
+ "session.started",
3559
+ "session.ended",
3560
+ "system.error",
3561
+ "system.shutdown"
3562
+ ]);
3563
+ var ScopeJoinPolicySchema = z.enum(["open", "approval", "invite"]);
3564
+ var ScopeVisibilitySchema = z.enum(["public", "private", "unlisted"]);
3565
+ var MessageVisibilitySchema = z.enum(["members", "public"]);
3566
+ var ScopeSendPolicySchema = z.enum(["anyone", "members"]);
3567
+ var ParticipantCapabilitiesSchema = z.object({
3568
+ observation: z.object({
3569
+ canObserve: z.boolean().optional(),
3570
+ canQuery: z.boolean().optional()
3571
+ }).strict().optional(),
3572
+ messaging: z.object({
3573
+ canSend: z.boolean().optional(),
3574
+ canReceive: z.boolean().optional(),
3575
+ canBroadcast: z.boolean().optional()
3576
+ }).strict().optional(),
3577
+ lifecycle: z.object({
3578
+ canSpawn: z.boolean().optional(),
3579
+ canRegister: z.boolean().optional(),
3580
+ canUnregister: z.boolean().optional(),
3581
+ canSteer: z.boolean().optional(),
3582
+ canStop: z.boolean().optional()
3583
+ }).strict().optional(),
3584
+ scopes: z.object({
3585
+ canCreateScopes: z.boolean().optional(),
3586
+ canManageScopes: z.boolean().optional()
3587
+ }).strict().optional(),
3588
+ _meta: MetaSchema
3589
+ }).strict();
3590
+ var DirectAddressSchema = z.object({ agent: AgentIdSchema }).strict();
3591
+ var MultiAddressSchema = z.object({ agents: z.array(AgentIdSchema).min(1) }).strict();
3592
+ var ScopeAddressSchema = z.object({ scope: ScopeIdSchema }).strict();
3593
+ var RoleAddressSchema = z.object({
3594
+ role: z.string(),
3595
+ scope: ScopeIdSchema.optional()
3596
+ }).strict();
3597
+ var HierarchicalAddressSchema = z.object({
3598
+ parent: z.literal(true).optional(),
3599
+ children: z.literal(true).optional(),
3600
+ siblings: z.literal(true).optional(),
3601
+ ancestors: z.literal(true).optional(),
3602
+ descendants: z.literal(true).optional()
3603
+ }).strict();
3604
+ var BroadcastAddressSchema = z.object({ broadcast: z.literal(true) }).strict();
3605
+ var SystemAddressSchema = z.object({ system: z.literal(true) }).strict();
3606
+ var ParticipantAddressSchema = z.object({ participant: ParticipantIdSchema }).strict();
3607
+ var FederatedAddressSchema = z.object({
3608
+ system: z.string(),
3609
+ address: z.lazy(() => AddressSchema)
3610
+ }).strict();
3611
+ var AddressSchema = z.union([
3612
+ DirectAddressSchema,
3613
+ MultiAddressSchema,
3614
+ ScopeAddressSchema,
3615
+ RoleAddressSchema,
3616
+ HierarchicalAddressSchema,
3617
+ BroadcastAddressSchema,
3618
+ SystemAddressSchema,
3619
+ ParticipantAddressSchema,
3620
+ FederatedAddressSchema
3621
+ ]);
3622
+ var AgentRelationshipSchema = z.object({
3623
+ type: z.enum(["peer", "supervisor", "supervised", "collaborator"]),
3624
+ agentId: AgentIdSchema,
3625
+ metadata: z.record(z.unknown()).optional(),
3626
+ _meta: MetaSchema
3627
+ }).strict();
3628
+ var AgentLifecycleSchema = z.object({
3629
+ createdAt: TimestampSchema.optional(),
3630
+ startedAt: TimestampSchema.optional(),
3631
+ stoppedAt: TimestampSchema.optional(),
3632
+ lastActiveAt: TimestampSchema.optional(),
3633
+ exitCode: z.number().int().optional(),
3634
+ exitReason: z.string().optional(),
3635
+ _meta: MetaSchema
3636
+ }).strict();
3637
+ var AgentSchema = z.object({
3638
+ id: AgentIdSchema,
3639
+ name: z.string().optional(),
3640
+ description: z.string().optional(),
3641
+ parent: AgentIdSchema.optional(),
3642
+ relationships: z.array(AgentRelationshipSchema).optional(),
3643
+ state: AgentStateSchema,
3644
+ role: z.string().optional(),
3645
+ scopes: z.array(ScopeIdSchema).optional(),
3646
+ visibility: AgentVisibilitySchema.optional(),
3647
+ lifecycle: AgentLifecycleSchema.optional(),
3648
+ capabilities: ParticipantCapabilitiesSchema.optional(),
3649
+ metadata: z.record(z.unknown()).optional(),
3650
+ _meta: MetaSchema
3651
+ }).strict();
3652
+ var ScopeSchema = z.object({
3653
+ id: ScopeIdSchema,
3654
+ name: z.string().optional(),
3655
+ parent: ScopeIdSchema.optional(),
3656
+ children: z.array(ScopeIdSchema).optional(),
3657
+ joinPolicy: ScopeJoinPolicySchema.optional(),
3658
+ visibility: ScopeVisibilitySchema.optional(),
3659
+ messageVisibility: MessageVisibilitySchema.optional(),
3660
+ sendPolicy: ScopeSendPolicySchema.optional(),
3661
+ metadata: z.record(z.unknown()).optional(),
3662
+ _meta: MetaSchema
3663
+ }).strict();
3664
+ var MessageMetaSchema = z.object({
3665
+ correlationId: CorrelationIdSchema.optional(),
3666
+ causationId: MessageIdSchema.optional(),
3667
+ traceId: z.string().optional(),
3668
+ spanId: z.string().optional(),
3669
+ priority: MessagePrioritySchema.optional(),
3670
+ delivery: DeliverySemanticsSchema.optional(),
3671
+ relationship: MessageRelationshipSchema.optional(),
3672
+ expiresAt: TimestampSchema.optional(),
3673
+ isResult: z.boolean().optional(),
3674
+ _meta: MetaSchema
3675
+ }).strict();
3676
+ var MessageSchema = z.object({
3677
+ id: MessageIdSchema,
3678
+ from: ParticipantIdSchema,
3679
+ to: AddressSchema,
3680
+ timestamp: TimestampSchema,
3681
+ payload: z.unknown().optional(),
3682
+ meta: MessageMetaSchema.optional(),
3683
+ _meta: MetaSchema
3684
+ }).strict();
3685
+ var EventSchema = z.object({
3686
+ type: EventTypeSchema,
3687
+ timestamp: TimestampSchema,
3688
+ data: z.record(z.unknown()).optional(),
3689
+ _meta: MetaSchema
3690
+ }).strict();
3691
+ var SubscriptionFilterSchema = z.object({
3692
+ eventTypes: z.array(EventTypeSchema).optional(),
3693
+ scopes: z.array(ScopeIdSchema).optional(),
3694
+ agents: z.array(AgentIdSchema).optional(),
3695
+ includeChildren: z.boolean().optional(),
3696
+ _meta: MetaSchema
3697
+ }).strict();
3698
+ var MAPErrorDataSchema = z.object({
3699
+ category: ErrorCategorySchema.optional(),
3700
+ retryable: z.boolean().optional(),
3701
+ retryAfterMs: z.number().int().optional(),
3702
+ details: z.record(z.unknown()).optional(),
3703
+ _meta: MetaSchema
3704
+ }).passthrough();
3705
+ var MAPErrorSchema = z.object({
3706
+ code: z.number().int(),
3707
+ message: z.string(),
3708
+ data: MAPErrorDataSchema.optional()
3709
+ }).strict();
3710
+ var MAPRequestSchema = z.object({
3711
+ jsonrpc: JsonRpcVersionSchema,
3712
+ id: RequestIdSchema,
3713
+ method: z.string(),
3714
+ params: z.record(z.unknown()).optional()
3715
+ }).strict();
3716
+ var MAPResponseSuccessSchema = z.object({
3717
+ jsonrpc: JsonRpcVersionSchema,
3718
+ id: RequestIdSchema,
3719
+ result: z.unknown()
3720
+ }).strict();
3721
+ var MAPResponseErrorSchema = z.object({
3722
+ jsonrpc: JsonRpcVersionSchema,
3723
+ id: RequestIdSchema,
3724
+ error: MAPErrorSchema
3725
+ }).strict();
3726
+ var MAPResponseSchema = z.union([MAPResponseSuccessSchema, MAPResponseErrorSchema]);
3727
+ var MAPNotificationSchema = z.object({
3728
+ jsonrpc: JsonRpcVersionSchema,
3729
+ method: z.string(),
3730
+ params: z.record(z.unknown()).optional()
3731
+ }).strict();
3732
+
3733
+ // src/protocol/index.ts
3734
+ var METHOD_REGISTRY = {
3735
+ // Core methods
3736
+ "connect": {
3737
+ method: "map/connect",
3738
+ category: "core",
3739
+ capabilities: [],
3740
+ description: "Establish connection to MAP system"
3741
+ },
3742
+ "disconnect": {
3743
+ method: "map/disconnect",
3744
+ category: "core",
3745
+ capabilities: [],
3746
+ description: "Disconnect from MAP system"
3747
+ },
3748
+ "send": {
3749
+ method: "map/send",
3750
+ category: "core",
3751
+ capabilities: ["messaging.canSend"],
3752
+ description: "Send a message to an address"
3753
+ },
3754
+ "subscribe": {
3755
+ method: "map/subscribe",
3756
+ category: "core",
3757
+ capabilities: ["observation.canObserve"],
3758
+ description: "Subscribe to event stream"
3759
+ },
3760
+ "unsubscribe": {
3761
+ method: "map/unsubscribe",
3762
+ category: "core",
3763
+ capabilities: ["observation.canObserve"],
3764
+ description: "Unsubscribe from event stream"
3765
+ },
3766
+ "replay": {
3767
+ method: "map/replay",
3768
+ category: "core",
3769
+ capabilities: ["observation.canObserve"],
3770
+ description: "Replay historical events with filtering and pagination"
3771
+ },
3772
+ // Observation methods
3773
+ "agents/list": {
3774
+ method: "map/agents/list",
3775
+ category: "observation",
3776
+ capabilities: ["observation.canQuery"],
3777
+ description: "List agents with optional filters"
3778
+ },
3779
+ "agents/get": {
3780
+ method: "map/agents/get",
3781
+ category: "observation",
3782
+ capabilities: ["observation.canQuery"],
3783
+ description: "Get agent by ID with optional hierarchy"
3784
+ },
3785
+ "scopes/list": {
3786
+ method: "map/scopes/list",
3787
+ category: "observation",
3788
+ capabilities: ["observation.canQuery"],
3789
+ description: "List all scopes"
3790
+ },
3791
+ "scopes/get": {
3792
+ method: "map/scopes/get",
3793
+ category: "observation",
3794
+ capabilities: ["observation.canQuery"],
3795
+ description: "Get scope by ID"
3796
+ },
3797
+ "scopes/members": {
3798
+ method: "map/scopes/members",
3799
+ category: "observation",
3800
+ capabilities: ["observation.canQuery"],
3801
+ description: "List scope members"
3802
+ },
3803
+ "structure/graph": {
3804
+ method: "map/structure/graph",
3805
+ category: "observation",
3806
+ capabilities: ["observation.canQuery"],
3807
+ description: "Get agent hierarchy graph"
3808
+ },
3809
+ // Lifecycle methods
3810
+ "agents/register": {
3811
+ method: "map/agents/register",
3812
+ category: "lifecycle",
3813
+ capabilities: ["lifecycle.canRegister"],
3814
+ description: "Register a new agent"
3815
+ },
3816
+ "agents/unregister": {
3817
+ method: "map/agents/unregister",
3818
+ category: "lifecycle",
3819
+ capabilities: ["lifecycle.canUnregister"],
3820
+ description: "Unregister an agent"
3821
+ },
3822
+ "agents/spawn": {
3823
+ method: "map/agents/spawn",
3824
+ category: "lifecycle",
3825
+ capabilities: ["lifecycle.canSpawn"],
3826
+ description: "Spawn a child agent"
3827
+ },
3828
+ // State methods
3829
+ "agents/update": {
3830
+ method: "map/agents/update",
3831
+ category: "state",
3832
+ capabilities: ["lifecycle.canRegister"],
3833
+ description: "Update agent state or metadata"
3834
+ },
3835
+ "agents/suspend": {
3836
+ method: "map/agents/suspend",
3837
+ category: "state",
3838
+ capabilities: ["lifecycle.canStop"],
3839
+ description: "Suspend an agent"
3840
+ },
3841
+ "agents/resume": {
3842
+ method: "map/agents/resume",
3843
+ category: "state",
3844
+ capabilities: ["lifecycle.canStop"],
3845
+ description: "Resume a suspended agent"
3846
+ },
3847
+ "agents/stop": {
3848
+ method: "map/agents/stop",
3849
+ category: "state",
3850
+ capabilities: ["lifecycle.canStop"],
3851
+ description: "Stop an agent"
3852
+ },
3853
+ // Steering methods
3854
+ "inject": {
3855
+ method: "map/inject",
3856
+ category: "steering",
3857
+ capabilities: ["lifecycle.canSteer"],
3858
+ description: "Inject context into an agent"
3859
+ },
3860
+ // Scope methods
3861
+ "scopes/create": {
3862
+ method: "map/scopes/create",
3863
+ category: "scope",
3864
+ capabilities: ["scopes.canCreateScopes"],
3865
+ description: "Create a new scope"
3866
+ },
3867
+ "scopes/delete": {
3868
+ method: "map/scopes/delete",
3869
+ category: "scope",
3870
+ capabilities: ["scopes.canManageScopes"],
3871
+ description: "Delete a scope"
3872
+ },
3873
+ "scopes/join": {
3874
+ method: "map/scopes/join",
3875
+ category: "scope",
3876
+ capabilities: [],
3877
+ description: "Join a scope"
3878
+ },
3879
+ "scopes/leave": {
3880
+ method: "map/scopes/leave",
3881
+ category: "scope",
3882
+ capabilities: [],
3883
+ description: "Leave a scope"
3884
+ },
3885
+ // Session methods
3886
+ "session/list": {
3887
+ method: "map/session/list",
3888
+ category: "session",
3889
+ capabilities: [],
3890
+ description: "List sessions"
3891
+ },
3892
+ "session/load": {
3893
+ method: "map/session/load",
3894
+ category: "session",
3895
+ capabilities: [],
3896
+ description: "Load a session"
3897
+ },
3898
+ "session/close": {
3899
+ method: "map/session/close",
3900
+ category: "session",
3901
+ capabilities: [],
3902
+ description: "Close a session"
3903
+ },
3904
+ // Auth methods
3905
+ "auth/refresh": {
3906
+ method: "map/auth/refresh",
3907
+ category: "auth",
3908
+ capabilities: [],
3909
+ description: "Refresh authentication token"
3910
+ },
3911
+ // Federation methods
3912
+ "federation/connect": {
3913
+ method: "map/federation/connect",
3914
+ category: "federation",
3915
+ capabilities: ["federation.canFederate"],
3916
+ description: "Connect to federated system"
3917
+ },
3918
+ "federation/route": {
3919
+ method: "map/federation/route",
3920
+ category: "federation",
3921
+ capabilities: ["federation.canFederate"],
3922
+ description: "Route message to federated system"
3923
+ },
3924
+ // Notification methods (client → server)
3925
+ "subscription/ack": {
3926
+ method: "map/subscribe.ack",
3927
+ category: "notification",
3928
+ capabilities: [],
3929
+ description: "Acknowledge received events for backpressure flow control"
3930
+ }
3931
+ };
3932
+ function getMethodsByCategory(category) {
3933
+ return Object.values(METHOD_REGISTRY).filter((m) => m.category === category);
3934
+ }
3935
+ function getRequiredCapabilities(methodName) {
3936
+ const info = METHOD_REGISTRY[methodName];
3937
+ if (info) return info.capabilities;
3938
+ const byWire = Object.values(METHOD_REGISTRY).find((m) => m.method === methodName);
3939
+ return byWire?.capabilities ?? [];
3940
+ }
3941
+ function hasRequiredCapabilities(methodName, capabilities) {
3942
+ const required = getRequiredCapabilities(methodName);
3943
+ if (required.length === 0) return true;
3944
+ for (const path of required) {
3945
+ const [category, capability] = path.split(".");
3946
+ const categoryCapabilities = capabilities[category];
3947
+ if (!categoryCapabilities?.[capability]) {
3948
+ return false;
3949
+ }
3950
+ }
3951
+ return true;
3952
+ }
3953
+ function getMethodInfo(wireMethod) {
3954
+ return Object.values(METHOD_REGISTRY).find((m) => m.method === wireMethod);
3955
+ }
3956
+ function buildConnectResponse(params) {
3957
+ return {
3958
+ protocolVersion: params.protocolVersion,
3959
+ sessionId: params.sessionId,
3960
+ participantId: params.participantId,
3961
+ capabilities: params.capabilities,
3962
+ systemInfo: params.systemInfo,
3963
+ reconnected: params.reconnected,
3964
+ reclaimedAgents: params.reclaimedAgents,
3965
+ ownedAgents: params.ownedAgents
3966
+ };
3967
+ }
3968
+ function buildDisconnectResponse(session) {
3969
+ return { session };
3970
+ }
3971
+ function buildSendResponse(messageId, delivered) {
3972
+ return { messageId, delivered };
3973
+ }
3974
+ function buildAgentsRegisterResponse(agent) {
3975
+ return { agent };
3976
+ }
3977
+ function buildAgentsUnregisterResponse(agent) {
3978
+ return { agent };
3979
+ }
3980
+ function buildAgentsListResponse(agents) {
3981
+ return { agents };
3982
+ }
3983
+ function buildAgentsGetResponse(agent, children, descendants) {
3984
+ const result = { agent };
3985
+ if (children) result.children = children;
3986
+ if (descendants) result.descendants = descendants;
3987
+ return result;
3988
+ }
3989
+ function buildAgentsUpdateResponse(agent) {
3990
+ return { agent };
3991
+ }
3992
+ function buildAgentsSpawnResponse(agent) {
3993
+ return { agent };
3994
+ }
3995
+ function buildScopesCreateResponse(scope) {
3996
+ return { scope };
3997
+ }
3998
+ function buildScopesListResponse(scopes) {
3999
+ return { scopes };
4000
+ }
4001
+ function buildScopesJoinResponse(scope, agent) {
4002
+ return { scope, agent };
4003
+ }
4004
+ function buildScopesLeaveResponse(scope, agent) {
4005
+ return { scope, agent };
4006
+ }
4007
+ function buildSubscribeResponse(subscriptionId) {
4008
+ return { subscriptionId };
4009
+ }
4010
+ function buildUnsubscribeResponse(subscriptionId, closedAt = Date.now()) {
4011
+ return {
4012
+ subscription: {
4013
+ id: subscriptionId,
4014
+ closedAt
4015
+ }
4016
+ };
4017
+ }
4018
+
4019
+ // src/permissions/index.ts
4020
+ function isAgentExposed(exposure, agentId) {
4021
+ if (!exposure?.agents) return true;
4022
+ const {
4023
+ publicByDefault = true,
4024
+ publicAgents = [],
4025
+ hiddenAgents = []
4026
+ } = exposure.agents;
4027
+ if (matchesPatterns(agentId, hiddenAgents)) return false;
4028
+ if (matchesPatterns(agentId, publicAgents)) return true;
4029
+ return publicByDefault;
4030
+ }
4031
+ function isEventTypeExposed(exposure, eventType) {
4032
+ if (!exposure?.events) return true;
4033
+ const { exposedTypes, hiddenTypes = [] } = exposure.events;
4034
+ if (hiddenTypes.includes(eventType)) return false;
4035
+ if (exposedTypes && !exposedTypes.includes(eventType)) return false;
4036
+ return true;
4037
+ }
4038
+ function isScopeExposed(exposure, scopeId) {
4039
+ if (!exposure?.scopes) return true;
4040
+ const {
4041
+ publicByDefault = true,
4042
+ publicScopes = [],
4043
+ hiddenScopes = []
4044
+ } = exposure.scopes;
4045
+ if (matchesPatterns(scopeId, hiddenScopes)) return false;
4046
+ if (matchesPatterns(scopeId, publicScopes)) return true;
4047
+ return publicByDefault;
4048
+ }
4049
+ function hasCapability(capabilities, path) {
4050
+ const [category, cap] = path.split(".");
4051
+ const categoryCapabilities = capabilities[category];
4052
+ return categoryCapabilities?.[cap] ?? false;
4053
+ }
4054
+ function canPerformMethod(method, capabilities) {
4055
+ return hasRequiredCapabilities(method, capabilities);
4056
+ }
4057
+ function canSeeScope(scope, participant, memberAgentIds = []) {
4058
+ const visibility = scope.visibility ?? "public";
4059
+ switch (visibility) {
4060
+ case "public":
4061
+ return true;
4062
+ case "members":
4063
+ return memberAgentIds.length > 0;
4064
+ case "system":
4065
+ return participant.type === "system";
4066
+ default:
4067
+ return false;
4068
+ }
4069
+ }
4070
+ function canSendToScope(scope, participant, memberAgentIds = []) {
4071
+ if (participant.type === "system") return true;
4072
+ const sendPolicy = scope.sendPolicy ?? "members";
4073
+ switch (sendPolicy) {
4074
+ case "any":
4075
+ return true;
4076
+ case "members":
4077
+ return memberAgentIds.length > 0;
4078
+ default:
4079
+ return false;
4080
+ }
4081
+ }
4082
+ function canJoinScope(scope, participantType, agentRole) {
4083
+ const joinPolicy = scope.joinPolicy ?? "open";
4084
+ switch (joinPolicy) {
4085
+ case "open":
4086
+ return true;
4087
+ case "invite":
4088
+ return false;
4089
+ case "role":
4090
+ if (!agentRole || !scope.autoJoinRoles) return false;
4091
+ return scope.autoJoinRoles.includes(agentRole);
4092
+ case "system":
4093
+ return participantType === "system";
4094
+ default:
4095
+ return false;
4096
+ }
4097
+ }
4098
+ function canSeeAgent(agent, participant, ownedAgentIds = []) {
4099
+ const visibility = agent.visibility ?? "public";
4100
+ switch (visibility) {
4101
+ case "public":
4102
+ return true;
4103
+ case "parent-only":
4104
+ if (ownedAgentIds.includes(agent.id)) return true;
4105
+ return agent.parent ? ownedAgentIds.includes(agent.parent) : false;
4106
+ case "scope":
4107
+ return true;
4108
+ case "system":
4109
+ return participant.type === "system";
4110
+ default:
4111
+ return false;
4112
+ }
4113
+ }
4114
+ function canMessageAgent(agent, participant, ownedAgentIds = []) {
4115
+ if (!canSeeAgent(agent, participant, ownedAgentIds)) {
4116
+ return false;
4117
+ }
4118
+ return true;
4119
+ }
4120
+ function canControlAgent(agent, participant, ownedAgentIds = []) {
4121
+ if (participant.type === "system") return true;
4122
+ if (ownedAgentIds.includes(agent.id)) return true;
4123
+ if (agent.parent && ownedAgentIds.includes(agent.parent)) return true;
4124
+ return false;
4125
+ }
4126
+ function canPerformAction(context, action) {
4127
+ if (action.target?.agentId) {
4128
+ if (!isAgentExposed(context.system.exposure, action.target.agentId)) {
4129
+ return {
4130
+ allowed: false,
4131
+ reason: "Agent not exposed by system configuration",
4132
+ layer: 1
4133
+ };
4134
+ }
4135
+ }
4136
+ if (action.target?.scopeId) {
4137
+ if (!isScopeExposed(context.system.exposure, action.target.scopeId)) {
4138
+ return {
4139
+ allowed: false,
4140
+ reason: "Scope not exposed by system configuration",
4141
+ layer: 1
4142
+ };
4143
+ }
4144
+ }
4145
+ if (action.target?.eventTypes) {
4146
+ for (const eventType of action.target.eventTypes) {
4147
+ if (!isEventTypeExposed(context.system.exposure, eventType)) {
4148
+ return {
4149
+ allowed: false,
4150
+ reason: `Event type '${eventType}' not exposed by system configuration`,
4151
+ layer: 1
4152
+ };
4153
+ }
4154
+ }
4155
+ }
4156
+ const requiredCaps = getRequiredCapabilities(action.method);
4157
+ for (const cap of requiredCaps) {
4158
+ if (!hasCapability(context.participant.capabilities, cap)) {
4159
+ return {
4160
+ allowed: false,
4161
+ reason: `Missing required capability: ${cap}`,
4162
+ layer: 2
4163
+ };
4164
+ }
4165
+ }
4166
+ return { allowed: true };
4167
+ }
4168
+ function filterVisibleAgents(agents, context) {
4169
+ const ownedAgentIds = context.ownedAgentIds ?? [];
4170
+ return agents.filter((agent) => {
4171
+ if (!isAgentExposed(context.system.exposure, agent.id)) {
4172
+ return false;
4173
+ }
4174
+ if (!canSeeAgent(agent, context.participant, ownedAgentIds)) {
4175
+ return false;
4176
+ }
4177
+ return true;
4178
+ });
4179
+ }
4180
+ function filterVisibleScopes(scopes, context) {
4181
+ const scopeMembership = context.scopeMembership ?? /* @__PURE__ */ new Map();
4182
+ return scopes.filter((scope) => {
4183
+ if (!isScopeExposed(context.system.exposure, scope.id)) {
4184
+ return false;
4185
+ }
4186
+ const memberAgentIds = scopeMembership.get(scope.id) ?? [];
4187
+ if (!canSeeScope(scope, context.participant, memberAgentIds)) {
4188
+ return false;
4189
+ }
4190
+ return true;
4191
+ });
4192
+ }
4193
+ function filterVisibleEvents(events, context) {
4194
+ return events.filter((event) => {
4195
+ if (!isEventTypeExposed(context.system.exposure, event.type)) {
4196
+ return false;
4197
+ }
4198
+ return true;
4199
+ });
4200
+ }
4201
+ function matchesPatterns(value, patterns) {
4202
+ return patterns.some((pattern) => matchGlob(value, pattern));
4203
+ }
4204
+ function matchGlob(value, pattern) {
4205
+ const escaped = pattern.replace(/[.+^${}()|[\]\\]/g, "\\$&").replace(/\*/g, ".*").replace(/\?/g, ".");
4206
+ const regex = new RegExp(`^${escaped}$`);
4207
+ return regex.test(value);
4208
+ }
4209
+ function deepClone(obj) {
4210
+ return JSON.parse(JSON.stringify(obj));
4211
+ }
4212
+ function deepMergePermissions(base, override) {
4213
+ const result = { ...base };
4214
+ if (override.canSee) {
4215
+ result.canSee = { ...base.canSee, ...override.canSee };
4216
+ }
4217
+ if (override.canMessage) {
4218
+ result.canMessage = { ...base.canMessage, ...override.canMessage };
4219
+ }
4220
+ if (override.acceptsFrom) {
4221
+ result.acceptsFrom = { ...base.acceptsFrom, ...override.acceptsFrom };
4222
+ }
4223
+ return result;
4224
+ }
4225
+ function mapVisibilityToRule(visibility) {
4226
+ switch (visibility) {
4227
+ case "public":
4228
+ return "all";
4229
+ case "parent-only":
4230
+ return "hierarchy";
4231
+ case "scope":
4232
+ return "scoped";
4233
+ case "system":
4234
+ return "direct";
4235
+ default:
4236
+ return "all";
4237
+ }
4238
+ }
4239
+ var DEFAULT_AGENT_PERMISSION_CONFIG = {
4240
+ defaultPermissions: {
4241
+ canSee: {
4242
+ agents: "all",
4243
+ scopes: "all",
4244
+ structure: "full"
4245
+ },
4246
+ canMessage: {
4247
+ agents: "all",
4248
+ scopes: "all"
4249
+ },
4250
+ acceptsFrom: {
4251
+ agents: "all",
4252
+ clients: "all",
4253
+ systems: "all"
4254
+ }
4255
+ },
4256
+ rolePermissions: {}
4257
+ };
4258
+ function resolveAgentPermissions(agent, config = DEFAULT_AGENT_PERMISSION_CONFIG) {
4259
+ let permissions = deepClone(config.defaultPermissions);
4260
+ if (agent.role && config.rolePermissions[agent.role]) {
4261
+ permissions = deepMergePermissions(permissions, config.rolePermissions[agent.role]);
4262
+ }
4263
+ if (agent.permissionOverrides) {
4264
+ permissions = deepMergePermissions(permissions, agent.permissionOverrides);
4265
+ }
4266
+ if (agent.visibility && !agent.permissionOverrides?.canSee?.agents) {
4267
+ permissions.canSee = permissions.canSee ?? {};
4268
+ permissions.canSee.agents = mapVisibilityToRule(agent.visibility);
4269
+ }
4270
+ return permissions;
4271
+ }
4272
+ function checkAgentAcceptance(rule, context) {
4273
+ if (!rule || rule === "all") return true;
4274
+ if (rule === "hierarchy") {
4275
+ return context.isParent === true || context.isChild === true || context.isAncestor === true || context.isDescendant === true;
4276
+ }
4277
+ if (rule === "scoped") {
4278
+ return (context.sharedScopes?.length ?? 0) > 0;
4279
+ }
4280
+ if (typeof rule === "object" && "include" in rule) {
4281
+ return context.senderAgentId !== void 0 && rule.include.includes(context.senderAgentId);
4282
+ }
4283
+ return false;
4284
+ }
4285
+ function checkClientAcceptance(rule, senderId) {
4286
+ if (!rule || rule === "all") return true;
4287
+ if (rule === "none") return false;
4288
+ if (typeof rule === "object" && "include" in rule) {
4289
+ return rule.include.includes(senderId);
4290
+ }
4291
+ return false;
4292
+ }
4293
+ function checkSystemAcceptance(rule, senderSystemId) {
4294
+ if (!rule || rule === "all") return true;
4295
+ if (rule === "none") return false;
4296
+ if (typeof rule === "object" && "include" in rule) {
4297
+ return senderSystemId !== void 0 && rule.include.includes(senderSystemId);
4298
+ }
4299
+ return false;
4300
+ }
4301
+ function canAgentAcceptMessage(targetAgent, context, config = DEFAULT_AGENT_PERMISSION_CONFIG) {
4302
+ const permissions = resolveAgentPermissions(targetAgent, config);
4303
+ const acceptsFrom = permissions.acceptsFrom;
4304
+ if (!acceptsFrom) return true;
4305
+ switch (context.senderType) {
4306
+ case "agent":
4307
+ return checkAgentAcceptance(acceptsFrom.agents, context);
4308
+ case "client":
4309
+ return checkClientAcceptance(acceptsFrom.clients, context.senderId);
4310
+ case "system":
4311
+ case "gateway":
4312
+ return checkSystemAcceptance(acceptsFrom.systems, context.senderSystemId);
4313
+ default:
4314
+ return false;
4315
+ }
4316
+ }
4317
+ function canAgentSeeAgent(viewerAgent, targetAgentId, context, config = DEFAULT_AGENT_PERMISSION_CONFIG) {
4318
+ const permissions = resolveAgentPermissions(viewerAgent, config);
4319
+ const canSee = permissions.canSee?.agents;
4320
+ if (!canSee || canSee === "all") return true;
4321
+ if (canSee === "hierarchy") {
4322
+ return context.isParent === true || context.isChild === true || context.isAncestor === true || context.isDescendant === true;
4323
+ }
4324
+ if (canSee === "scoped") {
4325
+ return (context.sharedScopes?.length ?? 0) > 0;
4326
+ }
4327
+ if (canSee === "direct") {
4328
+ return false;
4329
+ }
4330
+ if (typeof canSee === "object" && "include" in canSee) {
4331
+ return canSee.include.includes(targetAgentId);
4332
+ }
4333
+ return false;
4334
+ }
4335
+ function canAgentMessageAgent(senderAgent, targetAgentId, context, config = DEFAULT_AGENT_PERMISSION_CONFIG) {
4336
+ const permissions = resolveAgentPermissions(senderAgent, config);
4337
+ const canMessage = permissions.canMessage?.agents;
4338
+ if (!canMessage || canMessage === "all") return true;
4339
+ if (canMessage === "hierarchy") {
4340
+ return context.isParent === true || context.isChild === true || context.isAncestor === true || context.isDescendant === true;
4341
+ }
4342
+ if (canMessage === "scoped") {
4343
+ return (context.sharedScopes?.length ?? 0) > 0;
4344
+ }
4345
+ if (typeof canMessage === "object" && "include" in canMessage) {
4346
+ return canMessage.include.includes(targetAgentId);
4347
+ }
4348
+ return false;
4349
+ }
4350
+
4351
+ export { AGENT_ERROR_CODES, AUTH_ERROR_CODES, AUTH_METHODS, AddressSchema, AgentConnection, AgentIdSchema, AgentLifecycleSchema, AgentRelationshipSchema, AgentSchema, AgentStateSchema, AgentVisibilitySchema, BaseConnection, BroadcastAddressSchema, CAPABILITY_REQUIREMENTS, CORE_METHODS, CausalEventBuffer, ClientConnection, CorrelationIdSchema, DEFAULT_AGENT_PERMISSION_CONFIG, DEFAULT_RETRY_POLICY, DeliverySemanticsSchema, DirectAddressSchema, ERROR_CODES, EVENT_TYPES, EXTENSION_METHODS, ErrorCategorySchema, EventSchema, EventTypeSchema, FEDERATION_ERROR_CODES, FEDERATION_METHODS, FederatedAddressSchema, FederationOutageBuffer, GatewayConnection, HierarchicalAddressSchema, JSONRPC_VERSION, JsonRpcVersionSchema, LIFECYCLE_METHODS, MAPConnectionError, MAPErrorDataSchema, MAPErrorSchema, MAPNotificationSchema, MAPRequestError, MAPRequestSchema, MAPResponseErrorSchema, MAPResponseSchema, MAPResponseSuccessSchema, MAPTimeoutError, MAP_METHODS, METHOD_REGISTRY, MessageIdSchema, MessageMetaSchema, MessagePrioritySchema, MessageRelationshipSchema, MessageSchema, MessageVisibilitySchema, MetaSchema, MultiAddressSchema, NOTIFICATION_METHODS, OBSERVATION_METHODS, PERMISSION_METHODS, PROTOCOL_ERROR_CODES, PROTOCOL_VERSION, ParticipantAddressSchema, ParticipantCapabilitiesSchema, ParticipantIdSchema, ParticipantTypeSchema, ProtocolVersionSchema, RESOURCE_ERROR_CODES, ROUTING_ERROR_CODES, RequestIdSchema, RoleAddressSchema, SCOPE_METHODS, SESSION_METHODS, STATE_METHODS, STEERING_METHODS, STRUCTURE_METHODS, ScopeAddressSchema, ScopeIdSchema, ScopeJoinPolicySchema, ScopeSchema, ScopeSendPolicySchema, ScopeVisibilitySchema, SessionIdSchema, Subscription, SubscriptionFilterSchema, SubscriptionIdSchema, SystemAddressSchema, TimestampSchema, TransportTypeSchema, buildAgentsGetResponse, buildAgentsListResponse, buildAgentsRegisterResponse, buildAgentsSpawnResponse, buildAgentsUnregisterResponse, buildAgentsUpdateResponse, buildConnectResponse, buildDisconnectResponse, buildScopesCreateResponse, buildScopesJoinResponse, buildScopesLeaveResponse, buildScopesListResponse, buildSendResponse, buildSubscribeResponse, buildUnsubscribeResponse, calculateDelay, canAgentAcceptMessage, canAgentMessageAgent, canAgentSeeAgent, canControlAgent, canJoinScope, canMessageAgent, canPerformAction, canPerformMethod, canSeeAgent, canSeeScope, canSendToScope, compareUlid, createErrorResponse, createEvent, createFederationEnvelope, createNotification, createRequest, createRetryPolicy, createStreamPair, createSubscription, createSuccessResponse, deepMergePermissions, filterVisibleAgents, filterVisibleEvents, filterVisibleScopes, getEnvelopeRoutingInfo, getMethodInfo, getMethodsByCategory, getRequiredCapabilities, hasCapability, hasRequiredCapabilities, isAgentExposed, isBroadcastAddress, isDirectAddress, isEnvelopeAtDestination, isErrorResponse, isEventTypeExposed, isFederatedAddress, isHierarchicalAddress, isNotification, isOrphanedAgent, isRequest, isResponse, isScopeAddress, isScopeExposed, isSuccessResponse, isValidEnvelope, isValidUlid, mapVisibilityToRule, ndJsonStream, processFederationEnvelope, resolveAgentPermissions, retryable, sleep, sortCausalOrder, ulidTimestamp, unwrapEnvelope, validateCausalOrder, websocketStream, withPayload, withRetry };
4352
+ //# sourceMappingURL=index.js.map
4353
+ //# sourceMappingURL=index.js.map