@agentplat/mesh 0.3.0-alpha.1 → 0.3.0-alpha.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.
Files changed (102) hide show
  1. package/README.md +247 -2
  2. package/dist/coordination-allocation-contracts.d.ts +598 -0
  3. package/dist/coordination-allocation-contracts.d.ts.map +1 -0
  4. package/dist/coordination-allocation-contracts.js +2 -0
  5. package/dist/coordination-allocation-contracts.js.map +1 -0
  6. package/dist/coordination-allocation-state.d.ts +19 -0
  7. package/dist/coordination-allocation-state.d.ts.map +1 -0
  8. package/dist/coordination-allocation-state.js +5496 -0
  9. package/dist/coordination-allocation-state.js.map +1 -0
  10. package/dist/coordination-allocation.d.ts +10 -0
  11. package/dist/coordination-allocation.d.ts.map +1 -0
  12. package/dist/coordination-allocation.js +2743 -0
  13. package/dist/coordination-allocation.js.map +1 -0
  14. package/dist/coordination-contracts.d.ts +78 -0
  15. package/dist/coordination-contracts.d.ts.map +1 -0
  16. package/dist/coordination-contracts.js +2 -0
  17. package/dist/coordination-contracts.js.map +1 -0
  18. package/dist/coordination-discovery-contracts.d.ts +150 -0
  19. package/dist/coordination-discovery-contracts.d.ts.map +1 -0
  20. package/dist/coordination-discovery-contracts.js +2 -0
  21. package/dist/coordination-discovery-contracts.js.map +1 -0
  22. package/dist/coordination-discovery-state.d.ts +12 -0
  23. package/dist/coordination-discovery-state.d.ts.map +1 -0
  24. package/dist/coordination-discovery-state.js +542 -0
  25. package/dist/coordination-discovery-state.js.map +1 -0
  26. package/dist/coordination-discovery.d.ts +19 -0
  27. package/dist/coordination-discovery.d.ts.map +1 -0
  28. package/dist/coordination-discovery.js +850 -0
  29. package/dist/coordination-discovery.js.map +1 -0
  30. package/dist/coordination-execution.d.ts +6 -0
  31. package/dist/coordination-execution.d.ts.map +1 -0
  32. package/dist/coordination-execution.js +854 -0
  33. package/dist/coordination-execution.js.map +1 -0
  34. package/dist/coordination-inbound-contracts.d.ts +150 -0
  35. package/dist/coordination-inbound-contracts.d.ts.map +1 -0
  36. package/dist/coordination-inbound-contracts.js +2 -0
  37. package/dist/coordination-inbound-contracts.js.map +1 -0
  38. package/dist/coordination-inbound-state.d.ts +26 -0
  39. package/dist/coordination-inbound-state.d.ts.map +1 -0
  40. package/dist/coordination-inbound-state.js +331 -0
  41. package/dist/coordination-inbound-state.js.map +1 -0
  42. package/dist/coordination-inbound.d.ts +16 -0
  43. package/dist/coordination-inbound.d.ts.map +1 -0
  44. package/dist/coordination-inbound.js +944 -0
  45. package/dist/coordination-inbound.js.map +1 -0
  46. package/dist/coordination-lease-renewal.d.ts +8 -0
  47. package/dist/coordination-lease-renewal.d.ts.map +1 -0
  48. package/dist/coordination-lease-renewal.js +518 -0
  49. package/dist/coordination-lease-renewal.js.map +1 -0
  50. package/dist/coordination-objective-topic-contracts.d.ts +70 -0
  51. package/dist/coordination-objective-topic-contracts.d.ts.map +1 -0
  52. package/dist/coordination-objective-topic-contracts.js +2 -0
  53. package/dist/coordination-objective-topic-contracts.js.map +1 -0
  54. package/dist/coordination-objective-topic.d.ts +6 -0
  55. package/dist/coordination-objective-topic.d.ts.map +1 -0
  56. package/dist/coordination-objective-topic.js +561 -0
  57. package/dist/coordination-objective-topic.js.map +1 -0
  58. package/dist/coordination-objective-work-contracts.d.ts +212 -0
  59. package/dist/coordination-objective-work-contracts.d.ts.map +1 -0
  60. package/dist/coordination-objective-work-contracts.js +2 -0
  61. package/dist/coordination-objective-work-contracts.js.map +1 -0
  62. package/dist/coordination-objective-work-state.d.ts +10 -0
  63. package/dist/coordination-objective-work-state.d.ts.map +1 -0
  64. package/dist/coordination-objective-work-state.js +1075 -0
  65. package/dist/coordination-objective-work-state.js.map +1 -0
  66. package/dist/coordination-objective-work-time.d.ts +9 -0
  67. package/dist/coordination-objective-work-time.d.ts.map +1 -0
  68. package/dist/coordination-objective-work-time.js +48 -0
  69. package/dist/coordination-objective-work-time.js.map +1 -0
  70. package/dist/coordination-objective-work.d.ts +11 -0
  71. package/dist/coordination-objective-work.d.ts.map +1 -0
  72. package/dist/coordination-objective-work.js +1090 -0
  73. package/dist/coordination-objective-work.js.map +1 -0
  74. package/dist/coordination-recovery.d.ts +13 -0
  75. package/dist/coordination-recovery.d.ts.map +1 -0
  76. package/dist/coordination-recovery.js +951 -0
  77. package/dist/coordination-recovery.js.map +1 -0
  78. package/dist/coordination-reducer.d.ts +8 -0
  79. package/dist/coordination-reducer.d.ts.map +1 -0
  80. package/dist/coordination-reducer.js +81 -0
  81. package/dist/coordination-reducer.js.map +1 -0
  82. package/dist/coordination-state.d.ts +13 -0
  83. package/dist/coordination-state.d.ts.map +1 -0
  84. package/dist/coordination-state.js +335 -0
  85. package/dist/coordination-state.js.map +1 -0
  86. package/dist/coordination-topic-contracts.d.ts +82 -0
  87. package/dist/coordination-topic-contracts.d.ts.map +1 -0
  88. package/dist/coordination-topic-contracts.js +2 -0
  89. package/dist/coordination-topic-contracts.js.map +1 -0
  90. package/dist/coordination-topic.d.ts +6 -0
  91. package/dist/coordination-topic.d.ts.map +1 -0
  92. package/dist/coordination-topic.js +561 -0
  93. package/dist/coordination-topic.js.map +1 -0
  94. package/dist/coordination.d.ts +21 -0
  95. package/dist/coordination.d.ts.map +1 -0
  96. package/dist/coordination.js +21 -0
  97. package/dist/coordination.js.map +1 -0
  98. package/dist/sha256.d.ts +3 -0
  99. package/dist/sha256.d.ts.map +1 -0
  100. package/dist/sha256.js +117 -0
  101. package/dist/sha256.js.map +1 -0
  102. package/package.json +7 -3
@@ -0,0 +1,850 @@
1
+ import { compareMeshTimestamps, validateMeshEnvelopeContext, } from '@agentplat/mesh-protocol';
2
+ import { assertFrozenMeshDiscoveryState, createMeshDiscoveryRuntimeState, } from './coordination-discovery-state.js';
3
+ import { assertFrozenMeshCoordinationState } from './coordination-state.js';
4
+ import { assertMeshLogicalTime, createFrozenRecord, recordEntries, } from './state.js';
5
+ const payloadDigestPattern = /^[A-Za-z0-9_-]{43}$/;
6
+ const utf8Encoder = new TextEncoder();
7
+ /**
8
+ * Applies one already-verified discovery record. Signature verification and
9
+ * key resolution remain the responsibility of the next inbound-boundary slice.
10
+ */
11
+ export function evaluateVerifiedMeshDiscoveryEnvelope(state, request) {
12
+ assertRuntimeState(state);
13
+ if (!request || typeof request !== 'object') {
14
+ throw new TypeError('Mesh verified discovery request is required');
15
+ }
16
+ assertExactKeys(request, ['envelope', 'receivedAt', 'supportedCriticalExtensions', 'verifiedAt'], ['envelope', 'receivedAt', 'verifiedAt']);
17
+ if (request.supportedCriticalExtensions !== undefined &&
18
+ (!Array.isArray(request.supportedCriticalExtensions) ||
19
+ request.supportedCriticalExtensions.some((extension) => typeof extension !== 'string' || extension.length < 1) ||
20
+ new Set(request.supportedCriticalExtensions).size !==
21
+ request.supportedCriticalExtensions.length)) {
22
+ throw new TypeError('Mesh discovery supported critical extensions are invalid');
23
+ }
24
+ assertMeshLogicalTime(request.receivedAt);
25
+ if (request.receivedAt < state.coordination.lastLogicalTime ||
26
+ request.receivedAt < state.discovery.lastLogicalTime) {
27
+ throw new RangeError('Mesh discovery logical time cannot move backwards');
28
+ }
29
+ const envelope = request.envelope;
30
+ const contextFailure = validateContext(state, request);
31
+ if (contextFailure)
32
+ return rejection(state, contextFailure);
33
+ const commonFailure = validateCommon(state, envelope, request.verifiedAt);
34
+ if (commonFailure)
35
+ return rejection(state, commonFailure);
36
+ const metadata = domainMetadata(envelope);
37
+ const existing = state.coordination.domainRecords[metadata.recordKey];
38
+ if (existing) {
39
+ return existing.contentDigest === metadata.contentDigest &&
40
+ isExactProjectedDuplicate(state, envelope, existing.messageId)
41
+ ? Object.freeze({ accepted: true, duplicate: true, state })
42
+ : rejection(state, 'domain_record_conflict');
43
+ }
44
+ if (Object.keys(state.coordination.domainRecords).length >=
45
+ state.coordination.limits.maximumDomainRecords) {
46
+ return rejection(state, 'domain_capacity_exceeded');
47
+ }
48
+ if (state.coordination.journal.length >=
49
+ state.coordination.limits.maximumJournalEntries) {
50
+ return rejection(state, 'journal_capacity_exceeded');
51
+ }
52
+ const decision = evaluatePayload(state, request, metadata);
53
+ if ('code' in decision)
54
+ return rejection(state, decision.code);
55
+ return acceptRecord(state, request, metadata, decision.discovery);
56
+ }
57
+ /** Materializes expiry using only caller-supplied logical time. */
58
+ export function advanceMeshDiscoveryState(state, logicalTime) {
59
+ assertRuntimeState(state);
60
+ assertMeshLogicalTime(logicalTime);
61
+ if (logicalTime < state.coordination.lastLogicalTime ||
62
+ logicalTime < state.discovery.lastLogicalTime) {
63
+ throw new RangeError('Mesh discovery logical time cannot move backwards');
64
+ }
65
+ const expiredCardIds = new Set();
66
+ const peerCards = recordEntries(state.discovery.peerCards).map(([peerId, card]) => {
67
+ if (card.status !== 'active' || card.expiresAt > logicalTime) {
68
+ return [peerId, card];
69
+ }
70
+ expiredCardIds.add(peerId);
71
+ return [
72
+ peerId,
73
+ Object.freeze({ ...card, status: 'expired' }),
74
+ ];
75
+ });
76
+ const capabilities = recordEntries(state.discovery.capabilities).map(([key, capability]) => {
77
+ if (capability.status !== 'active')
78
+ return [key, capability];
79
+ const card = state.discovery.peerCards[capability.ownerPeerId];
80
+ if (capability.expiresAt <= logicalTime) {
81
+ return [
82
+ key,
83
+ Object.freeze({ ...capability, status: 'expired' }),
84
+ ];
85
+ }
86
+ if (!card ||
87
+ card.status !== 'active' ||
88
+ expiredCardIds.has(card.peerId)) {
89
+ return [
90
+ key,
91
+ Object.freeze({
92
+ ...capability,
93
+ status: card?.status === 'departed' ? 'departed' : 'expired',
94
+ }),
95
+ ];
96
+ }
97
+ return [key, capability];
98
+ });
99
+ const changedCards = peerCards.filter(([peerId, card]) => card !== state.discovery.peerCards[peerId]);
100
+ const changedCapabilities = capabilities.filter(([key, capability]) => capability !== state.discovery.capabilities[key]);
101
+ const expiredRecords = changedCards.length + changedCapabilities.length;
102
+ if (state.coordination.journal.length + expiredRecords >
103
+ state.coordination.limits.maximumJournalEntries) {
104
+ return Object.freeze({
105
+ accepted: false,
106
+ code: 'journal_capacity_exceeded',
107
+ state,
108
+ });
109
+ }
110
+ if (state.coordination.localEventSequence >
111
+ Number.MAX_SAFE_INTEGER - expiredRecords) {
112
+ throw new RangeError('Mesh coordination event sequence exhausted');
113
+ }
114
+ const journalEntries = [];
115
+ for (const [, card] of changedCards) {
116
+ journalEntries.push(expiryJournalEntry(state, journalEntries.length, logicalTime, JSON.stringify(['peer.card', card.peerCardId])));
117
+ }
118
+ for (const [, capability] of changedCapabilities) {
119
+ journalEntries.push(expiryJournalEntry(state, journalEntries.length, logicalTime, JSON.stringify(['capability.advertise', capability.advertisementId])));
120
+ }
121
+ const coordination = Object.freeze({
122
+ ...state.coordination,
123
+ journal: Object.freeze([...state.coordination.journal, ...journalEntries]),
124
+ localEventSequence: state.coordination.localEventSequence + expiredRecords,
125
+ lastLogicalTime: logicalTime,
126
+ });
127
+ const discovery = Object.freeze({
128
+ ...state.discovery,
129
+ peerCards: createFrozenRecord(peerCards),
130
+ peerViews: createFrozenRecord(recordEntries(state.discovery.peerViews).filter(([peerId, view]) => view.expiresAt > logicalTime &&
131
+ !expiredCardIds.has(peerId) &&
132
+ state.discovery.peerCards[peerId]?.status === 'active')),
133
+ capabilities: createFrozenRecord(capabilities),
134
+ lastLogicalTime: logicalTime,
135
+ });
136
+ return Object.freeze({
137
+ accepted: true,
138
+ expiredRecords,
139
+ state: createMeshDiscoveryRuntimeState(coordination, discovery),
140
+ });
141
+ }
142
+ /** Pure matching over locally visible, admitted and unexpired self-claims. */
143
+ export function matchMeshDiscoveryCapabilities(state, requirement, logicalTime) {
144
+ assertRuntimeState(state);
145
+ assertMeshLogicalTime(logicalTime);
146
+ if (logicalTime < state.discovery.lastLogicalTime) {
147
+ throw new RangeError('Mesh discovery logical time cannot move backwards');
148
+ }
149
+ assertRequirement(requirement, state.discovery.limits);
150
+ const eligible = [];
151
+ const evaluations = [];
152
+ for (const peerId of Object.keys(state.discovery.admittedPeers).sort()) {
153
+ const evaluated = evaluatePeerMatch(state, peerId, requirement, logicalTime);
154
+ if ('match' in evaluated) {
155
+ eligible.push(evaluated.match);
156
+ evaluations.push(Object.freeze({ peerId, reason: 'eligible' }));
157
+ }
158
+ else {
159
+ evaluations.push(Object.freeze({ peerId, reason: evaluated.reason }));
160
+ }
161
+ }
162
+ const matches = eligible.slice(0, requirement.fanout);
163
+ const selected = new Set(matches.map((match) => match.peerId));
164
+ const finalEvaluations = evaluations.map((evaluation) => evaluation.reason === 'eligible' && !selected.has(evaluation.peerId)
165
+ ? Object.freeze({
166
+ peerId: evaluation.peerId,
167
+ reason: 'fanout_limited',
168
+ })
169
+ : evaluation);
170
+ return Object.freeze({
171
+ matches: Object.freeze(matches),
172
+ evaluations: Object.freeze(finalEvaluations),
173
+ });
174
+ }
175
+ /**
176
+ * Resolves bounded topic recipients from the sender's local view only.
177
+ * Topic subscriptions are receiver-local and are enforced at inbound delivery;
178
+ * an existing capability self-claim is never required to receive a first
179
+ * capability advertisement.
180
+ */
181
+ export function selectMeshDiscoveryTopicRecipients(state, topic, logicalTime, fanout = state.discovery.limits.maximumFanout) {
182
+ assertRuntimeState(state);
183
+ assertMeshLogicalTime(logicalTime);
184
+ if (logicalTime < state.coordination.lastLogicalTime ||
185
+ logicalTime < state.discovery.lastLogicalTime) {
186
+ throw new RangeError('Mesh discovery logical time cannot move backwards');
187
+ }
188
+ if ((topic !== 'membership' && topic !== 'capability') ||
189
+ !Number.isSafeInteger(fanout) ||
190
+ fanout < 1 ||
191
+ fanout > state.discovery.limits.maximumFanout) {
192
+ throw new RangeError('Invalid Mesh discovery topic fanout');
193
+ }
194
+ const peers = recordEntries(state.discovery.peerViews)
195
+ .filter(([peerId, view]) => {
196
+ const card = state.discovery.peerCards[peerId];
197
+ return (card !== undefined &&
198
+ card.status === 'active' &&
199
+ card.expiresAt > logicalTime &&
200
+ view.expiresAt > logicalTime);
201
+ })
202
+ .map(([peerId]) => peerId)
203
+ .sort()
204
+ .slice(0, fanout);
205
+ return Object.freeze(peers);
206
+ }
207
+ function evaluatePayload(state, request, metadata) {
208
+ switch (request.envelope.payload.type) {
209
+ case 'peer.card':
210
+ return evaluatePeerCard(state, request, metadata);
211
+ case 'peer.goodbye':
212
+ return evaluatePeerGoodbye(state, request);
213
+ case 'capability.advertise':
214
+ return evaluateCapabilityAdvertisement(state, request, metadata);
215
+ case 'capability.withdraw':
216
+ return evaluateCapabilityWithdrawal(state, request);
217
+ default:
218
+ throw new TypeError('Unsupported verified Mesh discovery payload');
219
+ }
220
+ }
221
+ function evaluatePeerCard(state, request, metadata) {
222
+ const envelope = request.envelope;
223
+ const payload = envelope.payload;
224
+ const admission = state.discovery.admittedPeers[envelope.sender.peerId];
225
+ if (payload.subjectPeerId !== envelope.sender.peerId ||
226
+ payload.instanceId !== envelope.sender.instanceId) {
227
+ return { code: 'sender_instance_not_admitted' };
228
+ }
229
+ if (compare(payload.validUntil, admission.validUntil) > 0) {
230
+ return { code: 'validity_exceeds_admission' };
231
+ }
232
+ const expiresAt = logicalExpiry(payload.validFrom, payload.validUntil, request.verifiedAt, request.receivedAt);
233
+ if (expiresAt === undefined)
234
+ return { code: 'record_expired' };
235
+ const current = state.discovery.peerCards[payload.subjectPeerId];
236
+ if (current?.status === 'departed' &&
237
+ current.instanceId === payload.instanceId) {
238
+ return { code: 'peer_card_not_active' };
239
+ }
240
+ if ((!current && payload.cardRevision !== 1) ||
241
+ (current && payload.cardRevision !== current.cardRevision + 1)) {
242
+ return { code: 'peer_card_revision_invalid' };
243
+ }
244
+ if ((!current &&
245
+ (payload.previousPeerCardId !== undefined ||
246
+ envelope.causationId !== undefined)) ||
247
+ (current &&
248
+ (payload.previousPeerCardId !== current.peerCardId ||
249
+ envelope.causationId !== current.acceptedMessageId))) {
250
+ return { code: 'peer_card_predecessor_invalid' };
251
+ }
252
+ if (!current &&
253
+ Object.keys(state.discovery.peerCards).length >=
254
+ state.discovery.limits.maximumPeerCards) {
255
+ return { code: 'peer_card_capacity_exceeded' };
256
+ }
257
+ const card = freezePeerCard({
258
+ peerId: payload.subjectPeerId,
259
+ instanceId: payload.instanceId,
260
+ peerCardId: payload.peerCardId,
261
+ cardRevision: payload.cardRevision,
262
+ protocolVersions: payload.protocolVersions,
263
+ transportHints: payload.transportHints,
264
+ capabilityIds: payload.capabilityIds,
265
+ validFrom: payload.validFrom,
266
+ validUntil: payload.validUntil,
267
+ validityVerifiedAt: request.verifiedAt,
268
+ acceptedMessageId: envelope.messageId,
269
+ acceptedAt: request.receivedAt,
270
+ expiresAt,
271
+ status: 'active',
272
+ });
273
+ if (encodedBytes(card) > state.discovery.limits.maximumPeerCardBytes) {
274
+ return { code: 'peer_card_capacity_exceeded' };
275
+ }
276
+ const peerCards = createFrozenRecord([
277
+ ...recordEntries(state.discovery.peerCards).filter(([peerId]) => peerId !== card.peerId),
278
+ [card.peerId, card],
279
+ ]);
280
+ const view = Object.freeze({
281
+ peerId: card.peerId,
282
+ peerCardId: card.peerCardId,
283
+ cardRevision: card.cardRevision,
284
+ observedAt: request.receivedAt,
285
+ expiresAt,
286
+ });
287
+ const peerViews = insertPeerView(state, view, request.receivedAt);
288
+ const capabilities = createFrozenRecord(recordEntries(state.discovery.capabilities).map(([key, capability]) => capability.ownerPeerId === card.peerId &&
289
+ capability.status === 'active' &&
290
+ (capability.instanceId !== card.instanceId ||
291
+ !card.capabilityIds.includes(capability.capabilityId) ||
292
+ compare(capability.validUntil, card.validUntil) > 0)
293
+ ? [
294
+ key,
295
+ Object.freeze({
296
+ ...capability,
297
+ status: 'departed',
298
+ }),
299
+ ]
300
+ : [key, capability]));
301
+ return {
302
+ discovery: Object.freeze({
303
+ ...state.discovery,
304
+ peerCards,
305
+ peerViews,
306
+ capabilities,
307
+ }),
308
+ };
309
+ }
310
+ function evaluatePeerGoodbye(state, request) {
311
+ const envelope = request.envelope;
312
+ const payload = envelope.payload;
313
+ if (payload.type !== 'peer.goodbye')
314
+ throw new TypeError('Invalid payload');
315
+ const current = state.discovery.peerCards[envelope.sender.peerId];
316
+ if (!current)
317
+ return { code: 'peer_card_missing' };
318
+ if (current.status !== 'active' || current.expiresAt <= request.receivedAt) {
319
+ return { code: 'peer_card_not_active' };
320
+ }
321
+ if (envelope.sender.instanceId !== current.instanceId ||
322
+ payload.peerCardId !== current.peerCardId ||
323
+ payload.cardRevision !== current.cardRevision ||
324
+ payload.instanceId !== current.instanceId ||
325
+ envelope.causationId !== current.acceptedMessageId) {
326
+ return { code: 'peer_card_predecessor_invalid' };
327
+ }
328
+ const peerCards = createFrozenRecord([
329
+ ...recordEntries(state.discovery.peerCards).filter(([peerId]) => peerId !== current.peerId),
330
+ [
331
+ current.peerId,
332
+ Object.freeze({
333
+ ...current,
334
+ acceptedMessageId: envelope.messageId,
335
+ acceptedAt: request.receivedAt,
336
+ status: 'departed',
337
+ }),
338
+ ],
339
+ ]);
340
+ const capabilities = createFrozenRecord(recordEntries(state.discovery.capabilities).map(([key, capability]) => capability.ownerPeerId === current.peerId &&
341
+ capability.status === 'active'
342
+ ? [
343
+ key,
344
+ Object.freeze({
345
+ ...capability,
346
+ status: 'departed',
347
+ }),
348
+ ]
349
+ : [key, capability]));
350
+ return {
351
+ discovery: Object.freeze({
352
+ ...state.discovery,
353
+ peerCards,
354
+ peerViews: createFrozenRecord(recordEntries(state.discovery.peerViews).filter(([peerId]) => peerId !== current.peerId)),
355
+ capabilities,
356
+ }),
357
+ };
358
+ }
359
+ function evaluateCapabilityAdvertisement(state, request, metadata) {
360
+ const envelope = request.envelope;
361
+ const payload = envelope.payload;
362
+ if (payload.ownerPeerId !== envelope.sender.peerId) {
363
+ return { code: 'sender_not_admitted' };
364
+ }
365
+ const card = state.discovery.peerCards[payload.ownerPeerId];
366
+ if (!card)
367
+ return { code: 'peer_card_missing' };
368
+ if (card.instanceId !== envelope.sender.instanceId ||
369
+ card.status !== 'active' ||
370
+ card.expiresAt <= request.receivedAt ||
371
+ !Object.hasOwn(state.discovery.peerViews, payload.ownerPeerId)) {
372
+ return { code: 'peer_card_not_active' };
373
+ }
374
+ if (!card.capabilityIds.includes(payload.capabilityId)) {
375
+ return { code: 'capability_not_listed' };
376
+ }
377
+ if (compare(payload.validUntil, card.validUntil) > 0) {
378
+ return { code: 'validity_exceeds_admission' };
379
+ }
380
+ const expiresAt = logicalExpiry(payload.validFrom, payload.validUntil, request.verifiedAt, request.receivedAt);
381
+ if (expiresAt === undefined)
382
+ return { code: 'record_expired' };
383
+ const key = JSON.stringify([payload.ownerPeerId, payload.capabilityId]);
384
+ const current = state.discovery.capabilities[key];
385
+ if ((!current && payload.capabilityRevision !== 1) ||
386
+ (current && payload.capabilityRevision !== current.capabilityRevision + 1)) {
387
+ return { code: 'capability_revision_invalid' };
388
+ }
389
+ if ((!current &&
390
+ (payload.previousAdvertisementId !== undefined ||
391
+ envelope.causationId !== undefined)) ||
392
+ (current &&
393
+ (payload.previousAdvertisementId !== current.advertisementId ||
394
+ envelope.causationId !== current.acceptedMessageId))) {
395
+ return { code: 'capability_predecessor_invalid' };
396
+ }
397
+ if (!current) {
398
+ const total = Object.keys(state.discovery.capabilities).length;
399
+ const perPeer = Object.values(state.discovery.capabilities).filter((capability) => capability.ownerPeerId === payload.ownerPeerId).length;
400
+ if (total >= state.discovery.limits.maximumCapabilities ||
401
+ perPeer >= state.discovery.limits.maximumCapabilitiesPerPeer) {
402
+ return { code: 'capability_capacity_exceeded' };
403
+ }
404
+ }
405
+ const capability = freezeCapability({
406
+ ownerPeerId: payload.ownerPeerId,
407
+ instanceId: envelope.sender.instanceId,
408
+ advertisementId: payload.advertisementId,
409
+ capabilityId: payload.capabilityId,
410
+ capabilityRevision: payload.capabilityRevision,
411
+ capabilityKey: payload.capabilityKey,
412
+ version: payload.version,
413
+ ...(payload.variant === undefined ? {} : { variant: payload.variant }),
414
+ inputMediaTypes: payload.inputMediaTypes,
415
+ outputMediaTypes: payload.outputMediaTypes,
416
+ attributes: payload.attributes,
417
+ validFrom: payload.validFrom,
418
+ validUntil: payload.validUntil,
419
+ validityVerifiedAt: request.verifiedAt,
420
+ ...(payload.maximumConcurrency === undefined
421
+ ? {}
422
+ : { maximumConcurrency: payload.maximumConcurrency }),
423
+ ...(payload.maximumPayloadBytes === undefined
424
+ ? {}
425
+ : { maximumPayloadBytes: payload.maximumPayloadBytes }),
426
+ acceptedMessageId: envelope.messageId,
427
+ acceptedAt: request.receivedAt,
428
+ expiresAt,
429
+ status: 'active',
430
+ });
431
+ if (encodedBytes(capability) > state.discovery.limits.maximumCapabilityBytes) {
432
+ return { code: 'capability_capacity_exceeded' };
433
+ }
434
+ return {
435
+ discovery: Object.freeze({
436
+ ...state.discovery,
437
+ capabilities: createFrozenRecord([
438
+ ...recordEntries(state.discovery.capabilities).filter(([entryKey]) => entryKey !== key),
439
+ [key, capability],
440
+ ]),
441
+ }),
442
+ };
443
+ }
444
+ function evaluateCapabilityWithdrawal(state, request) {
445
+ const envelope = request.envelope;
446
+ const payload = envelope.payload;
447
+ if (payload.type !== 'capability.withdraw') {
448
+ throw new TypeError('Invalid payload');
449
+ }
450
+ const key = JSON.stringify([envelope.sender.peerId, payload.capabilityId]);
451
+ const current = state.discovery.capabilities[key];
452
+ if (!current)
453
+ return { code: 'capability_missing' };
454
+ if (current.instanceId !== envelope.sender.instanceId ||
455
+ current.status !== 'active' ||
456
+ current.expiresAt <= request.receivedAt) {
457
+ return { code: 'capability_not_active' };
458
+ }
459
+ if (payload.advertisementId !== current.advertisementId ||
460
+ payload.capabilityRevision !== current.capabilityRevision ||
461
+ envelope.causationId !== current.acceptedMessageId) {
462
+ return { code: 'capability_predecessor_invalid' };
463
+ }
464
+ return {
465
+ discovery: Object.freeze({
466
+ ...state.discovery,
467
+ capabilities: createFrozenRecord([
468
+ ...recordEntries(state.discovery.capabilities).filter(([entryKey]) => entryKey !== key),
469
+ [
470
+ key,
471
+ Object.freeze({
472
+ ...current,
473
+ acceptedMessageId: envelope.messageId,
474
+ acceptedAt: request.receivedAt,
475
+ status: 'withdrawn',
476
+ }),
477
+ ],
478
+ ]),
479
+ }),
480
+ };
481
+ }
482
+ function validateContext(state, request) {
483
+ const result = validateMeshEnvelopeContext(request.envelope, {
484
+ tenantId: state.discovery.identity.tenantId,
485
+ meshId: state.discovery.identity.meshId,
486
+ peerId: state.discovery.identity.peerId,
487
+ receivedAt: request.verifiedAt,
488
+ subscribedTopics: state.discovery.subscriptions,
489
+ ...(request.supportedCriticalExtensions === undefined
490
+ ? {}
491
+ : {
492
+ supportedCriticalExtensions: request.supportedCriticalExtensions,
493
+ }),
494
+ });
495
+ if (result.ok)
496
+ return undefined;
497
+ const code = result.issues[0]?.code;
498
+ switch (code) {
499
+ case 'scope_mismatch':
500
+ return 'scope_mismatch';
501
+ case 'invalid_audience':
502
+ return request.envelope.audience.kind === 'mesh' &&
503
+ !state.discovery.subscriptions.includes(request.envelope.audience.topic)
504
+ ? 'topic_not_subscribed'
505
+ : 'audience_mismatch';
506
+ case 'message_expired':
507
+ return 'message_expired';
508
+ case 'message_from_future':
509
+ return 'message_from_future';
510
+ case 'unknown_critical_extension':
511
+ return 'unknown_critical_extension';
512
+ default:
513
+ return 'invalid_verified_envelope';
514
+ }
515
+ }
516
+ function validateCommon(state, envelope, verifiedAt) {
517
+ if (envelope.payload.type !== 'peer.card' &&
518
+ envelope.payload.type !== 'peer.goodbye' &&
519
+ envelope.payload.type !== 'capability.advertise' &&
520
+ envelope.payload.type !== 'capability.withdraw') {
521
+ return 'invalid_verified_envelope';
522
+ }
523
+ if (envelope.tenantId !== state.discovery.identity.tenantId ||
524
+ envelope.meshId !== state.discovery.identity.meshId) {
525
+ return 'scope_mismatch';
526
+ }
527
+ const expectedTopic = envelope.payload.type === 'peer.card' ||
528
+ envelope.payload.type === 'peer.goodbye'
529
+ ? 'membership'
530
+ : 'capability';
531
+ if (envelope.audience.kind === 'peer') {
532
+ if (envelope.audience.peerId !== state.discovery.identity.peerId) {
533
+ return 'audience_mismatch';
534
+ }
535
+ }
536
+ else {
537
+ if (envelope.audience.topic !== expectedTopic) {
538
+ return 'audience_mismatch';
539
+ }
540
+ if (!state.discovery.subscriptions.includes(expectedTopic)) {
541
+ return 'topic_not_subscribed';
542
+ }
543
+ }
544
+ const admission = state.discovery.admittedPeers[envelope.sender.peerId];
545
+ if (!admission)
546
+ return 'sender_not_admitted';
547
+ if (!admission.instanceIds.includes(envelope.sender.instanceId)) {
548
+ return 'sender_instance_not_admitted';
549
+ }
550
+ return compare(verifiedAt, admission.validUntil) >= 0
551
+ ? 'sender_admission_expired'
552
+ : undefined;
553
+ }
554
+ function isExactProjectedDuplicate(state, envelope, acceptedMessageId) {
555
+ if (envelope.messageId !== acceptedMessageId)
556
+ return false;
557
+ const payload = envelope.payload;
558
+ switch (payload.type) {
559
+ case 'peer.card': {
560
+ const card = state.discovery.peerCards[payload.subjectPeerId];
561
+ return (payload.subjectPeerId === envelope.sender.peerId &&
562
+ payload.instanceId === envelope.sender.instanceId &&
563
+ card?.peerCardId === payload.peerCardId &&
564
+ card.cardRevision === payload.cardRevision &&
565
+ card.instanceId === envelope.sender.instanceId &&
566
+ card.acceptedMessageId === acceptedMessageId);
567
+ }
568
+ case 'peer.goodbye': {
569
+ const card = state.discovery.peerCards[envelope.sender.peerId];
570
+ return (card?.status === 'departed' &&
571
+ card.instanceId === envelope.sender.instanceId &&
572
+ card.peerCardId === payload.peerCardId &&
573
+ card.cardRevision === payload.cardRevision &&
574
+ payload.instanceId === envelope.sender.instanceId &&
575
+ card.acceptedMessageId === acceptedMessageId);
576
+ }
577
+ case 'capability.advertise': {
578
+ const capability = state.discovery.capabilities[JSON.stringify([payload.ownerPeerId, payload.capabilityId])];
579
+ return (payload.ownerPeerId === envelope.sender.peerId &&
580
+ capability?.instanceId === envelope.sender.instanceId &&
581
+ capability.advertisementId === payload.advertisementId &&
582
+ capability.capabilityRevision === payload.capabilityRevision &&
583
+ capability.acceptedMessageId === acceptedMessageId);
584
+ }
585
+ case 'capability.withdraw': {
586
+ const capability = state.discovery.capabilities[JSON.stringify([envelope.sender.peerId, payload.capabilityId])];
587
+ return (capability?.status === 'withdrawn' &&
588
+ capability.instanceId === envelope.sender.instanceId &&
589
+ capability.advertisementId === payload.advertisementId &&
590
+ capability.capabilityRevision === payload.capabilityRevision &&
591
+ capability.acceptedMessageId === acceptedMessageId);
592
+ }
593
+ }
594
+ }
595
+ function domainMetadata(envelope) {
596
+ const recordId = envelope.payload.type === 'peer.card' ||
597
+ envelope.payload.type === 'peer.goodbye'
598
+ ? envelope.payload.peerCardId
599
+ : envelope.payload.advertisementId;
600
+ const contentDigest = envelope.payloadHash.startsWith('sha256:')
601
+ ? envelope.payloadHash.slice(7)
602
+ : '';
603
+ if (!payloadDigestPattern.test(contentDigest)) {
604
+ throw new TypeError('Invalid verified Mesh discovery payload digest');
605
+ }
606
+ return {
607
+ recordKey: JSON.stringify([envelope.payload.type, recordId]),
608
+ recordId,
609
+ contentDigest,
610
+ };
611
+ }
612
+ function acceptRecord(state, request, metadata, projectedDiscovery) {
613
+ if (state.coordination.localEventSequence >= Number.MAX_SAFE_INTEGER) {
614
+ throw new RangeError('Mesh coordination event sequence exhausted');
615
+ }
616
+ const sequence = state.coordination.localEventSequence + 1;
617
+ const record = Object.freeze({
618
+ recordKey: metadata.recordKey,
619
+ recordType: request.envelope.payload.type,
620
+ recordId: metadata.recordId,
621
+ contentDigest: metadata.contentDigest,
622
+ messageId: request.envelope.messageId,
623
+ acceptedAt: request.receivedAt,
624
+ });
625
+ const journalEntry = Object.freeze({
626
+ sequence,
627
+ occurredAt: request.receivedAt,
628
+ kind: 'domain.accepted',
629
+ domainRecordKey: metadata.recordKey,
630
+ });
631
+ const coordination = Object.freeze({
632
+ ...state.coordination,
633
+ domainRecords: createFrozenRecord([
634
+ ...recordEntries(state.coordination.domainRecords),
635
+ [metadata.recordKey, record],
636
+ ]),
637
+ journal: Object.freeze([...state.coordination.journal, journalEntry]),
638
+ localEventSequence: sequence,
639
+ lastLogicalTime: request.receivedAt,
640
+ });
641
+ const discovery = Object.freeze({
642
+ ...projectedDiscovery,
643
+ lastLogicalTime: request.receivedAt,
644
+ });
645
+ return Object.freeze({
646
+ accepted: true,
647
+ duplicate: false,
648
+ state: createMeshDiscoveryRuntimeState(coordination, discovery),
649
+ });
650
+ }
651
+ function insertPeerView(state, view, logicalTime) {
652
+ const retained = recordEntries(state.discovery.peerViews).filter(([peerId, current]) => peerId !== view.peerId &&
653
+ current.expiresAt > logicalTime &&
654
+ state.discovery.peerCards[peerId]?.status === 'active');
655
+ const candidates = [...retained, [view.peerId, view]];
656
+ if (candidates.length > state.discovery.limits.maximumPeerViews) {
657
+ candidates.sort(([leftPeerId, left], [rightPeerId, right]) => left.observedAt - right.observedAt ||
658
+ compareText(leftPeerId, rightPeerId));
659
+ candidates.shift();
660
+ }
661
+ return createFrozenRecord(candidates);
662
+ }
663
+ function evaluatePeerMatch(state, peerId, requirement, logicalTime) {
664
+ const view = state.discovery.peerViews[peerId];
665
+ if (!view || view.expiresAt <= logicalTime) {
666
+ return { reason: 'peer_view_missing' };
667
+ }
668
+ const card = state.discovery.peerCards[peerId];
669
+ if (!card || card.status !== 'active' || card.expiresAt <= logicalTime) {
670
+ return { reason: 'peer_card_inactive' };
671
+ }
672
+ const owned = Object.values(state.discovery.capabilities)
673
+ .filter((capability) => capability.ownerPeerId === peerId)
674
+ .sort((left, right) => compareText(left.capabilityId, right.capabilityId) ||
675
+ left.capabilityRevision - right.capabilityRevision);
676
+ const selected = [];
677
+ for (const capabilityKey of requirement.capabilityKeys) {
678
+ const keyed = owned.filter((capability) => capability.capabilityKey === capabilityKey);
679
+ if (keyed.length === 0)
680
+ return { reason: 'capability_key_missing' };
681
+ const active = keyed.filter((capability) => capability.status === 'active' && capability.expiresAt > logicalTime);
682
+ if (active.length === 0)
683
+ return { reason: 'capability_expired' };
684
+ const attributes = active.filter((capability) => Object.entries(requirement.attributes ?? {}).every(([key, value]) => capability.attributes[key] === value));
685
+ if (attributes.length === 0)
686
+ return { reason: 'attribute_mismatch' };
687
+ const input = attributes.filter((capability) => requirement.inputMediaType === undefined ||
688
+ capability.inputMediaTypes.includes(requirement.inputMediaType));
689
+ if (input.length === 0)
690
+ return { reason: 'input_media_type_mismatch' };
691
+ const output = input.filter((capability) => requirement.outputMediaType === undefined ||
692
+ capability.outputMediaTypes.includes(requirement.outputMediaType));
693
+ if (output.length === 0)
694
+ return { reason: 'output_media_type_mismatch' };
695
+ const capability = output[0];
696
+ selected.push(Object.freeze({
697
+ capabilityKey,
698
+ capabilityId: capability.capabilityId,
699
+ capabilityRevision: capability.capabilityRevision,
700
+ advertisementId: capability.advertisementId,
701
+ }));
702
+ }
703
+ return {
704
+ match: Object.freeze({
705
+ peerId,
706
+ capabilities: Object.freeze(selected),
707
+ }),
708
+ };
709
+ }
710
+ function assertRequirement(requirement, limits) {
711
+ const prototype = requirement && typeof requirement === 'object'
712
+ ? Object.getPrototypeOf(requirement)
713
+ : null;
714
+ if (!requirement ||
715
+ typeof requirement !== 'object' ||
716
+ (prototype !== null && prototype !== Object.prototype) ||
717
+ !hasExactEnumerableKeys(requirement, [
718
+ 'attributes',
719
+ 'capabilityKeys',
720
+ 'fanout',
721
+ 'inputMediaType',
722
+ 'outputMediaType',
723
+ ], ['capabilityKeys', 'fanout']) ||
724
+ !Array.isArray(requirement.capabilityKeys) ||
725
+ requirement.capabilityKeys.length < 1 ||
726
+ requirement.capabilityKeys.length >
727
+ limits.maximumRequirementCapabilityKeys ||
728
+ requirement.capabilityKeys.some((key) => typeof key !== 'string' || key.length < 1) ||
729
+ new Set(requirement.capabilityKeys).size !==
730
+ requirement.capabilityKeys.length ||
731
+ !Number.isSafeInteger(requirement.fanout) ||
732
+ requirement.fanout < 1 ||
733
+ requirement.fanout > limits.maximumFanout ||
734
+ (requirement.attributes !== undefined &&
735
+ (!requirement.attributes ||
736
+ typeof requirement.attributes !== 'object' ||
737
+ Array.isArray(requirement.attributes) ||
738
+ !hasRecordPrototype(requirement.attributes) ||
739
+ Object.keys(requirement.attributes).length >
740
+ limits.maximumRequirementAttributes ||
741
+ !hasOnlyEnumerableStringKeys(requirement.attributes) ||
742
+ Object.values(requirement.attributes).some((value) => typeof value !== 'string'))) ||
743
+ (requirement.inputMediaType !== undefined &&
744
+ (typeof requirement.inputMediaType !== 'string' ||
745
+ requirement.inputMediaType.length < 1)) ||
746
+ (requirement.outputMediaType !== undefined &&
747
+ (typeof requirement.outputMediaType !== 'string' ||
748
+ requirement.outputMediaType.length < 1))) {
749
+ throw new TypeError('Invalid Mesh capability requirement');
750
+ }
751
+ if (encodedBytes(requirement) > limits.maximumRequirementBytes) {
752
+ throw new RangeError('Mesh capability requirement bound exceeded');
753
+ }
754
+ }
755
+ function hasRecordPrototype(value) {
756
+ const prototype = Object.getPrototypeOf(value);
757
+ return prototype === null || prototype === Object.prototype;
758
+ }
759
+ function hasOnlyEnumerableStringKeys(value) {
760
+ return (Object.getOwnPropertySymbols(value).length === 0 &&
761
+ Object.getOwnPropertyNames(value).every((key) => Object.prototype.propertyIsEnumerable.call(value, key)));
762
+ }
763
+ function hasExactEnumerableKeys(value, supportedKeys, requiredKeys) {
764
+ const supported = new Set(supportedKeys);
765
+ return (hasOnlyEnumerableStringKeys(value) &&
766
+ Object.getOwnPropertyNames(value).every((key) => supported.has(key)) &&
767
+ requiredKeys.every((key) => Object.hasOwn(value, key)));
768
+ }
769
+ function assertExactKeys(value, supportedKeys, requiredKeys) {
770
+ if (!hasExactEnumerableKeys(value, supportedKeys, requiredKeys)) {
771
+ throw new TypeError('Mesh discovery value contains unsupported fields');
772
+ }
773
+ }
774
+ function expiryJournalEntry(state, offset, logicalTime, domainRecordKey) {
775
+ return Object.freeze({
776
+ sequence: state.coordination.localEventSequence + offset + 1,
777
+ occurredAt: logicalTime,
778
+ kind: 'command.accepted',
779
+ domainRecordKey,
780
+ });
781
+ }
782
+ function freezePeerCard(card) {
783
+ return Object.freeze({
784
+ ...card,
785
+ protocolVersions: Object.freeze([...card.protocolVersions]),
786
+ transportHints: Object.freeze([...card.transportHints]),
787
+ capabilityIds: Object.freeze([...card.capabilityIds]),
788
+ });
789
+ }
790
+ function freezeCapability(capability) {
791
+ const attributes = Object.create(null);
792
+ for (const [key, value] of Object.entries(capability.attributes)) {
793
+ attributes[key] = value;
794
+ }
795
+ return Object.freeze({
796
+ ...capability,
797
+ inputMediaTypes: Object.freeze([...capability.inputMediaTypes]),
798
+ outputMediaTypes: Object.freeze([...capability.outputMediaTypes]),
799
+ attributes: Object.freeze(attributes),
800
+ });
801
+ }
802
+ function logicalExpiry(validFrom, validUntil, verifiedAt, receivedAt) {
803
+ if (compare(verifiedAt, validFrom) < 0 ||
804
+ compare(verifiedAt, validUntil) >= 0) {
805
+ return undefined;
806
+ }
807
+ const validUntilMs = Date.parse(validUntil);
808
+ const verifiedAtMs = Date.parse(verifiedAt);
809
+ if (!Number.isFinite(validUntilMs) || !Number.isFinite(verifiedAtMs)) {
810
+ throw new TypeError('Invalid Mesh discovery timestamp');
811
+ }
812
+ const remaining = Math.max(0, validUntilMs - verifiedAtMs);
813
+ if (receivedAt > Number.MAX_SAFE_INTEGER - remaining) {
814
+ throw new RangeError('Mesh discovery expiry exceeds logical time');
815
+ }
816
+ return receivedAt + remaining;
817
+ }
818
+ function compare(left, right) {
819
+ const result = compareMeshTimestamps(left, right);
820
+ if (!result.ok)
821
+ throw new TypeError('Invalid Mesh discovery timestamp');
822
+ return result.value;
823
+ }
824
+ function encodedBytes(value) {
825
+ return utf8Encoder.encode(JSON.stringify(value)).byteLength;
826
+ }
827
+ function compareText(left, right) {
828
+ return left < right ? -1 : left > right ? 1 : 0;
829
+ }
830
+ function assertRuntimeState(state) {
831
+ if (!state || typeof state !== 'object' || !Object.isFrozen(state)) {
832
+ throw new TypeError('Mesh discovery runtime state must be immutable');
833
+ }
834
+ assertFrozenMeshCoordinationState(state.coordination);
835
+ assertFrozenMeshDiscoveryState(state.discovery);
836
+ if (state.coordination.identity.tenantId !==
837
+ state.discovery.identity.tenantId ||
838
+ state.coordination.identity.meshId !== state.discovery.identity.meshId ||
839
+ state.coordination.identity.peerId !== state.discovery.identity.peerId ||
840
+ state.coordination.identity.instanceId !==
841
+ state.discovery.identity.instanceId ||
842
+ state.coordination.identity.keyId !== state.discovery.identity.keyId ||
843
+ state.coordination.lastLogicalTime !== state.discovery.lastLogicalTime) {
844
+ throw new TypeError('Mesh discovery runtime snapshots are not aligned');
845
+ }
846
+ }
847
+ function rejection(state, code) {
848
+ return Object.freeze({ accepted: false, code, state });
849
+ }
850
+ //# sourceMappingURL=coordination-discovery.js.map