@agentplat/mesh 0.3.0-alpha.1 → 0.3.0-alpha.3

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,2743 @@
1
+ import { canonicalizeMeshPayload, compareMeshTimestamps, validateMeshEnvelopeContext, validateSignedMeshEnvelope, } from "@agentplat/mesh-protocol";
2
+ import { createMeshAllocationRuntimeState, meshAllocationRetainsMessageId, meshAssignmentFenceKey, restoreMeshAllocationState, } from "./coordination-allocation-state.js";
3
+ import { matchMeshDiscoveryCapabilities } from "./coordination-discovery.js";
4
+ import { evaluateMeshExecutionCommand, evaluateVerifiedMeshExecutionEnvelope, } from "./coordination-execution.js";
5
+ import { evaluateMeshLeaseExpiryTimer, evaluateMeshLeaseRenewalCommand, evaluateVerifiedMeshLeaseRenewalEnvelope, } from "./coordination-lease-renewal.js";
6
+ import { evaluateMeshRecoveryCommand, evaluateVerifiedMeshRecoveryEnvelope, evaluateVerifiedMeshWitnessEnvelope, } from "./coordination-recovery.js";
7
+ import { createMeshDiscoveryRuntimeState } from "./coordination-discovery-state.js";
8
+ import { logicalDeadline } from "./coordination-objective-work-time.js";
9
+ import { sha256Base64Url } from "./sha256.js";
10
+ import { assertMeshLogicalTime, createFrozenRecord, recordEntries, } from "./state.js";
11
+ const utf8Encoder = new TextEncoder();
12
+ /** Commits a prepared local offer or award; signing and delivery stay outside. */
13
+ export function evaluateMeshAllocationCommand(state, command, verifiedAt, receivedAt) {
14
+ assertRuntime(state, receivedAt);
15
+ if (!command ||
16
+ ![
17
+ "allocation.offer",
18
+ "allocation.award",
19
+ "allocation.bid",
20
+ "allocation.assignment_response",
21
+ "allocation.execution",
22
+ "allocation.lease_renew",
23
+ "allocation.recovery",
24
+ "allocation.recovery_award",
25
+ ].includes(command.kind))
26
+ throw new TypeError("Invalid Mesh allocation command");
27
+ if (command.kind === "allocation.award")
28
+ return evaluateAwardCommand(state, command, verifiedAt, receivedAt);
29
+ if (command.kind === "allocation.bid")
30
+ return evaluateBidCommand(state, command, verifiedAt, receivedAt);
31
+ if (command.kind === "allocation.assignment_response")
32
+ return evaluateLocalAssignmentResponseCommand(state, command, verifiedAt, receivedAt);
33
+ if (command.kind === "allocation.execution")
34
+ return evaluateMeshExecutionCommand(state, command, verifiedAt, receivedAt);
35
+ if (command.kind === "allocation.lease_renew")
36
+ return evaluateMeshLeaseRenewalCommand(state, command, verifiedAt, receivedAt);
37
+ if (command.kind === "allocation.recovery")
38
+ return evaluateMeshRecoveryCommand(state, command, verifiedAt, receivedAt);
39
+ if (command.kind === "allocation.recovery_award")
40
+ return evaluateRecoveryAwardCommand(state, command, verifiedAt, receivedAt);
41
+ if (Object.keys(command).sort().join(",") !==
42
+ "expectedWorkItemRevision,kind,objectiveId,recipients,workItemId" ||
43
+ typeof command.objectiveId !== "string" ||
44
+ typeof command.workItemId !== "string" ||
45
+ !Number.isSafeInteger(command.expectedWorkItemRevision) ||
46
+ command.expectedWorkItemRevision < 1 ||
47
+ !Array.isArray(command.recipients) ||
48
+ command.recipients.some((recipient) => !recipient ||
49
+ typeof recipient !== "object" ||
50
+ Object.keys(recipient).sort().join(",") !==
51
+ "envelope,preparedAt,recipientPeerId" ||
52
+ typeof recipient.recipientPeerId !== "string" ||
53
+ !Number.isSafeInteger(recipient.preparedAt) ||
54
+ recipient.preparedAt < 0 ||
55
+ !recipient.envelope ||
56
+ typeof recipient.envelope !== "object"))
57
+ throw new TypeError("invalid mesh local offer command");
58
+ const work = state.objectives.workItems[workKey(command.objectiveId, command.workItemId)];
59
+ if (!work)
60
+ return reject(state, "work_missing");
61
+ if (work.objectiveId !== command.objectiveId ||
62
+ work.workItemRevision !== command.expectedWorkItemRevision)
63
+ return reject(state, "work_revision_conflict");
64
+ if (work.ownerPeerId !== state.allocation.identity.peerId ||
65
+ work.status !== "ready")
66
+ return reject(state, "work_not_ready");
67
+ const objective = state.objectives.objectives[work.objectiveId];
68
+ if (!objective ||
69
+ objective.status !== "active" ||
70
+ receivedAt >= objective.expiresAt ||
71
+ compare(verifiedAt, objective.validUntil) >= 0 ||
72
+ work.objectiveDocumentId !== objective.objectiveDocumentId ||
73
+ work.objectiveRevision !== objective.objectiveRevision ||
74
+ work.objectivePolicy !==
75
+ state.objectives.objectivePolicies[policyKey(work.objectiveId, work.objectiveRevision)])
76
+ return reject(state, "offer_invalid");
77
+ const existing = state.allocation.workAllocations[workKey(work.objectiveId, work.workItemId)];
78
+ if (existing && existing.phase !== "ready")
79
+ return reject(state, "work_not_ready");
80
+ const priorOffers = Object.values(state.allocation.localOffers)
81
+ .filter((offer) => offer.objectiveId === work.objectiveId &&
82
+ offer.work.workItemId === work.workItemId)
83
+ .sort((a, b) => a.offerAttempt - b.offerAttempt);
84
+ const prepared = validatePrepared(command.recipients, state, work, verifiedAt, receivedAt, priorOffers.at(-1));
85
+ if ("code" in prepared)
86
+ return reject(state, prepared.code);
87
+ const { payload, entries, bidDeadlineAt } = prepared;
88
+ if (payload.offerAttempt !== priorOffers.length + 1 ||
89
+ payload.previousOfferId !== priorOffers.at(-1)?.offerId ||
90
+ payload.ownerPeerId !== state.allocation.identity.peerId ||
91
+ payload.ownerEpoch !== 1 ||
92
+ payload.objectiveId !== work.objectiveId ||
93
+ payload.objectiveDocumentId !== work.objectiveDocumentId ||
94
+ payload.objectiveRevision !== work.objectiveRevision ||
95
+ payload.workItemId !== work.workItemId ||
96
+ payload.workItemRevision !== work.workItemRevision ||
97
+ payload.budgetReservationUnits !== work.budgetReservationUnits ||
98
+ payload.workDeadline !== work.workDeadline ||
99
+ !sameArray(payload.requiredCapabilityKeys, work.requiredCapabilityKeys) ||
100
+ !sameRecord(payload.matchingAttributes, work.matchingAttributes) ||
101
+ !sameArray(payload.completionCriteria, work.completionCriteria) ||
102
+ !sameInput(payload, work))
103
+ return reject(state, "offer_invalid");
104
+ const signedBidWindowDurations = entries.map(({ envelope }) => logicalDeadline(payload.bidDeadline, envelope.sentAt, 0));
105
+ if (bidDeadlineAt > work.workDeadlineAt ||
106
+ bidDeadlineAt - receivedAt > objective.bidWindowMs ||
107
+ signedBidWindowDurations.some((duration) => duration === undefined || duration > objective.bidWindowMs))
108
+ return reject(state, "offer_invalid");
109
+ if (objective.committedBudgetUnits >
110
+ objective.maximumBudgetUnits - objective.reservedBudgetUnits ||
111
+ work.budgetReservationUnits >
112
+ objective.maximumBudgetUnits -
113
+ objective.reservedBudgetUnits -
114
+ objective.committedBudgetUnits)
115
+ return reject(state, "offer_invalid");
116
+ if (state.coordination.journal.length >=
117
+ state.coordination.limits.maximumJournalEntries)
118
+ return reject(state, "journal_capacity_exceeded");
119
+ if (Object.keys(state.coordination.timers).length >=
120
+ state.coordination.limits.maximumTimers)
121
+ return reject(state, "timer_capacity_exceeded");
122
+ if (Object.keys(state.allocation.localOffers).length >=
123
+ state.allocation.limits.maximumOffers)
124
+ return reject(state, "offer_capacity_exceeded");
125
+ const offerRecordKey = offerDomainKey(payload.offerId);
126
+ if (state.coordination.domainRecords[offerRecordKey])
127
+ return reject(state, "offer_duplicate_conflict");
128
+ if (Object.keys(state.coordination.domainRecords).length >=
129
+ state.coordination.limits.maximumDomainRecords)
130
+ return reject(state, "domain_capacity_exceeded");
131
+ if (state.coordination.localEventSequence >= Number.MAX_SAFE_INTEGER)
132
+ throw new RangeError("Mesh coordination event sequence exhausted");
133
+ const reservationId = `allocation.reservation.${payload.offerId}`;
134
+ const timerId = `allocation.bid.${payload.offerId}`;
135
+ if (state.coordination.timers[timerId])
136
+ return reject(state, "timer_id_conflict");
137
+ const generation = 1;
138
+ const binding = {
139
+ objectiveId: work.objectiveId,
140
+ objectiveDocumentId: work.objectiveDocumentId,
141
+ objectiveRevision: work.objectiveRevision,
142
+ objectivePolicy: work.objectivePolicy,
143
+ work,
144
+ };
145
+ const localOffer = {
146
+ ...binding,
147
+ offerId: payload.offerId,
148
+ offerAttempt: payload.offerAttempt,
149
+ ...(payload.previousOfferId === undefined
150
+ ? {}
151
+ : { previousOfferId: payload.previousOfferId }),
152
+ bidDeadline: payload.bidDeadline,
153
+ bidDeadlineAt,
154
+ bidDeadlineTimerId: timerId,
155
+ bidDeadlineTimerGeneration: generation,
156
+ createdAt: receivedAt,
157
+ validityVerifiedAt: verifiedAt,
158
+ reservationId,
159
+ recipientOffers: createFrozenRecord(entries.map(({ recipientPeerId, preparedAt, envelope }) => [
160
+ recipientPeerId,
161
+ Object.freeze({
162
+ recipientPeerId,
163
+ preparedAt,
164
+ messageId: envelope.messageId,
165
+ envelope,
166
+ }),
167
+ ])),
168
+ };
169
+ const allocationWork = Object.freeze({
170
+ ...binding,
171
+ workKey: workKey(work.objectiveId, work.workItemId),
172
+ phase: "offered",
173
+ activeOfferId: payload.offerId,
174
+ bidDeadlineAt,
175
+ reservationId,
176
+ updatedAt: receivedAt,
177
+ });
178
+ const allocation = restoreMeshAllocationState({
179
+ ...state.allocation,
180
+ workAllocations: createFrozenRecord([
181
+ ...recordEntries(state.allocation.workAllocations).filter(([key]) => key !== allocationWork.workKey),
182
+ [allocationWork.workKey, allocationWork],
183
+ ]),
184
+ localOffers: createFrozenRecord([
185
+ ...recordEntries(state.allocation.localOffers),
186
+ [payload.offerId, localOffer],
187
+ ]),
188
+ reservations: createFrozenRecord([
189
+ ...recordEntries(state.allocation.reservations),
190
+ [
191
+ reservationId,
192
+ Object.freeze({
193
+ reservationId,
194
+ workKey: allocationWork.workKey,
195
+ offerId: payload.offerId,
196
+ objectiveId: work.objectiveId,
197
+ objectiveDocumentId: work.objectiveDocumentId,
198
+ objectiveRevision: work.objectiveRevision,
199
+ workItemId: work.workItemId,
200
+ workItemRevision: work.workItemRevision,
201
+ budgetReservationUnits: work.budgetReservationUnits,
202
+ reservedAt: receivedAt,
203
+ status: "reserved",
204
+ }),
205
+ ],
206
+ ]),
207
+ lastLogicalTime: receivedAt,
208
+ });
209
+ const sequence = state.coordination.localEventSequence + 1;
210
+ const offerRecord = Object.freeze({
211
+ recordKey: offerRecordKey,
212
+ recordType: "work.offer",
213
+ recordId: payload.offerId,
214
+ contentDigest: payloadDigest(entries[0].envelope),
215
+ messageId: entries[0].envelope.messageId,
216
+ acceptedAt: receivedAt,
217
+ objectiveId: payload.objectiveId,
218
+ });
219
+ const journal = Object.freeze({
220
+ sequence,
221
+ occurredAt: receivedAt,
222
+ kind: "command.accepted",
223
+ domainRecordKey: offerDomainKey(payload.offerId),
224
+ });
225
+ const timer = Object.freeze({
226
+ timerId,
227
+ kind: "work.bid_deadline",
228
+ dueAt: bidDeadlineAt,
229
+ generation,
230
+ domainRecordKey: offerDomainKey(payload.offerId),
231
+ });
232
+ const coordination = Object.freeze({
233
+ ...state.coordination,
234
+ domainRecords: createFrozenRecord([
235
+ ...recordEntries(state.coordination.domainRecords),
236
+ [offerRecordKey, offerRecord],
237
+ ]),
238
+ timers: createFrozenRecord([
239
+ ...recordEntries(state.coordination.timers),
240
+ [timerId, timer],
241
+ ]),
242
+ journal: Object.freeze([...state.coordination.journal, journal]),
243
+ localEventSequence: sequence,
244
+ lastLogicalTime: receivedAt,
245
+ });
246
+ const nextObjective = Object.freeze({
247
+ ...objective,
248
+ reservedBudgetUnits: objective.reservedBudgetUnits + work.budgetReservationUnits,
249
+ });
250
+ const objectives = Object.freeze({
251
+ ...state.objectives,
252
+ objectives: createFrozenRecord([
253
+ ...recordEntries(state.objectives.objectives).filter(([key]) => key !== objective.objectiveId),
254
+ [objective.objectiveId, nextObjective],
255
+ ]),
256
+ lastLogicalTime: receivedAt,
257
+ });
258
+ const next = createMeshAllocationRuntimeState(coordination, Object.freeze({ ...state.discovery, lastLogicalTime: receivedAt }), objectives, allocation);
259
+ const storedOffer = allocation.localOffers[payload.offerId];
260
+ const effects = Object.freeze(entries.map(({ recipientPeerId }) => {
261
+ const stored = storedOffer.recipientOffers[recipientPeerId];
262
+ return Object.freeze({
263
+ kind: "allocation.offer.dispatch",
264
+ offerId: payload.offerId,
265
+ recipientPeerId,
266
+ messageId: stored.messageId,
267
+ envelope: stored.envelope,
268
+ });
269
+ }));
270
+ return Object.freeze({
271
+ accepted: true,
272
+ duplicate: false,
273
+ state: next,
274
+ effects,
275
+ });
276
+ }
277
+ function evaluateAwardCommand(state, command, verifiedAt, receivedAt) {
278
+ if (Object.keys(command).sort().join(",") !==
279
+ "bidId,bidRevision,kind,offerId,recipient" ||
280
+ !command.recipient ||
281
+ Object.keys(command.recipient).sort().join(",") !==
282
+ "envelope,preparedAt,recipientPeerId")
283
+ throw new TypeError("invalid mesh local award command");
284
+ const offer = state.allocation.localOffers[command.offerId];
285
+ if (!offer)
286
+ return reject(state, "offer_missing");
287
+ if (Object.values(state.allocation.localAwards).some((award) => award.objectiveId === offer.objectiveId &&
288
+ award.work.workItemId === offer.work.workItemId &&
289
+ award.work.workItemRevision === offer.work.workItemRevision &&
290
+ award.assignmentEpoch === 1))
291
+ return reject(state, "award_invalid");
292
+ const selected = selectMeshAllocationBid(state, {
293
+ offerId: offer.offerId,
294
+ evaluatedAt: receivedAt,
295
+ });
296
+ if (selected.reason !== "selected" ||
297
+ !selected.bid ||
298
+ selected.bid.bidId !== command.bidId ||
299
+ selected.bid.bidRevision !== command.bidRevision)
300
+ return reject(state, "bid_invalid");
301
+ const bid = selected.bid;
302
+ const work = state.allocation.workAllocations[workKey(offer.objectiveId, offer.work.workItemId)];
303
+ const reservation = state.allocation.reservations[offer.reservationId];
304
+ const bidTimer = state.coordination.timers[offer.bidDeadlineTimerId];
305
+ const objective = state.objectives.objectives[offer.objectiveId];
306
+ if (!work ||
307
+ work.phase !== "offered" ||
308
+ work.activeOfferId !== offer.offerId ||
309
+ !reservation ||
310
+ reservation.status !== "reserved" ||
311
+ !bidTimer ||
312
+ !objective ||
313
+ objective.status !== "active" ||
314
+ objective.objectiveDocumentId !== offer.objectiveDocumentId ||
315
+ objective.objectiveRevision !== offer.objectiveRevision ||
316
+ receivedAt >= objective.expiresAt ||
317
+ compare(verifiedAt, objective.validUntil) >= 0 ||
318
+ compare(verifiedAt, offer.bidDeadline) >= 0)
319
+ return reject(state, "offer_invalid");
320
+ if (Object.values(state.allocation.workAllocations).filter((entry) => entry.objectiveId === offer.objectiveId &&
321
+ (entry.phase === "award_pending" || entry.phase === "active")).length >= objective.maximumConcurrentAssignments)
322
+ return reject(state, "assignment_capacity_exceeded");
323
+ const parsed = validateSignedMeshEnvelope(command.recipient.envelope);
324
+ if (!parsed.ok || parsed.value.payload.type !== "work.award")
325
+ return reject(state, "award_invalid");
326
+ const envelope = parsed.value;
327
+ const payload = envelope.payload;
328
+ const acceptanceDeadlineAt = logicalDeadline(payload.acceptanceDeadline, verifiedAt, receivedAt);
329
+ const leaseExpiresAtLogical = logicalDeadline(payload.leaseExpiresAt, verifiedAt, receivedAt);
330
+ const signedAcceptanceDuration = logicalDeadline(payload.acceptanceDeadline, envelope.sentAt, 0);
331
+ const signedLeaseDuration = logicalDeadline(payload.leaseExpiresAt, payload.leaseStartsAt, 0);
332
+ const context = validateMeshEnvelopeContext(envelope, {
333
+ tenantId: state.allocation.identity.tenantId,
334
+ meshId: state.allocation.identity.meshId,
335
+ peerId: bid.bidderPeerId,
336
+ receivedAt: verifiedAt,
337
+ });
338
+ if (!context.ok ||
339
+ command.recipient.preparedAt !== receivedAt ||
340
+ !canonicalDigest(envelope) ||
341
+ !acceptanceDeadlineAt ||
342
+ !leaseExpiresAtLogical ||
343
+ !signedAcceptanceDuration ||
344
+ !signedLeaseDuration ||
345
+ acceptanceDeadlineAt <= receivedAt ||
346
+ leaseExpiresAtLogical <= receivedAt ||
347
+ signedAcceptanceDuration > objective.acceptanceWindowMs ||
348
+ signedLeaseDuration > objective.maximumLeaseDurationMs ||
349
+ leaseExpiresAtLogical > offer.work.workDeadlineAt ||
350
+ leaseExpiresAtLogical > objective.expiresAt ||
351
+ envelope.sender.peerId !== state.allocation.identity.peerId ||
352
+ envelope.sender.instanceId !== state.allocation.identity.instanceId ||
353
+ envelope.proof.keyId !== state.allocation.identity.keyId ||
354
+ envelope.audience.kind !== "peer" ||
355
+ envelope.audience.peerId !== bid.bidderPeerId ||
356
+ command.recipient.recipientPeerId !== bid.bidderPeerId ||
357
+ envelope.causationId !== bid.acceptedMessageId ||
358
+ payload.awardId === "" ||
359
+ payload.offerId !== offer.offerId ||
360
+ payload.bidId !== bid.bidId ||
361
+ payload.bidRevision !== bid.bidRevision ||
362
+ payload.assigneePeerId !== bid.bidderPeerId ||
363
+ payload.assignmentEpoch !== 1 ||
364
+ payload.authorityKind !== "award" ||
365
+ payload.assignmentAuthorityId !== payload.awardId ||
366
+ payload.fencingToken !== payload.awardId ||
367
+ payload.budgetReservationUnits !== reservation.budgetReservationUnits ||
368
+ payload.workDeadline !== offer.work.workDeadline ||
369
+ payload.objectiveId !== offer.objectiveId ||
370
+ payload.objectiveDocumentId !== offer.objectiveDocumentId ||
371
+ payload.objectiveRevision !== offer.objectiveRevision ||
372
+ payload.workItemId !== offer.work.workItemId ||
373
+ payload.workItemRevision !== offer.work.workItemRevision ||
374
+ payload.ownerPeerId !== state.allocation.identity.peerId ||
375
+ payload.ownerEpoch !== 1 ||
376
+ payload.offerAttempt !== offer.offerAttempt)
377
+ return reject(state, "award_invalid");
378
+ const awardKey = awardDomainKey(payload.awardId);
379
+ if (Object.values(state.coordination.domainRecords).some((record) => record.messageId === envelope.messageId) ||
380
+ Object.values(state.allocation.localOffers).some((entry) => Object.values(entry.recipientOffers).some((prepared) => prepared.messageId === envelope.messageId)) ||
381
+ Object.values(state.allocation.acceptedBidEvidence).some((entry) => entry.envelope.messageId === envelope.messageId) ||
382
+ Object.values(state.allocation.localAwards).some((entry) => entry.recipientAward.messageId === envelope.messageId))
383
+ return reject(state, "award_duplicate_conflict");
384
+ if (state.coordination.domainRecords[awardKey])
385
+ return reject(state, "domain_record_conflict");
386
+ const timerId = `allocation.acceptance.${payload.awardId}`;
387
+ if (state.coordination.timers[timerId])
388
+ return reject(state, "timer_id_conflict");
389
+ if (state.coordination.journal.length >=
390
+ state.coordination.limits.maximumJournalEntries)
391
+ return reject(state, "journal_capacity_exceeded");
392
+ if (Object.keys(state.coordination.domainRecords).length >=
393
+ state.coordination.limits.maximumDomainRecords)
394
+ return reject(state, "domain_capacity_exceeded");
395
+ if (Object.keys(state.allocation.localAwards).length >=
396
+ state.allocation.limits.maximumAwards)
397
+ return reject(state, "award_capacity_exceeded");
398
+ if (state.coordination.localEventSequence >= Number.MAX_SAFE_INTEGER)
399
+ throw new RangeError("Mesh coordination event sequence exhausted");
400
+ const award = Object.freeze({
401
+ objectiveId: offer.objectiveId,
402
+ objectiveDocumentId: offer.objectiveDocumentId,
403
+ objectiveRevision: offer.objectiveRevision,
404
+ objectivePolicy: offer.objectivePolicy,
405
+ work: offer.work,
406
+ awardId: payload.awardId,
407
+ offerId: payload.offerId,
408
+ bidId: payload.bidId,
409
+ bidRevision: payload.bidRevision,
410
+ offerAttempt: payload.offerAttempt,
411
+ assigneePeerId: payload.assigneePeerId,
412
+ assignmentEpoch: 1,
413
+ assignmentAuthorityId: payload.awardId,
414
+ fencingToken: payload.awardId,
415
+ budgetReservationUnits: payload.budgetReservationUnits,
416
+ workDeadline: payload.workDeadline,
417
+ leaseStartsAt: payload.leaseStartsAt,
418
+ leaseExpiresAt: payload.leaseExpiresAt,
419
+ leaseExpiresAtLogical,
420
+ acceptanceDeadline: payload.acceptanceDeadline,
421
+ acceptanceDeadlineAt,
422
+ acceptanceDeadlineTimerId: timerId,
423
+ acceptanceDeadlineTimerGeneration: 1,
424
+ createdAt: receivedAt,
425
+ validityVerifiedAt: verifiedAt,
426
+ reservationId: reservation.reservationId,
427
+ status: "awaiting_acceptance",
428
+ recipientAward: Object.freeze({
429
+ recipientPeerId: bid.bidderPeerId,
430
+ messageId: envelope.messageId,
431
+ preparedAt: command.recipient.preparedAt,
432
+ envelope,
433
+ }),
434
+ });
435
+ const allocation = restoreMeshAllocationState({
436
+ ...state.allocation,
437
+ workAllocations: createFrozenRecord([
438
+ ...recordEntries(state.allocation.workAllocations).filter(([key]) => key !== work.workKey),
439
+ [
440
+ work.workKey,
441
+ Object.freeze({
442
+ ...work,
443
+ phase: "award_pending",
444
+ activeOfferId: undefined,
445
+ bidDeadlineAt: undefined,
446
+ activeAwardId: payload.awardId,
447
+ updatedAt: receivedAt,
448
+ }),
449
+ ],
450
+ ]),
451
+ localAwards: createFrozenRecord([
452
+ ...recordEntries(state.allocation.localAwards),
453
+ [payload.awardId, award],
454
+ ]),
455
+ lastLogicalTime: receivedAt,
456
+ });
457
+ const sequence = state.coordination.localEventSequence + 1;
458
+ const coordination = Object.freeze({
459
+ ...state.coordination,
460
+ domainRecords: createFrozenRecord([
461
+ ...recordEntries(state.coordination.domainRecords),
462
+ [
463
+ awardKey,
464
+ Object.freeze({
465
+ recordKey: awardKey,
466
+ recordType: "work.award",
467
+ recordId: payload.awardId,
468
+ contentDigest: payloadDigest(envelope),
469
+ messageId: envelope.messageId,
470
+ acceptedAt: receivedAt,
471
+ objectiveId: payload.objectiveId,
472
+ }),
473
+ ],
474
+ ]),
475
+ timers: createFrozenRecord([
476
+ ...recordEntries(state.coordination.timers).filter(([key]) => key !== bidTimer.timerId),
477
+ [
478
+ timerId,
479
+ Object.freeze({
480
+ timerId,
481
+ kind: "work.acceptance_deadline",
482
+ dueAt: acceptanceDeadlineAt,
483
+ generation: 1,
484
+ domainRecordKey: awardKey,
485
+ }),
486
+ ],
487
+ ]),
488
+ journal: Object.freeze([
489
+ ...state.coordination.journal,
490
+ Object.freeze({
491
+ sequence,
492
+ occurredAt: receivedAt,
493
+ kind: "command.accepted",
494
+ domainRecordKey: awardKey,
495
+ }),
496
+ ]),
497
+ localEventSequence: sequence,
498
+ lastLogicalTime: receivedAt,
499
+ });
500
+ return Object.freeze({
501
+ accepted: true,
502
+ duplicate: false,
503
+ state: createMeshAllocationRuntimeState(coordination, Object.freeze({ ...state.discovery, lastLogicalTime: receivedAt }), Object.freeze({ ...state.objectives, lastLogicalTime: receivedAt }), allocation),
504
+ effects: Object.freeze([
505
+ Object.freeze({
506
+ kind: "allocation.award.dispatch",
507
+ awardId: payload.awardId,
508
+ recipientPeerId: bid.bidderPeerId,
509
+ messageId: envelope.messageId,
510
+ envelope,
511
+ }),
512
+ ]),
513
+ });
514
+ }
515
+ function evaluateRecoveryAwardCommand(state, command, verifiedAt, receivedAt) {
516
+ if (Object.keys(command).sort().join(",") !== "certificateId,kind,recipient" ||
517
+ !command.recipient ||
518
+ Object.keys(command.recipient).sort().join(",") !==
519
+ "envelope,preparedAt,recipientPeerId" ||
520
+ command.recipient.preparedAt !== receivedAt)
521
+ throw new TypeError("invalid mesh local recovery award command");
522
+ const certificate = state.allocation.recoveryCertificates[command.certificateId];
523
+ const proposal = certificate === undefined
524
+ ? undefined
525
+ : state.allocation.takeoverProposals[certificate.takeoverProposalId];
526
+ if (certificate === undefined || proposal === undefined)
527
+ return reject(state, "recovery_invalid");
528
+ const proposed = proposal.envelope.payload;
529
+ const fenceKey = meshAssignmentFenceKey(proposed);
530
+ const fence = state.allocation.assignmentFenceHeads[fenceKey];
531
+ const work = state.allocation.workAllocations[workKey(proposed.objectiveId, proposed.workItemId)];
532
+ const reservation = work?.reservationId === undefined
533
+ ? undefined
534
+ : state.allocation.reservations[work.reservationId];
535
+ const offer = reservation === undefined
536
+ ? undefined
537
+ : state.allocation.localOffers[reservation.offerId];
538
+ const parsed = validateSignedMeshEnvelope(command.recipient.envelope);
539
+ if (!parsed.ok || parsed.value.payload.type !== "work.award")
540
+ return reject(state, "award_invalid");
541
+ const envelope = parsed.value;
542
+ const payload = envelope.payload;
543
+ const bid = Object.values(state.allocation.bidHeads).find((candidate) => candidate.offerId === payload.offerId &&
544
+ candidate.bidId === payload.bidId &&
545
+ candidate.bidRevision === payload.bidRevision &&
546
+ candidate.bidderPeerId === proposed.proposedAssigneePeerId);
547
+ const objective = state.objectives.objectives[proposed.objectiveId];
548
+ const acceptanceDeadlineAt = logicalDeadline(payload.acceptanceDeadline, verifiedAt, receivedAt);
549
+ const leaseExpiresAtLogical = logicalDeadline(payload.leaseExpiresAt, verifiedAt, receivedAt);
550
+ const signedAcceptanceDuration = logicalDeadline(payload.acceptanceDeadline, envelope.sentAt, 0);
551
+ const signedLeaseDuration = logicalDeadline(payload.leaseExpiresAt, payload.leaseStartsAt, 0);
552
+ const checkpointIds = new Set([
553
+ ...Object.values(state.allocation.executionHeads)
554
+ .filter((head) => meshAssignmentFenceKey(head) === fenceKey &&
555
+ head.assignmentEpoch === proposed.proposedAssignmentEpoch - 1)
556
+ .map((head) => head.latestCheckpointId),
557
+ state.allocation.witnessAssignments[fenceKey]?.latestCheckpoint?.recordId,
558
+ ].filter((checkpointId) => checkpointId !== undefined));
559
+ if (checkpointIds.size > 1)
560
+ return reject(state, "recovery_checkpoint_invalid");
561
+ const resumeCheckpointId = [...checkpointIds][0];
562
+ const context = validateMeshEnvelopeContext(envelope, {
563
+ tenantId: state.allocation.identity.tenantId,
564
+ meshId: state.allocation.identity.meshId,
565
+ peerId: proposed.proposedAssigneePeerId,
566
+ receivedAt: verifiedAt,
567
+ });
568
+ if (!context.ok ||
569
+ !canonicalDigest(envelope) ||
570
+ fence === undefined ||
571
+ fence.phase !== "recovering" ||
572
+ fence.recoveryCertificateId !== certificate.certificateId ||
573
+ fence.assignmentEpoch !== proposed.proposedAssignmentEpoch ||
574
+ fence.assignmentAuthorityId !== certificate.certificateId ||
575
+ fence.fencingToken !== certificate.certificateId ||
576
+ fence.assigneePeerId !== proposed.proposedAssigneePeerId ||
577
+ fence.activeAwardId !== undefined ||
578
+ work === undefined ||
579
+ work.phase !== "recovering" ||
580
+ reservation === undefined ||
581
+ reservation.status !== "committed" ||
582
+ offer === undefined ||
583
+ bid === undefined ||
584
+ objective === undefined ||
585
+ objective.status !== "active" ||
586
+ receivedAt >= objective.expiresAt ||
587
+ compare(verifiedAt, objective.validUntil) >= 0 ||
588
+ acceptanceDeadlineAt === undefined ||
589
+ leaseExpiresAtLogical === undefined ||
590
+ signedAcceptanceDuration === undefined ||
591
+ signedLeaseDuration === undefined ||
592
+ acceptanceDeadlineAt <= receivedAt ||
593
+ leaseExpiresAtLogical <= receivedAt ||
594
+ signedAcceptanceDuration > work.objectivePolicy.acceptanceWindowMs ||
595
+ signedLeaseDuration > work.objectivePolicy.maximumLeaseDurationMs ||
596
+ leaseExpiresAtLogical > work.work.workDeadlineAt ||
597
+ leaseExpiresAtLogical > objective.expiresAt ||
598
+ envelope.sender.peerId !== state.allocation.identity.peerId ||
599
+ envelope.sender.instanceId !== state.allocation.identity.instanceId ||
600
+ envelope.proof.keyId !== state.allocation.identity.keyId ||
601
+ envelope.audience.kind !== "peer" ||
602
+ envelope.audience.peerId !== proposed.proposedAssigneePeerId ||
603
+ command.recipient.recipientPeerId !== proposed.proposedAssigneePeerId ||
604
+ envelope.causationId !==
605
+ (certificate.recipientEnvelopes?.[proposed.proposedAssigneePeerId] ??
606
+ certificate.envelope).messageId ||
607
+ payload.authorityKind !== "recovery_certificate" ||
608
+ payload.recoveryCertificateId !== certificate.certificateId ||
609
+ payload.assignmentAuthorityId !== certificate.certificateId ||
610
+ payload.fencingToken !== certificate.certificateId ||
611
+ payload.assignmentEpoch !== proposed.proposedAssignmentEpoch ||
612
+ payload.assigneePeerId !== proposed.proposedAssigneePeerId ||
613
+ payload.resumeCheckpointId !== resumeCheckpointId ||
614
+ payload.objectiveId !== proposed.objectiveId ||
615
+ payload.objectiveDocumentId !== proposed.objectiveDocumentId ||
616
+ payload.objectiveRevision !== proposed.objectiveRevision ||
617
+ payload.workItemId !== proposed.workItemId ||
618
+ payload.workItemRevision !== proposed.workItemRevision ||
619
+ payload.ownerPeerId !== proposed.ownerPeerId ||
620
+ payload.ownerEpoch !== proposed.ownerEpoch ||
621
+ payload.offerId !== offer.offerId ||
622
+ payload.offerAttempt !== offer.offerAttempt ||
623
+ payload.bidId !== bid.bidId ||
624
+ payload.bidRevision !== bid.bidRevision ||
625
+ payload.budgetReservationUnits !== reservation.budgetReservationUnits ||
626
+ payload.workDeadline !== work.work.workDeadline)
627
+ return reject(state, "award_invalid");
628
+ if (Object.values(state.allocation.localAwards).some((award) => meshAssignmentFenceKey({
629
+ objectiveId: award.objectiveId,
630
+ objectiveRevision: award.objectiveRevision,
631
+ workItemId: award.work.workItemId,
632
+ workItemRevision: award.work.workItemRevision,
633
+ ownerPeerId: award.work.ownerPeerId,
634
+ ownerEpoch: award.work.ownerEpoch,
635
+ }) === fenceKey &&
636
+ award.assignmentEpoch === proposed.proposedAssignmentEpoch) ||
637
+ meshAllocationRetainsMessageId(state, envelope.messageId))
638
+ return reject(state, "award_duplicate_conflict");
639
+ const awardKey = awardDomainKey(payload.awardId);
640
+ const timerId = `allocation.acceptance.${payload.awardId}`;
641
+ if (state.coordination.domainRecords[awardKey])
642
+ return reject(state, "domain_record_conflict");
643
+ if (state.coordination.timers[timerId])
644
+ return reject(state, "timer_id_conflict");
645
+ if (Object.keys(state.allocation.localAwards).length >=
646
+ state.allocation.limits.maximumAwards)
647
+ return reject(state, "award_capacity_exceeded");
648
+ const capacity = allocationWriteCapacity(state, true);
649
+ if (capacity)
650
+ return reject(state, capacity);
651
+ const award = Object.freeze({
652
+ objectiveId: work.objectiveId,
653
+ objectiveDocumentId: work.objectiveDocumentId,
654
+ objectiveRevision: work.objectiveRevision,
655
+ objectivePolicy: work.objectivePolicy,
656
+ work: work.work,
657
+ awardId: payload.awardId,
658
+ offerId: payload.offerId,
659
+ bidId: payload.bidId,
660
+ bidRevision: payload.bidRevision,
661
+ offerAttempt: payload.offerAttempt,
662
+ assigneePeerId: payload.assigneePeerId,
663
+ assignmentEpoch: payload.assignmentEpoch,
664
+ assignmentAuthorityId: payload.assignmentAuthorityId,
665
+ fencingToken: payload.fencingToken,
666
+ budgetReservationUnits: payload.budgetReservationUnits,
667
+ workDeadline: payload.workDeadline,
668
+ leaseStartsAt: payload.leaseStartsAt,
669
+ leaseExpiresAt: payload.leaseExpiresAt,
670
+ leaseExpiresAtLogical,
671
+ acceptanceDeadline: payload.acceptanceDeadline,
672
+ acceptanceDeadlineAt,
673
+ acceptanceDeadlineTimerId: timerId,
674
+ acceptanceDeadlineTimerGeneration: 1,
675
+ createdAt: receivedAt,
676
+ validityVerifiedAt: verifiedAt,
677
+ reservationId: reservation.reservationId,
678
+ status: "awaiting_acceptance",
679
+ recipientAward: Object.freeze({
680
+ recipientPeerId: proposed.proposedAssigneePeerId,
681
+ messageId: envelope.messageId,
682
+ preparedAt: receivedAt,
683
+ envelope,
684
+ }),
685
+ });
686
+ const allocation = restoreMeshAllocationState({
687
+ ...state.allocation,
688
+ workAllocations: createFrozenRecord([
689
+ ...recordEntries(state.allocation.workAllocations).filter(([key]) => key !== work.workKey),
690
+ [
691
+ work.workKey,
692
+ Object.freeze({
693
+ ...work,
694
+ phase: "award_pending",
695
+ activeAwardId: payload.awardId,
696
+ activeAcceptanceId: undefined,
697
+ updatedAt: receivedAt,
698
+ }),
699
+ ],
700
+ ]),
701
+ localAwards: createFrozenRecord([
702
+ ...recordEntries(state.allocation.localAwards),
703
+ [payload.awardId, award],
704
+ ]),
705
+ assignmentFenceHeads: createFrozenRecord([
706
+ ...recordEntries(state.allocation.assignmentFenceHeads).filter(([key]) => key !== fenceKey),
707
+ [
708
+ fenceKey,
709
+ Object.freeze({
710
+ ...fence,
711
+ activeAwardId: payload.awardId,
712
+ phase: "award_pending",
713
+ }),
714
+ ],
715
+ ]),
716
+ lastLogicalTime: receivedAt,
717
+ });
718
+ const sequence = state.coordination.localEventSequence + 1;
719
+ const coordination = Object.freeze({
720
+ ...state.coordination,
721
+ domainRecords: createFrozenRecord([
722
+ ...recordEntries(state.coordination.domainRecords),
723
+ [
724
+ awardKey,
725
+ Object.freeze({
726
+ recordKey: awardKey,
727
+ recordType: "work.award",
728
+ recordId: payload.awardId,
729
+ contentDigest: payloadDigest(envelope),
730
+ messageId: envelope.messageId,
731
+ acceptedAt: receivedAt,
732
+ objectiveId: payload.objectiveId,
733
+ }),
734
+ ],
735
+ ]),
736
+ timers: createFrozenRecord([
737
+ ...recordEntries(state.coordination.timers),
738
+ [
739
+ timerId,
740
+ Object.freeze({
741
+ timerId,
742
+ kind: "work.acceptance_deadline",
743
+ dueAt: acceptanceDeadlineAt,
744
+ generation: 1,
745
+ domainRecordKey: awardKey,
746
+ }),
747
+ ],
748
+ ]),
749
+ journal: Object.freeze([
750
+ ...state.coordination.journal,
751
+ Object.freeze({
752
+ sequence,
753
+ occurredAt: receivedAt,
754
+ kind: "command.accepted",
755
+ domainRecordKey: awardKey,
756
+ }),
757
+ ]),
758
+ localEventSequence: sequence,
759
+ lastLogicalTime: receivedAt,
760
+ });
761
+ return Object.freeze({
762
+ accepted: true,
763
+ duplicate: false,
764
+ state: createMeshAllocationRuntimeState(coordination, Object.freeze({ ...state.discovery, lastLogicalTime: receivedAt }), Object.freeze({ ...state.objectives, lastLogicalTime: receivedAt }), allocation),
765
+ effects: Object.freeze([
766
+ Object.freeze({
767
+ kind: "allocation.award.dispatch",
768
+ awardId: payload.awardId,
769
+ recipientPeerId: proposed.proposedAssigneePeerId,
770
+ messageId: envelope.messageId,
771
+ envelope,
772
+ }),
773
+ ]),
774
+ });
775
+ }
776
+ function evaluateBidCommand(state, command, verifiedAt, receivedAt) {
777
+ if (Object.keys(command).sort().join(",") !==
778
+ "envelope,kind,offerId,preparedAt" ||
779
+ typeof command.offerId !== "string" ||
780
+ !Number.isSafeInteger(command.preparedAt) ||
781
+ command.preparedAt < 0)
782
+ throw new TypeError("invalid mesh local bid command");
783
+ const offer = state.allocation.receivedOffers[command.offerId];
784
+ if (!offer)
785
+ return reject(state, "offer_missing");
786
+ if (Object.values(state.allocation.receivedAwards).some((award) => award.offerId === offer.offerId))
787
+ return reject(state, "local_bid_invalid");
788
+ const objective = state.objectives.objectives[offer.envelope.payload.objectiveId];
789
+ if (!objective ||
790
+ objective.status !== "active" ||
791
+ objective.objectiveDocumentId !==
792
+ offer.envelope.payload.objectiveDocumentId ||
793
+ objective.objectiveRevision !== offer.envelope.payload.objectiveRevision ||
794
+ receivedAt >= objective.expiresAt ||
795
+ compare(verifiedAt, objective.validUntil) >= 0)
796
+ return reject(state, "local_bid_invalid");
797
+ if (receivedAt >= offer.bidDeadlineAt ||
798
+ compare(verifiedAt, offer.envelope.payload.bidDeadline) >= 0)
799
+ return reject(state, "bid_deadline_elapsed");
800
+ const parsed = validateSignedMeshEnvelope(command.envelope);
801
+ if (!parsed.ok || parsed.value.payload.type !== "work.bid")
802
+ return reject(state, "local_bid_invalid");
803
+ const envelope = parsed.value;
804
+ const payload = envelope.payload;
805
+ const bidExpiresAtLogical = logicalDeadline(payload.bidExpiresAt, verifiedAt, receivedAt);
806
+ const context = validateMeshEnvelopeContext(envelope, {
807
+ tenantId: state.allocation.identity.tenantId,
808
+ meshId: state.allocation.identity.meshId,
809
+ peerId: offer.envelope.sender.peerId,
810
+ receivedAt: verifiedAt,
811
+ });
812
+ if (!context.ok ||
813
+ !canonicalDigest(envelope) ||
814
+ command.preparedAt !== receivedAt ||
815
+ !bidExpiresAtLogical ||
816
+ bidExpiresAtLogical <= receivedAt ||
817
+ bidExpiresAtLogical > offer.workDeadlineAt ||
818
+ payload.budgetUnits > offer.envelope.payload.budgetReservationUnits ||
819
+ envelope.sender.peerId !== state.allocation.identity.peerId ||
820
+ envelope.sender.instanceId !== state.allocation.identity.instanceId ||
821
+ envelope.proof.keyId !== state.allocation.identity.keyId ||
822
+ envelope.audience.kind !== "peer" ||
823
+ envelope.audience.peerId !== offer.envelope.sender.peerId ||
824
+ envelope.causationId !== offer.envelope.messageId ||
825
+ payload.bidderPeerId !== state.allocation.identity.peerId ||
826
+ !matchesReceivedOffer(payload, offer))
827
+ return reject(state, "local_bid_invalid");
828
+ const prior = Object.values(state.allocation.localBids)
829
+ .filter((bid) => bid.offerId === offer.offerId)
830
+ .sort((left, right) => left.bidRevision - right.bidRevision)
831
+ .at(-1);
832
+ if (payload.bidRevision !== (prior?.bidRevision ?? 0) + 1 ||
833
+ payload.previousBidId !== prior?.bidId)
834
+ return reject(state, "bid_predecessor_invalid");
835
+ if (state.allocation.localBids[payload.bidId])
836
+ return sameData(state.allocation.localBids[payload.bidId].envelope, envelope)
837
+ ? Object.freeze({
838
+ accepted: true,
839
+ duplicate: true,
840
+ state,
841
+ effects: Object.freeze([]),
842
+ })
843
+ : reject(state, "local_bid_duplicate_conflict");
844
+ const domainKey = bidDomainKey(payload.bidId);
845
+ if (meshAllocationRetainsMessageId(state, envelope.messageId) ||
846
+ state.coordination.domainRecords[domainKey])
847
+ return reject(state, "local_bid_duplicate_conflict");
848
+ if (Object.keys(state.allocation.localBids).length >=
849
+ state.allocation.limits.maximumLocalBids)
850
+ return reject(state, "local_bid_capacity_exceeded");
851
+ const capacity = allocationWriteCapacity(state);
852
+ if (capacity)
853
+ return reject(state, capacity);
854
+ const bid = Object.freeze({
855
+ bidId: payload.bidId,
856
+ offerId: payload.offerId,
857
+ bidRevision: payload.bidRevision,
858
+ ...(payload.previousBidId === undefined
859
+ ? {}
860
+ : { previousBidId: payload.previousBidId }),
861
+ preparedAt: receivedAt,
862
+ validityVerifiedAt: verifiedAt,
863
+ bidExpiresAt: payload.bidExpiresAt,
864
+ bidExpiresAtLogical,
865
+ envelope,
866
+ });
867
+ return acceptAssigneeWrite(state, receivedAt, domainKey, "work.bid", payload.bidId, payload.objectiveId, envelope, {
868
+ ...state.allocation,
869
+ localBids: createFrozenRecord([
870
+ ...recordEntries(state.allocation.localBids),
871
+ [payload.bidId, bid],
872
+ ]),
873
+ lastLogicalTime: receivedAt,
874
+ }, Object.freeze({
875
+ kind: "allocation.bid.dispatch",
876
+ bidId: payload.bidId,
877
+ recipientPeerId: offer.envelope.sender.peerId,
878
+ messageId: envelope.messageId,
879
+ envelope,
880
+ }));
881
+ }
882
+ function evaluateLocalAssignmentResponseCommand(state, command, verifiedAt, receivedAt) {
883
+ if (Object.keys(command).sort().join(",") !==
884
+ "awardId,envelope,kind,preparedAt" ||
885
+ typeof command.awardId !== "string" ||
886
+ !Number.isSafeInteger(command.preparedAt) ||
887
+ command.preparedAt < 0)
888
+ throw new TypeError("invalid mesh local assignment response command");
889
+ const award = state.allocation.receivedAwards[command.awardId];
890
+ if (!award)
891
+ return reject(state, "award_missing");
892
+ const existing = state.allocation.localAssignmentResponses[award.awardId];
893
+ if (existing)
894
+ return sameData(existing.envelope, command.envelope)
895
+ ? Object.freeze({
896
+ accepted: true,
897
+ duplicate: true,
898
+ state,
899
+ effects: Object.freeze([]),
900
+ })
901
+ : reject(state, "local_assignment_response_duplicate_conflict");
902
+ if (award.status !== "awaiting_response" ||
903
+ receivedAt >= award.acceptanceDeadlineAt ||
904
+ compare(verifiedAt, award.envelope.payload.acceptanceDeadline) >= 0)
905
+ return reject(state, "local_assignment_response_deadline_elapsed");
906
+ const parsed = validateSignedMeshEnvelope(command.envelope);
907
+ if (!parsed.ok ||
908
+ (parsed.value.payload.type !== "work.accept" &&
909
+ parsed.value.payload.type !== "work.decline"))
910
+ return reject(state, "local_assignment_response_invalid");
911
+ const envelope = parsed.value;
912
+ const payload = envelope.payload;
913
+ const objective = state.objectives.objectives[payload.objectiveId];
914
+ const policy = state.objectives.objectivePolicies[JSON.stringify([payload.objectiveId, payload.objectiveRevision])];
915
+ const responseId = payload.type === "work.accept" ? payload.acceptanceId : payload.declineId;
916
+ const recoveryAward = award.envelope.payload.authorityKind === "recovery_certificate";
917
+ const recoveryFenceKey = recoveryAward
918
+ ? meshAssignmentFenceKey(award.envelope.payload)
919
+ : undefined;
920
+ const recoveryFence = recoveryFenceKey === undefined
921
+ ? undefined
922
+ : state.allocation.assignmentFenceHeads[recoveryFenceKey];
923
+ const context = validateMeshEnvelopeContext(envelope, {
924
+ tenantId: state.allocation.identity.tenantId,
925
+ meshId: state.allocation.identity.meshId,
926
+ peerId: award.envelope.sender.peerId,
927
+ receivedAt: verifiedAt,
928
+ });
929
+ if (!context.ok ||
930
+ !canonicalDigest(envelope) ||
931
+ !objective ||
932
+ objective.status !== "active" ||
933
+ !policy ||
934
+ policy.objectiveDocumentId !== payload.objectiveDocumentId ||
935
+ receivedAt >= policy.expiresAt ||
936
+ compare(verifiedAt, policy.validUntil) >= 0 ||
937
+ command.preparedAt !== receivedAt ||
938
+ envelope.sender.peerId !== state.allocation.identity.peerId ||
939
+ envelope.sender.instanceId !== state.allocation.identity.instanceId ||
940
+ envelope.proof.keyId !== state.allocation.identity.keyId ||
941
+ envelope.audience.kind !== "peer" ||
942
+ envelope.audience.peerId !== award.envelope.sender.peerId ||
943
+ envelope.causationId !== award.envelope.messageId ||
944
+ !matchesAwardResponse(payload, award) ||
945
+ (recoveryAward &&
946
+ (recoveryFence === undefined ||
947
+ recoveryFence.phase !== "award_pending" ||
948
+ recoveryFence.activeAwardId !== award.awardId ||
949
+ recoveryFence.assignmentEpoch !== payload.assignmentEpoch ||
950
+ recoveryFence.assignmentAuthorityId !== payload.assignmentAuthorityId ||
951
+ recoveryFence.fencingToken !== payload.fencingToken ||
952
+ recoveryFence.assigneePeerId !== payload.assigneePeerId ||
953
+ recoveryFence.recoveryCertificateId !==
954
+ award.envelope.payload.recoveryCertificateId)) ||
955
+ meshAllocationRetainsMessageId(state, envelope.messageId))
956
+ return reject(state, "local_assignment_response_invalid");
957
+ const domainKey = JSON.stringify([payload.type, responseId]);
958
+ if (state.coordination.domainRecords[domainKey])
959
+ return reject(state, "domain_record_conflict");
960
+ if (Object.keys(state.allocation.localAssignmentResponses).length >=
961
+ state.allocation.limits.maximumLocalAssignmentResponses)
962
+ return reject(state, "local_assignment_response_capacity_exceeded");
963
+ if (payload.type === "work.accept" &&
964
+ Object.keys(state.allocation.assigneeAuthorities).length >=
965
+ state.allocation.limits.maximumAssignmentAuthorities)
966
+ return reject(state, "assignment_authority_capacity_exceeded");
967
+ if (payload.type === "work.accept" &&
968
+ Object.keys(state.allocation.leaseHeads).length >=
969
+ state.allocation.limits.maximumExecutionHeads)
970
+ return reject(state, "execution_head_capacity_exceeded");
971
+ const capacity = allocationWriteCapacity(state);
972
+ if (capacity)
973
+ return reject(state, capacity);
974
+ const response = Object.freeze({
975
+ awardId: award.awardId,
976
+ responseId,
977
+ kind: payload.type,
978
+ preparedAt: receivedAt,
979
+ validityVerifiedAt: verifiedAt,
980
+ envelope,
981
+ });
982
+ const accepted = payload.type === "work.accept";
983
+ if (accepted &&
984
+ Object.values(state.allocation.assigneeAuthorities).some((authority) => authority.objectiveId === payload.objectiveId &&
985
+ authority.workItemId === payload.workItemId &&
986
+ authority.workItemRevision === payload.workItemRevision &&
987
+ authority.assignmentEpoch === payload.assignmentEpoch &&
988
+ authority.assignmentAuthorityId !== payload.assignmentAuthorityId))
989
+ return reject(state, "local_assignment_response_invalid");
990
+ const authority = !accepted
991
+ ? undefined
992
+ : Object.freeze({
993
+ awardId: award.awardId,
994
+ acceptanceId: responseId,
995
+ objectiveId: payload.objectiveId,
996
+ objectiveDocumentId: payload.objectiveDocumentId,
997
+ objectiveRevision: payload.objectiveRevision,
998
+ workItemId: payload.workItemId,
999
+ workItemRevision: payload.workItemRevision,
1000
+ ownerPeerId: payload.ownerPeerId,
1001
+ ownerEpoch: payload.ownerEpoch,
1002
+ assigneePeerId: payload.assigneePeerId,
1003
+ assignmentEpoch: payload.assignmentEpoch,
1004
+ assignmentAuthorityId: payload.assignmentAuthorityId,
1005
+ fencingToken: payload.fencingToken,
1006
+ workDeadline: award.envelope.payload.workDeadline,
1007
+ workDeadlineAt: state.allocation.receivedOffers[award.offerId].workDeadlineAt,
1008
+ leaseExpiresAt: award.envelope.payload.leaseExpiresAt,
1009
+ leaseExpiresAtLogical: award.leaseExpiresAtLogical,
1010
+ activatedAt: receivedAt,
1011
+ });
1012
+ const leaseActivation = authority === undefined
1013
+ ? undefined
1014
+ : createInitialLeaseActivation({
1015
+ ...authority,
1016
+ acceptanceMessageId: response.envelope.messageId,
1017
+ });
1018
+ if (leaseActivation !== undefined &&
1019
+ state.coordination.timers[leaseActivation.timer.timerId] !== undefined)
1020
+ return reject(state, "timer_id_conflict");
1021
+ return acceptAssigneeWrite(state, receivedAt, domainKey, payload.type, responseId, payload.objectiveId, envelope, {
1022
+ ...state.allocation,
1023
+ receivedAwards: createFrozenRecord([
1024
+ ...recordEntries(state.allocation.receivedAwards).filter(([key]) => key !== award.awardId),
1025
+ [
1026
+ award.awardId,
1027
+ Object.freeze({
1028
+ ...award,
1029
+ status: accepted ? "accepted" : "declined",
1030
+ }),
1031
+ ],
1032
+ ]),
1033
+ localAssignmentResponses: createFrozenRecord([
1034
+ ...recordEntries(state.allocation.localAssignmentResponses),
1035
+ [award.awardId, response],
1036
+ ]),
1037
+ assigneeAuthorities: createFrozenRecord(accepted
1038
+ ? [
1039
+ ...recordEntries(state.allocation.assigneeAuthorities),
1040
+ [award.awardId, authority],
1041
+ ]
1042
+ : recordEntries(state.allocation.assigneeAuthorities)),
1043
+ leaseHeads: createFrozenRecord(leaseActivation === undefined
1044
+ ? recordEntries(state.allocation.leaseHeads)
1045
+ : [
1046
+ ...recordEntries(state.allocation.leaseHeads),
1047
+ [leaseActivation.head.executionScopeKey, leaseActivation.head],
1048
+ ]),
1049
+ ...(recoveryFenceKey === undefined || recoveryFence === undefined
1050
+ ? {}
1051
+ : {
1052
+ assignmentFenceHeads: createFrozenRecord([
1053
+ ...recordEntries(state.allocation.assignmentFenceHeads).filter(([key]) => key !== recoveryFenceKey),
1054
+ [
1055
+ recoveryFenceKey,
1056
+ Object.freeze({
1057
+ ...recoveryFence,
1058
+ activeAwardId: accepted ? award.awardId : undefined,
1059
+ phase: accepted
1060
+ ? "active"
1061
+ : "recovering",
1062
+ }),
1063
+ ],
1064
+ ]),
1065
+ }),
1066
+ lastLogicalTime: receivedAt,
1067
+ }, Object.freeze({
1068
+ kind: "allocation.assignment_response.dispatch",
1069
+ awardId: award.awardId,
1070
+ responseId,
1071
+ recipientPeerId: award.envelope.sender.peerId,
1072
+ messageId: envelope.messageId,
1073
+ envelope,
1074
+ }), leaseActivation?.timer, award.acceptanceDeadlineTimerId);
1075
+ }
1076
+ /** Evaluates a verified direct bid at the local offer owner. */
1077
+ export function evaluateVerifiedMeshAllocationEnvelope(state, request) {
1078
+ assertRuntime(state, request.receivedAt);
1079
+ const context = validateContext(state, request);
1080
+ if (context)
1081
+ return reject(state, context);
1082
+ if (request.envelope.payload.type === "lease.takeover_proposal" ||
1083
+ request.envelope.payload.type === "lease.vote" ||
1084
+ request.envelope.payload.type === "lease.certificate")
1085
+ return evaluateVerifiedMeshRecoveryEnvelope(state, request);
1086
+ if (isWitnessCopy(state, request))
1087
+ return evaluateVerifiedMeshWitnessEnvelope(state, request);
1088
+ if (request.envelope.payload.type === "work.offer")
1089
+ return evaluateReceivedOffer(state, request);
1090
+ if (request.envelope.payload.type === "work.award")
1091
+ return evaluateReceivedAward(state, request);
1092
+ if (request.envelope.payload.type === "work.accept" ||
1093
+ request.envelope.payload.type === "work.decline")
1094
+ return evaluateAssignmentResponse(state, request);
1095
+ if (request.envelope.payload.type === "work.progress" ||
1096
+ request.envelope.payload.type === "work.checkpoint" ||
1097
+ request.envelope.payload.type === "work.result" ||
1098
+ request.envelope.payload.type === "work.release" ||
1099
+ request.envelope.payload.type === "work.cancel")
1100
+ return evaluateVerifiedMeshExecutionEnvelope(state, request);
1101
+ if (request.envelope.payload.type === "lease.renew")
1102
+ return evaluateVerifiedMeshLeaseRenewalEnvelope(state, request);
1103
+ if (request.envelope.payload.type !== "work.bid")
1104
+ return reject(state, "invalid_verified_envelope");
1105
+ const envelope = request.envelope;
1106
+ const payload = envelope.payload;
1107
+ const offer = state.allocation.localOffers[payload.offerId];
1108
+ if (!offer)
1109
+ return reject(state, "offer_missing");
1110
+ const allocationWork = state.allocation.workAllocations[workKey(offer.objectiveId, offer.work.workItemId)];
1111
+ const reservation = state.allocation.reservations[offer.reservationId];
1112
+ const timer = state.coordination.timers[offer.bidDeadlineTimerId];
1113
+ if (!allocationWork ||
1114
+ allocationWork.phase !== "offered" ||
1115
+ allocationWork.activeOfferId !== offer.offerId ||
1116
+ allocationWork.reservationId !== offer.reservationId ||
1117
+ !reservation ||
1118
+ reservation.status !== "reserved" ||
1119
+ !timer ||
1120
+ timer.kind !== "work.bid_deadline" ||
1121
+ timer.generation !== offer.bidDeadlineTimerGeneration)
1122
+ return reject(state, "bid_deadline_elapsed");
1123
+ if (request.receivedAt >= offer.bidDeadlineAt ||
1124
+ compare(request.verifiedAt, offer.bidDeadline) >= 0)
1125
+ return reject(state, "bid_deadline_elapsed");
1126
+ if (envelope.audience.kind !== "peer" ||
1127
+ envelope.audience.peerId !== state.allocation.identity.peerId ||
1128
+ envelope.sender.peerId !== payload.bidderPeerId)
1129
+ return reject(state, "audience_mismatch");
1130
+ const prepared = offer.recipientOffers[payload.bidderPeerId];
1131
+ if (!prepared)
1132
+ return reject(state, "bidder_not_recipient");
1133
+ if (envelope.causationId !== prepared.messageId)
1134
+ return reject(state, "bid_causation_invalid");
1135
+ if (!matchesOffer(payload, offer) ||
1136
+ payload.bidDeadline !== offer.bidDeadline ||
1137
+ payload.workDeadline !== offer.work.workDeadline ||
1138
+ payload.budgetUnits > reservation.budgetReservationUnits)
1139
+ return reject(state, "bid_invalid");
1140
+ if (!hasActiveCapability(state, offer, envelope, request.verifiedAt, request.receivedAt))
1141
+ return reject(state, "bid_invalid");
1142
+ const bidExpiresAtLogical = logicalDeadline(payload.bidExpiresAt, request.verifiedAt, request.receivedAt);
1143
+ if (bidExpiresAtLogical === undefined ||
1144
+ bidExpiresAtLogical <= request.receivedAt)
1145
+ return reject(state, "bid_invalid");
1146
+ const key = bidKey(payload.offerId, payload.bidderPeerId);
1147
+ const current = state.allocation.bidHeads[key];
1148
+ const domainKey = bidDomainKey(payload.bidId);
1149
+ const messageDomain = Object.values(state.coordination.domainRecords).find((record) => record.messageId === envelope.messageId);
1150
+ if ((messageDomain && messageDomain.recordKey !== domainKey) ||
1151
+ Object.values(state.allocation.localOffers).some((localOffer) => Object.values(localOffer.recipientOffers).some((recipient) => recipient.messageId === envelope.messageId)))
1152
+ return reject(state, "bid_duplicate_conflict");
1153
+ const existingDomain = state.coordination.domainRecords[domainKey];
1154
+ if (existingDomain)
1155
+ return existingDomain.contentDigest === payloadDigest(envelope) &&
1156
+ existingDomain.messageId === envelope.messageId
1157
+ ? Object.freeze({
1158
+ accepted: true,
1159
+ duplicate: true,
1160
+ state,
1161
+ effects: Object.freeze([]),
1162
+ })
1163
+ : reject(state, "domain_record_conflict");
1164
+ if (current &&
1165
+ (payload.bidRevision !== current.bidRevision + 1 ||
1166
+ payload.previousBidId !== current.bidId))
1167
+ return reject(state, "bid_predecessor_invalid");
1168
+ if (!current &&
1169
+ (payload.bidRevision !== 1 || payload.previousBidId !== undefined))
1170
+ return reject(state, "bid_predecessor_invalid");
1171
+ if (Object.keys(state.coordination.domainRecords).length >=
1172
+ state.coordination.limits.maximumDomainRecords)
1173
+ return reject(state, "domain_capacity_exceeded");
1174
+ if (state.coordination.journal.length >=
1175
+ state.coordination.limits.maximumJournalEntries)
1176
+ return reject(state, "journal_capacity_exceeded");
1177
+ if (!current &&
1178
+ Object.keys(state.allocation.bidHeads).length >=
1179
+ state.allocation.limits.maximumBidHeads)
1180
+ return reject(state, "bid_capacity_exceeded");
1181
+ if (Object.values(state.allocation.acceptedBidEvidence).filter((entry) => entry.offerId === payload.offerId).length >= state.allocation.limits.maximumBidsPerOffer)
1182
+ return reject(state, "bid_capacity_exceeded");
1183
+ if (state.coordination.localEventSequence >= Number.MAX_SAFE_INTEGER)
1184
+ throw new RangeError("Mesh coordination event sequence exhausted");
1185
+ const evidence = Object.freeze({
1186
+ bidId: payload.bidId,
1187
+ offerId: payload.offerId,
1188
+ bidderPeerId: payload.bidderPeerId,
1189
+ bidRevision: payload.bidRevision,
1190
+ ...(payload.previousBidId === undefined
1191
+ ? {}
1192
+ : { previousBidId: payload.previousBidId }),
1193
+ acceptedAt: request.receivedAt,
1194
+ validityVerifiedAt: request.verifiedAt,
1195
+ ...(request.supportedCriticalExtensions === undefined
1196
+ ? {}
1197
+ : {
1198
+ supportedCriticalExtensions: Object.freeze([
1199
+ ...request.supportedCriticalExtensions,
1200
+ ]),
1201
+ }),
1202
+ envelope,
1203
+ });
1204
+ const head = Object.freeze({
1205
+ bidKey: key,
1206
+ offerId: payload.offerId,
1207
+ bidderPeerId: payload.bidderPeerId,
1208
+ bidId: payload.bidId,
1209
+ bidRevision: payload.bidRevision,
1210
+ ...(payload.previousBidId === undefined
1211
+ ? {}
1212
+ : { previousBidId: payload.previousBidId }),
1213
+ acceptedMessageId: envelope.messageId,
1214
+ acceptedAt: request.receivedAt,
1215
+ capacityReservationUnits: payload.capacityReservationUnits,
1216
+ budgetUnits: payload.budgetUnits,
1217
+ expectedCompletionAt: payload.expectedCompletionAt,
1218
+ bidExpiresAt: payload.bidExpiresAt,
1219
+ bidExpiresAtLogical,
1220
+ });
1221
+ const allocation = restoreMeshAllocationState({
1222
+ ...state.allocation,
1223
+ bidHeads: createFrozenRecord([
1224
+ ...recordEntries(state.allocation.bidHeads).filter(([entry]) => entry !== key),
1225
+ [key, head],
1226
+ ]),
1227
+ acceptedBidEvidence: createFrozenRecord([
1228
+ ...recordEntries(state.allocation.acceptedBidEvidence),
1229
+ [payload.bidId, evidence],
1230
+ ]),
1231
+ lastLogicalTime: request.receivedAt,
1232
+ });
1233
+ const sequence = state.coordination.localEventSequence + 1;
1234
+ const record = Object.freeze({
1235
+ recordKey: domainKey,
1236
+ recordType: "work.bid",
1237
+ recordId: payload.bidId,
1238
+ contentDigest: payloadDigest(envelope),
1239
+ messageId: envelope.messageId,
1240
+ acceptedAt: request.receivedAt,
1241
+ objectiveId: payload.objectiveId,
1242
+ });
1243
+ const coordination = Object.freeze({
1244
+ ...state.coordination,
1245
+ domainRecords: createFrozenRecord([
1246
+ ...recordEntries(state.coordination.domainRecords),
1247
+ [domainKey, record],
1248
+ ]),
1249
+ journal: Object.freeze([
1250
+ ...state.coordination.journal,
1251
+ Object.freeze({
1252
+ sequence,
1253
+ occurredAt: request.receivedAt,
1254
+ kind: "domain.accepted",
1255
+ domainRecordKey: domainKey,
1256
+ }),
1257
+ ]),
1258
+ localEventSequence: sequence,
1259
+ lastLogicalTime: request.receivedAt,
1260
+ });
1261
+ return Object.freeze({
1262
+ accepted: true,
1263
+ duplicate: false,
1264
+ state: createMeshAllocationRuntimeState(coordination, Object.freeze({
1265
+ ...state.discovery,
1266
+ lastLogicalTime: request.receivedAt,
1267
+ }), Object.freeze({
1268
+ ...state.objectives,
1269
+ lastLogicalTime: request.receivedAt,
1270
+ }), allocation),
1271
+ effects: Object.freeze([]),
1272
+ });
1273
+ }
1274
+ function evaluateReceivedOffer(state, request) {
1275
+ const envelope = request.envelope;
1276
+ const payload = envelope.payload;
1277
+ const objective = state.objectives.objectives[payload.objectiveId];
1278
+ const bidDeadlineAt = logicalDeadline(payload.bidDeadline, request.verifiedAt, request.receivedAt);
1279
+ const workDeadlineAt = logicalDeadline(payload.workDeadline, request.verifiedAt, request.receivedAt);
1280
+ const signedBidWindow = logicalDeadline(payload.bidDeadline, envelope.sentAt, 0);
1281
+ if (!canonicalDigest(envelope) ||
1282
+ !objective ||
1283
+ objective.status !== "active" ||
1284
+ objective.objectiveDocumentId !== payload.objectiveDocumentId ||
1285
+ objective.objectiveRevision !== payload.objectiveRevision ||
1286
+ request.receivedAt >= objective.expiresAt ||
1287
+ compare(request.verifiedAt, objective.validUntil) >= 0 ||
1288
+ !bidDeadlineAt ||
1289
+ !workDeadlineAt ||
1290
+ !signedBidWindow ||
1291
+ bidDeadlineAt <= request.receivedAt ||
1292
+ workDeadlineAt <= request.receivedAt ||
1293
+ bidDeadlineAt > workDeadlineAt ||
1294
+ signedBidWindow > objective.bidWindowMs ||
1295
+ workDeadlineAt > objective.expiresAt ||
1296
+ compare(payload.workDeadline, objective.validUntil) > 0 ||
1297
+ payload.budgetReservationUnits > objective.maximumBudgetUnits ||
1298
+ payload.requiredCapabilityKeys.some((key) => !objective.permittedCapabilityKeys.includes(key)) ||
1299
+ envelope.audience.kind !== "peer" ||
1300
+ envelope.audience.peerId !== state.allocation.identity.peerId ||
1301
+ envelope.sender.peerId !== payload.ownerPeerId ||
1302
+ payload.ownerPeerId === state.allocation.identity.peerId)
1303
+ return reject(state, "received_offer_invalid");
1304
+ const existing = state.allocation.receivedOffers[payload.offerId];
1305
+ if (existing)
1306
+ return sameData(existing.envelope, envelope)
1307
+ ? Object.freeze({
1308
+ accepted: true,
1309
+ duplicate: true,
1310
+ state,
1311
+ effects: Object.freeze([]),
1312
+ })
1313
+ : reject(state, "received_offer_duplicate_conflict");
1314
+ const priorOffers = Object.values(state.allocation.receivedOffers)
1315
+ .filter((offer) => sameReceivedOfferScope(offer.envelope.payload, payload))
1316
+ .sort((left, right) => left.envelope.payload.offerAttempt -
1317
+ right.envelope.payload.offerAttempt);
1318
+ const predecessor = priorOffers.at(-1);
1319
+ const predecessorAwards = predecessor === undefined
1320
+ ? []
1321
+ : Object.values(state.allocation.receivedAwards).filter((award) => award.offerId === predecessor.offerId);
1322
+ const predecessorIsTerminal = predecessorAwards.some((award) => award.status === "declined" || award.status === "timed_out");
1323
+ if (payload.offerAttempt !==
1324
+ (predecessor?.envelope.payload.offerAttempt ?? 0) + 1 ||
1325
+ payload.previousOfferId !== predecessor?.offerId ||
1326
+ (predecessor === undefined
1327
+ ? envelope.causationId !== undefined
1328
+ : envelope.causationId !== predecessor.envelope.messageId) ||
1329
+ (predecessor !== undefined &&
1330
+ !sameReceivedOfferWorkTerms(predecessor.envelope.payload, payload)) ||
1331
+ (predecessor !== undefined &&
1332
+ request.receivedAt < predecessor.receivedAt) ||
1333
+ predecessorAwards.some((award) => award.status === "awaiting_response" || award.status === "accepted") ||
1334
+ (predecessor !== undefined &&
1335
+ !predecessorIsTerminal &&
1336
+ request.receivedAt < predecessor.bidDeadlineAt))
1337
+ return reject(state, "received_offer_invalid");
1338
+ const domainKey = offerDomainKey(payload.offerId);
1339
+ if (meshAllocationRetainsMessageId(state, envelope.messageId) ||
1340
+ state.coordination.domainRecords[domainKey])
1341
+ return reject(state, "received_offer_duplicate_conflict");
1342
+ if (Object.keys(state.allocation.receivedOffers).length >=
1343
+ state.allocation.limits.maximumReceivedOffers)
1344
+ return reject(state, "received_offer_capacity_exceeded");
1345
+ const capacity = allocationWriteCapacity(state);
1346
+ if (capacity)
1347
+ return reject(state, capacity);
1348
+ const offer = Object.freeze({
1349
+ offerId: payload.offerId,
1350
+ receivedAt: request.receivedAt,
1351
+ validityVerifiedAt: request.verifiedAt,
1352
+ ...(request.supportedCriticalExtensions === undefined
1353
+ ? {}
1354
+ : {
1355
+ supportedCriticalExtensions: Object.freeze([
1356
+ ...request.supportedCriticalExtensions,
1357
+ ]),
1358
+ }),
1359
+ bidDeadlineAt,
1360
+ workDeadlineAt,
1361
+ envelope,
1362
+ });
1363
+ return acceptAssigneeWrite(state, request.receivedAt, domainKey, "work.offer", payload.offerId, payload.objectiveId, envelope, {
1364
+ ...state.allocation,
1365
+ receivedOffers: createFrozenRecord([
1366
+ ...recordEntries(state.allocation.receivedOffers),
1367
+ [payload.offerId, offer],
1368
+ ]),
1369
+ lastLogicalTime: request.receivedAt,
1370
+ });
1371
+ }
1372
+ function evaluateReceivedAward(state, request) {
1373
+ const envelope = request.envelope;
1374
+ const payload = envelope.payload;
1375
+ if (payload.authorityKind === "recovery_certificate")
1376
+ return evaluateReceivedRecoveryAward(state, request, envelope);
1377
+ const offer = state.allocation.receivedOffers[payload.offerId];
1378
+ const bid = state.allocation.localBids[payload.bidId];
1379
+ if (!offer || !bid || bid.offerId !== offer.offerId)
1380
+ return reject(state, "received_award_invalid");
1381
+ const currentBid = Object.values(state.allocation.localBids)
1382
+ .filter((candidate) => candidate.offerId === offer.offerId)
1383
+ .sort((left, right) => left.bidRevision - right.bidRevision)
1384
+ .at(-1);
1385
+ if (!currentBid || currentBid.bidId !== bid.bidId)
1386
+ return reject(state, "received_award_invalid");
1387
+ const objective = state.objectives.objectives[payload.objectiveId];
1388
+ const acceptanceDeadlineAt = logicalDeadline(payload.acceptanceDeadline, request.verifiedAt, request.receivedAt);
1389
+ const leaseExpiresAtLogical = logicalDeadline(payload.leaseExpiresAt, request.verifiedAt, request.receivedAt);
1390
+ const signedAcceptanceDuration = logicalDeadline(payload.acceptanceDeadline, envelope.sentAt, 0);
1391
+ const signedLeaseDuration = logicalDeadline(payload.leaseExpiresAt, payload.leaseStartsAt, 0);
1392
+ if (!canonicalDigest(envelope) ||
1393
+ !objective ||
1394
+ objective.status !== "active" ||
1395
+ objective.objectiveDocumentId !== payload.objectiveDocumentId ||
1396
+ objective.objectiveRevision !== payload.objectiveRevision ||
1397
+ request.receivedAt >= objective.expiresAt ||
1398
+ compare(request.verifiedAt, objective.validUntil) >= 0 ||
1399
+ !acceptanceDeadlineAt ||
1400
+ !leaseExpiresAtLogical ||
1401
+ !signedAcceptanceDuration ||
1402
+ !signedLeaseDuration ||
1403
+ acceptanceDeadlineAt <= request.receivedAt ||
1404
+ leaseExpiresAtLogical <= request.receivedAt ||
1405
+ request.receivedAt >= bid.bidExpiresAtLogical ||
1406
+ compare(request.verifiedAt, bid.bidExpiresAt) >= 0 ||
1407
+ signedAcceptanceDuration > objective.acceptanceWindowMs ||
1408
+ signedLeaseDuration > objective.maximumLeaseDurationMs ||
1409
+ leaseExpiresAtLogical > offer.workDeadlineAt ||
1410
+ leaseExpiresAtLogical > objective.expiresAt ||
1411
+ envelope.audience.kind !== "peer" ||
1412
+ envelope.audience.peerId !== state.allocation.identity.peerId ||
1413
+ envelope.sender.peerId !== offer.envelope.sender.peerId ||
1414
+ envelope.causationId !== bid.envelope.messageId ||
1415
+ payload.assigneePeerId !== state.allocation.identity.peerId ||
1416
+ payload.assignmentEpoch !== 1 ||
1417
+ payload.authorityKind !== "award" ||
1418
+ payload.assignmentAuthorityId !== payload.awardId ||
1419
+ payload.fencingToken !== payload.awardId ||
1420
+ payload.bidId !== bid.bidId ||
1421
+ payload.bidRevision !== bid.bidRevision ||
1422
+ !matchesAwardOffer(payload, offer))
1423
+ return reject(state, "received_award_invalid");
1424
+ const conflictingScopeAward = Object.values(state.allocation.receivedAwards).find((award) => award.envelope.payload.objectiveId === payload.objectiveId &&
1425
+ award.envelope.payload.workItemId === payload.workItemId &&
1426
+ award.envelope.payload.workItemRevision === payload.workItemRevision &&
1427
+ award.envelope.payload.assignmentEpoch === payload.assignmentEpoch &&
1428
+ award.awardId !== payload.awardId);
1429
+ if (conflictingScopeAward)
1430
+ return reject(state, "received_award_duplicate_conflict");
1431
+ const existing = state.allocation.receivedAwards[payload.awardId];
1432
+ if (existing)
1433
+ return sameData(existing.envelope, envelope)
1434
+ ? Object.freeze({
1435
+ accepted: true,
1436
+ duplicate: true,
1437
+ state,
1438
+ effects: Object.freeze([]),
1439
+ })
1440
+ : reject(state, "received_award_duplicate_conflict");
1441
+ const domainKey = awardDomainKey(payload.awardId);
1442
+ if (meshAllocationRetainsMessageId(state, envelope.messageId) ||
1443
+ state.coordination.domainRecords[domainKey])
1444
+ return reject(state, "received_award_duplicate_conflict");
1445
+ if (Object.keys(state.allocation.receivedAwards).length >=
1446
+ state.allocation.limits.maximumReceivedAwards)
1447
+ return reject(state, "received_award_capacity_exceeded");
1448
+ const award = Object.freeze({
1449
+ awardId: payload.awardId,
1450
+ offerId: payload.offerId,
1451
+ bidId: payload.bidId,
1452
+ bidRevision: payload.bidRevision,
1453
+ receivedAt: request.receivedAt,
1454
+ validityVerifiedAt: request.verifiedAt,
1455
+ ...(request.supportedCriticalExtensions === undefined
1456
+ ? {}
1457
+ : {
1458
+ supportedCriticalExtensions: Object.freeze([
1459
+ ...request.supportedCriticalExtensions,
1460
+ ]),
1461
+ }),
1462
+ acceptanceDeadlineAt,
1463
+ acceptanceDeadlineTimerId: `allocation.assignee_response.${payload.awardId}`,
1464
+ acceptanceDeadlineTimerGeneration: 1,
1465
+ leaseExpiresAtLogical,
1466
+ status: "awaiting_response",
1467
+ envelope,
1468
+ });
1469
+ const timerId = `allocation.assignee_response.${payload.awardId}`;
1470
+ if (state.coordination.timers[timerId])
1471
+ return reject(state, "timer_id_conflict");
1472
+ const capacity = allocationWriteCapacity(state, true);
1473
+ if (capacity)
1474
+ return reject(state, capacity);
1475
+ return acceptAssigneeWrite(state, request.receivedAt, domainKey, "work.award", payload.awardId, payload.objectiveId, envelope, {
1476
+ ...state.allocation,
1477
+ receivedAwards: createFrozenRecord([
1478
+ ...recordEntries(state.allocation.receivedAwards),
1479
+ [payload.awardId, award],
1480
+ ]),
1481
+ lastLogicalTime: request.receivedAt,
1482
+ }, undefined, Object.freeze({
1483
+ timerId,
1484
+ kind: "work.acceptance_deadline",
1485
+ dueAt: acceptanceDeadlineAt,
1486
+ generation: 1,
1487
+ domainRecordKey: domainKey,
1488
+ }));
1489
+ }
1490
+ function evaluateReceivedRecoveryAward(state, request, envelope) {
1491
+ const payload = envelope.payload;
1492
+ const certificate = state.allocation.recoveryCertificates[payload.recoveryCertificateId];
1493
+ const proposal = certificate === undefined
1494
+ ? undefined
1495
+ : state.allocation.takeoverProposals[certificate.takeoverProposalId];
1496
+ if (certificate === undefined || proposal === undefined)
1497
+ return reject(state, "received_award_invalid");
1498
+ const proposed = proposal.envelope.payload;
1499
+ const fenceKey = meshAssignmentFenceKey(proposed);
1500
+ const fence = state.allocation.assignmentFenceHeads[fenceKey];
1501
+ const offer = state.allocation.receivedOffers[payload.offerId];
1502
+ const bid = state.allocation.localBids[payload.bidId];
1503
+ const objective = state.objectives.objectives[payload.objectiveId];
1504
+ const policy = state.objectives.objectivePolicies[policyKey(payload.objectiveId, payload.objectiveRevision)];
1505
+ const acceptanceDeadlineAt = logicalDeadline(payload.acceptanceDeadline, request.verifiedAt, request.receivedAt);
1506
+ const leaseExpiresAtLogical = logicalDeadline(payload.leaseExpiresAt, request.verifiedAt, request.receivedAt);
1507
+ const signedAcceptanceDuration = logicalDeadline(payload.acceptanceDeadline, envelope.sentAt, 0);
1508
+ const signedLeaseDuration = logicalDeadline(payload.leaseExpiresAt, payload.leaseStartsAt, 0);
1509
+ const checkpointIds = new Set([
1510
+ ...Object.values(state.allocation.executionHeads)
1511
+ .filter((head) => meshAssignmentFenceKey(head) === fenceKey &&
1512
+ head.assignmentEpoch === proposed.proposedAssignmentEpoch - 1)
1513
+ .map((head) => head.latestCheckpointId),
1514
+ state.allocation.witnessAssignments[fenceKey]?.latestCheckpoint?.recordId,
1515
+ ].filter((checkpointId) => checkpointId !== undefined));
1516
+ const resumeCheckpointId = checkpointIds.size === 1 ? [...checkpointIds][0] : undefined;
1517
+ if (checkpointIds.size > 1 ||
1518
+ !canonicalDigest(envelope) ||
1519
+ fence === undefined ||
1520
+ fence.phase !== "recovering" ||
1521
+ fence.recoveryCertificateId !== certificate.certificateId ||
1522
+ fence.assignmentEpoch !== proposed.proposedAssignmentEpoch ||
1523
+ fence.assignmentAuthorityId !== certificate.certificateId ||
1524
+ fence.fencingToken !== certificate.certificateId ||
1525
+ fence.assigneePeerId !== state.allocation.identity.peerId ||
1526
+ fence.activeAwardId !== undefined ||
1527
+ offer === undefined ||
1528
+ bid === undefined ||
1529
+ bid.offerId !== offer.offerId ||
1530
+ bid.bidRevision !== payload.bidRevision ||
1531
+ objective === undefined ||
1532
+ objective.status !== "active" ||
1533
+ policy === undefined ||
1534
+ request.receivedAt >= policy.expiresAt ||
1535
+ compare(request.verifiedAt, policy.validUntil) >= 0 ||
1536
+ acceptanceDeadlineAt === undefined ||
1537
+ leaseExpiresAtLogical === undefined ||
1538
+ signedAcceptanceDuration === undefined ||
1539
+ signedLeaseDuration === undefined ||
1540
+ acceptanceDeadlineAt <= request.receivedAt ||
1541
+ leaseExpiresAtLogical <= request.receivedAt ||
1542
+ signedAcceptanceDuration > policy.acceptanceWindowMs ||
1543
+ signedLeaseDuration > policy.maximumLeaseDurationMs ||
1544
+ leaseExpiresAtLogical > offer.workDeadlineAt ||
1545
+ leaseExpiresAtLogical > policy.expiresAt ||
1546
+ envelope.audience.kind !== "peer" ||
1547
+ envelope.audience.peerId !== state.allocation.identity.peerId ||
1548
+ envelope.sender.peerId !== proposed.ownerPeerId ||
1549
+ envelope.causationId !==
1550
+ (certificate.recipientEnvelopes?.[state.allocation.identity.peerId] ??
1551
+ certificate.envelope).messageId ||
1552
+ payload.recoveryCertificateId !== certificate.certificateId ||
1553
+ payload.assignmentAuthorityId !== certificate.certificateId ||
1554
+ payload.fencingToken !== certificate.certificateId ||
1555
+ payload.assignmentEpoch !== proposed.proposedAssignmentEpoch ||
1556
+ payload.assigneePeerId !== proposed.proposedAssigneePeerId ||
1557
+ payload.assigneePeerId !== state.allocation.identity.peerId ||
1558
+ payload.resumeCheckpointId !== resumeCheckpointId ||
1559
+ payload.objectiveId !== proposed.objectiveId ||
1560
+ payload.objectiveDocumentId !== proposed.objectiveDocumentId ||
1561
+ payload.objectiveRevision !== proposed.objectiveRevision ||
1562
+ payload.workItemId !== proposed.workItemId ||
1563
+ payload.workItemRevision !== proposed.workItemRevision ||
1564
+ payload.ownerPeerId !== proposed.ownerPeerId ||
1565
+ payload.ownerEpoch !== proposed.ownerEpoch ||
1566
+ payload.offerId !== offer.offerId ||
1567
+ payload.offerAttempt !== offer.envelope.payload.offerAttempt ||
1568
+ payload.bidId !== bid.bidId ||
1569
+ payload.workDeadline !== offer.envelope.payload.workDeadline ||
1570
+ payload.budgetReservationUnits !==
1571
+ offer.envelope.payload.budgetReservationUnits)
1572
+ return reject(state, "received_award_invalid");
1573
+ const existing = state.allocation.receivedAwards[payload.awardId];
1574
+ if (existing)
1575
+ return sameData(existing.envelope, envelope)
1576
+ ? Object.freeze({
1577
+ accepted: true,
1578
+ duplicate: true,
1579
+ state,
1580
+ effects: Object.freeze([]),
1581
+ })
1582
+ : reject(state, "received_award_duplicate_conflict");
1583
+ const domainKey = awardDomainKey(payload.awardId);
1584
+ if (meshAllocationRetainsMessageId(state, envelope.messageId) ||
1585
+ state.coordination.domainRecords[domainKey])
1586
+ return reject(state, "received_award_duplicate_conflict");
1587
+ if (Object.keys(state.allocation.receivedAwards).length >=
1588
+ state.allocation.limits.maximumReceivedAwards)
1589
+ return reject(state, "received_award_capacity_exceeded");
1590
+ const timerId = `allocation.assignee_response.${payload.awardId}`;
1591
+ if (state.coordination.timers[timerId])
1592
+ return reject(state, "timer_id_conflict");
1593
+ const capacity = allocationWriteCapacity(state, true);
1594
+ if (capacity)
1595
+ return reject(state, capacity);
1596
+ const award = Object.freeze({
1597
+ awardId: payload.awardId,
1598
+ offerId: payload.offerId,
1599
+ bidId: payload.bidId,
1600
+ bidRevision: payload.bidRevision,
1601
+ receivedAt: request.receivedAt,
1602
+ validityVerifiedAt: request.verifiedAt,
1603
+ ...(request.supportedCriticalExtensions === undefined
1604
+ ? {}
1605
+ : {
1606
+ supportedCriticalExtensions: Object.freeze([
1607
+ ...request.supportedCriticalExtensions,
1608
+ ]),
1609
+ }),
1610
+ acceptanceDeadlineAt,
1611
+ acceptanceDeadlineTimerId: timerId,
1612
+ acceptanceDeadlineTimerGeneration: 1,
1613
+ leaseExpiresAtLogical,
1614
+ status: "awaiting_response",
1615
+ envelope,
1616
+ });
1617
+ return acceptAssigneeWrite(state, request.receivedAt, domainKey, "work.award", payload.awardId, payload.objectiveId, envelope, {
1618
+ ...state.allocation,
1619
+ receivedAwards: createFrozenRecord([
1620
+ ...recordEntries(state.allocation.receivedAwards),
1621
+ [payload.awardId, award],
1622
+ ]),
1623
+ assignmentFenceHeads: createFrozenRecord([
1624
+ ...recordEntries(state.allocation.assignmentFenceHeads).filter(([key]) => key !== fenceKey),
1625
+ [
1626
+ fenceKey,
1627
+ Object.freeze({
1628
+ ...fence,
1629
+ activeAwardId: payload.awardId,
1630
+ phase: "award_pending",
1631
+ }),
1632
+ ],
1633
+ ]),
1634
+ lastLogicalTime: request.receivedAt,
1635
+ }, undefined, Object.freeze({
1636
+ timerId,
1637
+ kind: "work.acceptance_deadline",
1638
+ dueAt: acceptanceDeadlineAt,
1639
+ generation: 1,
1640
+ domainRecordKey: domainKey,
1641
+ }));
1642
+ }
1643
+ function evaluateAssignmentResponse(state, request) {
1644
+ const envelope = request.envelope;
1645
+ const payload = envelope.payload;
1646
+ const award = state.allocation.localAwards[payload.awardId];
1647
+ if (!award)
1648
+ return reject(state, "award_missing");
1649
+ const responseId = payload.type === "work.accept" ? payload.acceptanceId : payload.declineId;
1650
+ const kind = payload.type;
1651
+ const existingResponse = state.allocation.assignmentResponses[award.awardId];
1652
+ if (existingResponse)
1653
+ return sameData(existingResponse.envelope, envelope)
1654
+ ? Object.freeze({
1655
+ accepted: true,
1656
+ duplicate: true,
1657
+ state,
1658
+ effects: Object.freeze([]),
1659
+ })
1660
+ : reject(state, "assignment_response_duplicate_conflict");
1661
+ const work = state.allocation.workAllocations[workKey(award.objectiveId, award.work.workItemId)];
1662
+ const reservation = state.allocation.reservations[award.reservationId];
1663
+ const timer = state.coordination.timers[award.acceptanceDeadlineTimerId];
1664
+ const objective = state.objectives.objectives[award.objectiveId];
1665
+ const recoveryAward = award.recipientAward.envelope.payload.authorityKind ===
1666
+ "recovery_certificate";
1667
+ const fenceKey = recoveryAward
1668
+ ? meshAssignmentFenceKey(award.recipientAward.envelope.payload)
1669
+ : undefined;
1670
+ const fence = fenceKey === undefined
1671
+ ? undefined
1672
+ : state.allocation.assignmentFenceHeads[fenceKey];
1673
+ if (request.receivedAt >= award.acceptanceDeadlineAt ||
1674
+ compare(request.verifiedAt, award.acceptanceDeadline) >= 0 ||
1675
+ award.status === "timed_out")
1676
+ return reject(state, "assignment_response_deadline_elapsed");
1677
+ if (!work ||
1678
+ !reservation ||
1679
+ !timer ||
1680
+ !objective ||
1681
+ objective.status !== "active" ||
1682
+ request.receivedAt >= award.objectivePolicy.expiresAt ||
1683
+ compare(request.verifiedAt, award.objectivePolicy.validUntil) >= 0 ||
1684
+ award.status !== "awaiting_acceptance" ||
1685
+ work.phase !== "award_pending" ||
1686
+ work.activeAwardId !== award.awardId ||
1687
+ reservation.status !== (recoveryAward ? "committed" : "reserved") ||
1688
+ timer.kind !== "work.acceptance_deadline" ||
1689
+ timer.generation !== award.acceptanceDeadlineTimerGeneration ||
1690
+ timer.dueAt !== award.acceptanceDeadlineAt ||
1691
+ timer.domainRecordKey !== awardDomainKey(award.awardId) ||
1692
+ envelope.audience.kind !== "peer" ||
1693
+ envelope.audience.peerId !== state.allocation.identity.peerId ||
1694
+ envelope.sender.peerId !== award.assigneePeerId ||
1695
+ envelope.causationId !== award.recipientAward.messageId ||
1696
+ payload.objectiveId !== award.objectiveId ||
1697
+ payload.objectiveDocumentId !== award.objectiveDocumentId ||
1698
+ payload.objectiveRevision !== award.objectiveRevision ||
1699
+ payload.workItemId !== award.work.workItemId ||
1700
+ payload.workItemRevision !== award.work.workItemRevision ||
1701
+ payload.ownerPeerId !== state.allocation.identity.peerId ||
1702
+ payload.ownerEpoch !== award.work.ownerEpoch ||
1703
+ payload.assigneePeerId !== award.assigneePeerId ||
1704
+ payload.assignmentEpoch !== award.assignmentEpoch ||
1705
+ payload.assignmentAuthorityId !== award.assignmentAuthorityId ||
1706
+ payload.fencingToken !== award.fencingToken ||
1707
+ payload.acceptanceDeadline !== award.acceptanceDeadline ||
1708
+ (!recoveryAward &&
1709
+ objective.reservedBudgetUnits < reservation.budgetReservationUnits) ||
1710
+ (!recoveryAward &&
1711
+ payload.type === "work.accept" &&
1712
+ objective.committedBudgetUnits >
1713
+ Number.MAX_SAFE_INTEGER - reservation.budgetReservationUnits) ||
1714
+ (recoveryAward &&
1715
+ (fence === undefined ||
1716
+ fence.phase !== "award_pending" ||
1717
+ fence.activeAwardId !== award.awardId ||
1718
+ fence.assignmentEpoch !== award.assignmentEpoch ||
1719
+ fence.assignmentAuthorityId !== award.assignmentAuthorityId ||
1720
+ fence.fencingToken !== award.fencingToken ||
1721
+ fence.assigneePeerId !== award.assigneePeerId ||
1722
+ fence.recoveryCertificateId !==
1723
+ award.recipientAward.envelope.payload.recoveryCertificateId)))
1724
+ return reject(state, "assignment_response_invalid");
1725
+ const domainKey = JSON.stringify([kind, responseId]);
1726
+ if (Object.values(state.coordination.domainRecords).some((record) => record.messageId === envelope.messageId) ||
1727
+ Object.values(state.allocation.localOffers).some((entry) => Object.values(entry.recipientOffers).some((prepared) => prepared.messageId === envelope.messageId)) ||
1728
+ Object.values(state.allocation.acceptedBidEvidence).some((entry) => entry.envelope.messageId === envelope.messageId) ||
1729
+ Object.values(state.allocation.localAwards).some((entry) => entry.recipientAward.messageId === envelope.messageId))
1730
+ return reject(state, "assignment_response_duplicate_conflict");
1731
+ if (state.coordination.domainRecords[domainKey])
1732
+ return reject(state, "domain_record_conflict");
1733
+ if (Object.keys(state.allocation.assignmentResponses).length >=
1734
+ state.allocation.limits.maximumAssignmentResponses)
1735
+ return reject(state, "assignment_response_capacity_exceeded");
1736
+ if (kind === "work.accept" &&
1737
+ Object.keys(state.allocation.leaseHeads).length >=
1738
+ state.allocation.limits.maximumExecutionHeads)
1739
+ return reject(state, "execution_head_capacity_exceeded");
1740
+ if (Object.keys(state.coordination.domainRecords).length >=
1741
+ state.coordination.limits.maximumDomainRecords)
1742
+ return reject(state, "domain_capacity_exceeded");
1743
+ if (state.coordination.journal.length >=
1744
+ state.coordination.limits.maximumJournalEntries)
1745
+ return reject(state, "journal_capacity_exceeded");
1746
+ if (state.coordination.localEventSequence >= Number.MAX_SAFE_INTEGER)
1747
+ throw new RangeError("Mesh coordination event sequence exhausted");
1748
+ const evidence = Object.freeze({
1749
+ awardId: award.awardId,
1750
+ responseId,
1751
+ kind,
1752
+ acceptedAt: request.receivedAt,
1753
+ validityVerifiedAt: request.verifiedAt,
1754
+ ...(request.supportedCriticalExtensions === undefined
1755
+ ? {}
1756
+ : {
1757
+ supportedCriticalExtensions: Object.freeze([
1758
+ ...request.supportedCriticalExtensions,
1759
+ ]),
1760
+ }),
1761
+ envelope,
1762
+ });
1763
+ const accepted = kind === "work.accept";
1764
+ const leaseActivation = accepted
1765
+ ? createInitialLeaseActivation({
1766
+ objectiveId: award.objectiveId,
1767
+ objectiveDocumentId: award.objectiveDocumentId,
1768
+ objectiveRevision: award.objectiveRevision,
1769
+ workItemId: award.work.workItemId,
1770
+ workItemRevision: award.work.workItemRevision,
1771
+ ownerPeerId: award.work.ownerPeerId,
1772
+ ownerEpoch: award.work.ownerEpoch,
1773
+ assigneePeerId: award.assigneePeerId,
1774
+ awardId: award.awardId,
1775
+ assignmentEpoch: award.assignmentEpoch,
1776
+ assignmentAuthorityId: award.assignmentAuthorityId,
1777
+ fencingToken: award.fencingToken,
1778
+ acceptanceId: responseId,
1779
+ acceptanceMessageId: evidence.envelope.messageId,
1780
+ workDeadline: award.workDeadline,
1781
+ workDeadlineAt: award.work.workDeadlineAt,
1782
+ leaseExpiresAt: award.leaseExpiresAt,
1783
+ leaseExpiresAtLogical: award.leaseExpiresAtLogical,
1784
+ })
1785
+ : undefined;
1786
+ if (leaseActivation !== undefined &&
1787
+ state.coordination.timers[leaseActivation.timer.timerId] !== undefined)
1788
+ return reject(state, "timer_id_conflict");
1789
+ const nextWork = Object.freeze({
1790
+ ...work,
1791
+ phase: accepted
1792
+ ? "active"
1793
+ : recoveryAward
1794
+ ? "recovering"
1795
+ : "ready",
1796
+ activeAwardId: accepted ? award.awardId : undefined,
1797
+ activeAcceptanceId: accepted ? responseId : undefined,
1798
+ reservationId: accepted || recoveryAward ? reservation.reservationId : undefined,
1799
+ updatedAt: request.receivedAt,
1800
+ });
1801
+ const nextReservation = recoveryAward
1802
+ ? reservation
1803
+ : Object.freeze({
1804
+ ...reservation,
1805
+ status: accepted ? "committed" : "released",
1806
+ ...(accepted
1807
+ ? { committedAt: request.receivedAt }
1808
+ : { releasedAt: request.receivedAt }),
1809
+ });
1810
+ const allocation = restoreMeshAllocationState({
1811
+ ...state.allocation,
1812
+ workAllocations: createFrozenRecord([
1813
+ ...recordEntries(state.allocation.workAllocations).filter(([key]) => key !== work.workKey),
1814
+ [work.workKey, nextWork],
1815
+ ]),
1816
+ localAwards: createFrozenRecord([
1817
+ ...recordEntries(state.allocation.localAwards).filter(([key]) => key !== award.awardId),
1818
+ [
1819
+ award.awardId,
1820
+ Object.freeze({
1821
+ ...award,
1822
+ status: accepted ? "accepted" : "declined",
1823
+ }),
1824
+ ],
1825
+ ]),
1826
+ assignmentResponses: createFrozenRecord([
1827
+ ...recordEntries(state.allocation.assignmentResponses),
1828
+ [award.awardId, evidence],
1829
+ ]),
1830
+ leaseHeads: createFrozenRecord(leaseActivation === undefined
1831
+ ? recordEntries(state.allocation.leaseHeads)
1832
+ : [
1833
+ ...recordEntries(state.allocation.leaseHeads),
1834
+ [leaseActivation.head.executionScopeKey, leaseActivation.head],
1835
+ ]),
1836
+ reservations: createFrozenRecord([
1837
+ ...recordEntries(state.allocation.reservations).filter(([key]) => key !== reservation.reservationId),
1838
+ [reservation.reservationId, nextReservation],
1839
+ ]),
1840
+ ...(fenceKey === undefined || fence === undefined
1841
+ ? {}
1842
+ : {
1843
+ assignmentFenceHeads: createFrozenRecord([
1844
+ ...recordEntries(state.allocation.assignmentFenceHeads).filter(([key]) => key !== fenceKey),
1845
+ [
1846
+ fenceKey,
1847
+ Object.freeze({
1848
+ ...fence,
1849
+ activeAwardId: accepted ? award.awardId : undefined,
1850
+ phase: accepted ? "active" : "recovering",
1851
+ }),
1852
+ ],
1853
+ ]),
1854
+ }),
1855
+ lastLogicalTime: request.receivedAt,
1856
+ });
1857
+ const sequence = state.coordination.localEventSequence + 1;
1858
+ const coordination = Object.freeze({
1859
+ ...state.coordination,
1860
+ domainRecords: createFrozenRecord([
1861
+ ...recordEntries(state.coordination.domainRecords),
1862
+ [
1863
+ domainKey,
1864
+ Object.freeze({
1865
+ recordKey: domainKey,
1866
+ recordType: kind,
1867
+ recordId: responseId,
1868
+ contentDigest: payloadDigest(envelope),
1869
+ messageId: envelope.messageId,
1870
+ acceptedAt: request.receivedAt,
1871
+ objectiveId: payload.objectiveId,
1872
+ }),
1873
+ ],
1874
+ ]),
1875
+ timers: createFrozenRecord([
1876
+ ...recordEntries(state.coordination.timers).filter(([key]) => key !== timer.timerId),
1877
+ ...(leaseActivation === undefined
1878
+ ? []
1879
+ : [[leaseActivation.timer.timerId, leaseActivation.timer]]),
1880
+ ]),
1881
+ journal: Object.freeze([
1882
+ ...state.coordination.journal,
1883
+ Object.freeze({
1884
+ sequence,
1885
+ occurredAt: request.receivedAt,
1886
+ kind: "domain.accepted",
1887
+ domainRecordKey: domainKey,
1888
+ }),
1889
+ ]),
1890
+ localEventSequence: sequence,
1891
+ lastLogicalTime: request.receivedAt,
1892
+ });
1893
+ const objectives = recoveryAward
1894
+ ? Object.freeze({
1895
+ ...state.objectives,
1896
+ lastLogicalTime: request.receivedAt,
1897
+ })
1898
+ : Object.freeze({
1899
+ ...state.objectives,
1900
+ objectives: createFrozenRecord([
1901
+ ...recordEntries(state.objectives.objectives).filter(([key]) => key !== objective.objectiveId),
1902
+ [
1903
+ objective.objectiveId,
1904
+ Object.freeze({
1905
+ ...objective,
1906
+ reservedBudgetUnits: objective.reservedBudgetUnits -
1907
+ reservation.budgetReservationUnits,
1908
+ committedBudgetUnits: objective.committedBudgetUnits +
1909
+ (accepted ? reservation.budgetReservationUnits : 0),
1910
+ }),
1911
+ ],
1912
+ ]),
1913
+ lastLogicalTime: request.receivedAt,
1914
+ });
1915
+ return Object.freeze({
1916
+ accepted: true,
1917
+ duplicate: false,
1918
+ state: createMeshAllocationRuntimeState(coordination, Object.freeze({
1919
+ ...state.discovery,
1920
+ lastLogicalTime: request.receivedAt,
1921
+ }), objectives, allocation),
1922
+ effects: Object.freeze([]),
1923
+ });
1924
+ }
1925
+ function isWitnessCopy(state, request) {
1926
+ const payload = request.envelope.payload;
1927
+ if (request.envelope.audience.kind !== "peer" ||
1928
+ request.envelope.audience.peerId !== state.allocation.identity.peerId ||
1929
+ (payload.type !== "work.award" &&
1930
+ payload.type !== "work.accept" &&
1931
+ payload.type !== "lease.renew" &&
1932
+ payload.type !== "work.checkpoint"))
1933
+ return false;
1934
+ const policy = state.objectives.objectivePolicies[policyKey(payload.objectiveId, payload.objectiveRevision)];
1935
+ if (policy === undefined ||
1936
+ !policy.recoveryWitnessPeerIds.includes(state.allocation.identity.peerId))
1937
+ return false;
1938
+ const semanticRecipientPeerId = payload.type === "work.award"
1939
+ ? payload.assigneePeerId
1940
+ : payload.ownerPeerId;
1941
+ return semanticRecipientPeerId !== state.allocation.identity.peerId;
1942
+ }
1943
+ /** Selects a currently valid bid deterministically while the window is open. */
1944
+ export function selectMeshAllocationBid(state, input) {
1945
+ if (!input ||
1946
+ typeof input !== "object" ||
1947
+ Object.keys(input).sort().join(",") !== "evaluatedAt,offerId" ||
1948
+ typeof input.offerId !== "string" ||
1949
+ !Number.isSafeInteger(input.evaluatedAt) ||
1950
+ input.evaluatedAt < 0)
1951
+ throw new TypeError("Invalid Mesh allocation selection input");
1952
+ assertRuntime(state, input.evaluatedAt);
1953
+ const offer = state.allocation.localOffers[input.offerId];
1954
+ if (!offer)
1955
+ return Object.freeze({
1956
+ offerId: input.offerId,
1957
+ evaluatedAt: input.evaluatedAt,
1958
+ reason: "offer_missing",
1959
+ });
1960
+ const allocationWork = state.allocation.workAllocations[workKey(offer.objectiveId, offer.work.workItemId)];
1961
+ if (allocationWork?.phase !== "offered" ||
1962
+ allocationWork.activeOfferId !== offer.offerId ||
1963
+ state.allocation.reservations[offer.reservationId]?.status !== "reserved") {
1964
+ return Object.freeze({
1965
+ offerId: input.offerId,
1966
+ evaluatedAt: input.evaluatedAt,
1967
+ reason: "offer_closed",
1968
+ });
1969
+ }
1970
+ if (input.evaluatedAt >= offer.bidDeadlineAt)
1971
+ return Object.freeze({
1972
+ offerId: input.offerId,
1973
+ evaluatedAt: input.evaluatedAt,
1974
+ reason: "bid_window_closed",
1975
+ });
1976
+ const eligible = Object.values(state.allocation.bidHeads).filter((bid) => {
1977
+ if (bid.offerId !== input.offerId)
1978
+ return false;
1979
+ const evidence = state.allocation.acceptedBidEvidence[bid.bidId];
1980
+ if (!evidence)
1981
+ return false;
1982
+ return (bid.bidExpiresAtLogical > input.evaluatedAt &&
1983
+ bid.budgetUnits <=
1984
+ (state.allocation.reservations[offer.reservationId]
1985
+ ?.budgetReservationUnits ?? -1) &&
1986
+ hasEligibleCapabilityAt(state, offer, evidence.envelope, input.evaluatedAt));
1987
+ });
1988
+ if (!eligible.length)
1989
+ return Object.freeze({
1990
+ offerId: input.offerId,
1991
+ evaluatedAt: input.evaluatedAt,
1992
+ reason: "no_eligible_bids",
1993
+ });
1994
+ eligible.sort((a, b) => a.budgetUnits - b.budgetUnits ||
1995
+ compare(a.expectedCompletionAt, b.expectedCompletionAt) ||
1996
+ utf16(a.bidderPeerId, b.bidderPeerId) ||
1997
+ utf16(a.bidId, b.bidId));
1998
+ return Object.freeze({
1999
+ offerId: input.offerId,
2000
+ evaluatedAt: input.evaluatedAt,
2001
+ reason: "selected",
2002
+ bid: eligible[0],
2003
+ });
2004
+ }
2005
+ /** Applies a due bid or acceptance deadline exactly once. */
2006
+ export function evaluateMeshAllocationTimer(state, input, logicalTime) {
2007
+ assertRuntime(state, logicalTime);
2008
+ if (!input ||
2009
+ typeof input !== "object" ||
2010
+ Object.keys(input).length !== 3 ||
2011
+ input.kind !== "timer.fired" ||
2012
+ typeof input.timerId !== "string" ||
2013
+ !Number.isSafeInteger(input.generation) ||
2014
+ input.generation < 1)
2015
+ throw new TypeError("Invalid Mesh allocation timer input");
2016
+ const timer = state.coordination.timers[input.timerId];
2017
+ if (!timer)
2018
+ return Object.freeze({ accepted: false, code: "timer_unknown", state });
2019
+ if (timer.generation !== input.generation)
2020
+ return Object.freeze({
2021
+ accepted: false,
2022
+ code: "timer_generation_stale",
2023
+ state,
2024
+ });
2025
+ if (logicalTime < timer.dueAt)
2026
+ return Object.freeze({ accepted: false, code: "timer_not_due", state });
2027
+ if (timer.kind === "lease.expiry")
2028
+ return evaluateMeshLeaseExpiryTimer(state, input, logicalTime);
2029
+ if (state.coordination.journal.length >=
2030
+ state.coordination.limits.maximumJournalEntries)
2031
+ return Object.freeze({
2032
+ accepted: false,
2033
+ code: "journal_capacity_exceeded",
2034
+ state,
2035
+ });
2036
+ if (state.coordination.localEventSequence >= Number.MAX_SAFE_INTEGER)
2037
+ throw new RangeError("Mesh coordination event sequence exhausted");
2038
+ if (timer.kind === "work.acceptance_deadline" &&
2039
+ Object.values(state.allocation.receivedAwards).some((award) => award.status === "awaiting_response" &&
2040
+ award.acceptanceDeadlineTimerId === timer.timerId &&
2041
+ award.acceptanceDeadlineTimerGeneration === timer.generation))
2042
+ return evaluateAssigneeAcceptanceDeadline(state, timer, logicalTime);
2043
+ if (timer.kind === "work.acceptance_deadline")
2044
+ return evaluateAcceptanceDeadline(state, timer, logicalTime);
2045
+ if (timer.kind !== "work.bid_deadline")
2046
+ throw new TypeError("Mesh allocation timer binding is invalid");
2047
+ const offer = Object.values(state.allocation.localOffers).find((entry) => entry.bidDeadlineTimerId === timer.timerId &&
2048
+ entry.bidDeadlineTimerGeneration === timer.generation);
2049
+ if (!offer)
2050
+ throw new TypeError("Mesh allocation timer is orphaned");
2051
+ const allocationWork = state.allocation.workAllocations[workKey(offer.objectiveId, offer.work.workItemId)];
2052
+ const reservation = state.allocation.reservations[offer.reservationId];
2053
+ const objective = state.objectives.objectives[offer.objectiveId];
2054
+ if (!allocationWork ||
2055
+ !reservation ||
2056
+ reservation.status !== "reserved" ||
2057
+ !objective ||
2058
+ objective.reservedBudgetUnits < reservation.budgetReservationUnits)
2059
+ throw new TypeError("Mesh allocation deadline binding is invalid");
2060
+ const ready = Object.freeze({
2061
+ ...allocationWork,
2062
+ phase: "ready",
2063
+ activeOfferId: undefined,
2064
+ bidDeadlineAt: undefined,
2065
+ reservationId: undefined,
2066
+ updatedAt: logicalTime,
2067
+ });
2068
+ const allocation = restoreMeshAllocationState({
2069
+ ...state.allocation,
2070
+ workAllocations: createFrozenRecord([
2071
+ ...recordEntries(state.allocation.workAllocations).filter(([key]) => key !== ready.workKey),
2072
+ [ready.workKey, ready],
2073
+ ]),
2074
+ reservations: createFrozenRecord([
2075
+ ...recordEntries(state.allocation.reservations).filter(([key]) => key !== reservation.reservationId),
2076
+ [
2077
+ reservation.reservationId,
2078
+ Object.freeze({
2079
+ ...reservation,
2080
+ status: "released",
2081
+ releasedAt: logicalTime,
2082
+ }),
2083
+ ],
2084
+ ]),
2085
+ lastLogicalTime: logicalTime,
2086
+ });
2087
+ const sequence = state.coordination.localEventSequence + 1;
2088
+ const coordination = Object.freeze({
2089
+ ...state.coordination,
2090
+ timers: createFrozenRecord(recordEntries(state.coordination.timers).filter(([key]) => key !== timer.timerId)),
2091
+ journal: Object.freeze([
2092
+ ...state.coordination.journal,
2093
+ Object.freeze({
2094
+ sequence,
2095
+ occurredAt: logicalTime,
2096
+ kind: "timer.fired",
2097
+ timerId: timer.timerId,
2098
+ domainRecordKey: timer.domainRecordKey,
2099
+ }),
2100
+ ]),
2101
+ localEventSequence: sequence,
2102
+ lastLogicalTime: logicalTime,
2103
+ });
2104
+ const objectives = Object.freeze({
2105
+ ...state.objectives,
2106
+ objectives: createFrozenRecord([
2107
+ ...recordEntries(state.objectives.objectives).filter(([key]) => key !== objective.objectiveId),
2108
+ [
2109
+ objective.objectiveId,
2110
+ Object.freeze({
2111
+ ...objective,
2112
+ reservedBudgetUnits: objective.reservedBudgetUnits - reservation.budgetReservationUnits,
2113
+ }),
2114
+ ],
2115
+ ]),
2116
+ lastLogicalTime: logicalTime,
2117
+ });
2118
+ return Object.freeze({
2119
+ accepted: true,
2120
+ timer,
2121
+ state: createMeshAllocationRuntimeState(coordination, Object.freeze({ ...state.discovery, lastLogicalTime: logicalTime }), objectives, allocation),
2122
+ });
2123
+ }
2124
+ function evaluateAssigneeAcceptanceDeadline(state, timer, logicalTime) {
2125
+ const award = Object.values(state.allocation.receivedAwards).find((entry) => entry.status === "awaiting_response" &&
2126
+ entry.acceptanceDeadlineTimerId === timer.timerId &&
2127
+ entry.acceptanceDeadlineTimerGeneration === timer.generation);
2128
+ if (!award)
2129
+ throw new TypeError("Mesh assignee acceptance timer is orphaned");
2130
+ if (timer.kind !== "work.acceptance_deadline" ||
2131
+ timer.dueAt !== award.acceptanceDeadlineAt ||
2132
+ timer.domainRecordKey !== awardDomainKey(award.awardId))
2133
+ throw new TypeError("Mesh assignee acceptance timer binding is invalid");
2134
+ const recoveryAward = award.envelope.payload.authorityKind === "recovery_certificate";
2135
+ const fenceKey = recoveryAward
2136
+ ? meshAssignmentFenceKey(award.envelope.payload)
2137
+ : undefined;
2138
+ const fence = fenceKey === undefined
2139
+ ? undefined
2140
+ : state.allocation.assignmentFenceHeads[fenceKey];
2141
+ if (recoveryAward &&
2142
+ (fence === undefined ||
2143
+ fence.phase !== "award_pending" ||
2144
+ fence.activeAwardId !== award.awardId ||
2145
+ fence.recoveryCertificateId !==
2146
+ award.envelope.payload.recoveryCertificateId))
2147
+ throw new TypeError("Mesh recovery award timer binding is invalid");
2148
+ const allocation = restoreMeshAllocationState({
2149
+ ...state.allocation,
2150
+ receivedAwards: createFrozenRecord([
2151
+ ...recordEntries(state.allocation.receivedAwards).filter(([key]) => key !== award.awardId),
2152
+ [
2153
+ award.awardId,
2154
+ Object.freeze({ ...award, status: "timed_out" }),
2155
+ ],
2156
+ ]),
2157
+ ...(fenceKey === undefined || fence === undefined
2158
+ ? {}
2159
+ : {
2160
+ assignmentFenceHeads: createFrozenRecord([
2161
+ ...recordEntries(state.allocation.assignmentFenceHeads).filter(([key]) => key !== fenceKey),
2162
+ [
2163
+ fenceKey,
2164
+ Object.freeze({
2165
+ ...fence,
2166
+ activeAwardId: undefined,
2167
+ phase: "recovering",
2168
+ }),
2169
+ ],
2170
+ ]),
2171
+ }),
2172
+ lastLogicalTime: logicalTime,
2173
+ });
2174
+ const sequence = state.coordination.localEventSequence + 1;
2175
+ const coordination = Object.freeze({
2176
+ ...state.coordination,
2177
+ timers: createFrozenRecord(recordEntries(state.coordination.timers).filter(([key]) => key !== timer.timerId)),
2178
+ journal: Object.freeze([
2179
+ ...state.coordination.journal,
2180
+ Object.freeze({
2181
+ sequence,
2182
+ occurredAt: logicalTime,
2183
+ kind: "timer.fired",
2184
+ timerId: timer.timerId,
2185
+ domainRecordKey: timer.domainRecordKey,
2186
+ }),
2187
+ ]),
2188
+ localEventSequence: sequence,
2189
+ lastLogicalTime: logicalTime,
2190
+ });
2191
+ return Object.freeze({
2192
+ accepted: true,
2193
+ timer,
2194
+ state: createMeshAllocationRuntimeState(coordination, Object.freeze({ ...state.discovery, lastLogicalTime: logicalTime }), Object.freeze({ ...state.objectives, lastLogicalTime: logicalTime }), allocation),
2195
+ });
2196
+ }
2197
+ function evaluateAcceptanceDeadline(state, timer, logicalTime) {
2198
+ const award = Object.values(state.allocation.localAwards).find((entry) => entry.acceptanceDeadlineTimerId === timer.timerId &&
2199
+ entry.acceptanceDeadlineTimerGeneration === timer.generation);
2200
+ if (!award)
2201
+ throw new TypeError("Mesh acceptance timer is orphaned");
2202
+ const work = state.allocation.workAllocations[workKey(award.objectiveId, award.work.workItemId)];
2203
+ const reservation = state.allocation.reservations[award.reservationId];
2204
+ const objective = state.objectives.objectives[award.objectiveId];
2205
+ const recoveryAward = award.recipientAward.envelope.payload.authorityKind ===
2206
+ "recovery_certificate";
2207
+ const fenceKey = recoveryAward
2208
+ ? meshAssignmentFenceKey(award.recipientAward.envelope.payload)
2209
+ : undefined;
2210
+ const fence = fenceKey === undefined
2211
+ ? undefined
2212
+ : state.allocation.assignmentFenceHeads[fenceKey];
2213
+ if (!work ||
2214
+ !reservation ||
2215
+ !objective ||
2216
+ award.status !== "awaiting_acceptance" ||
2217
+ work.phase !== "award_pending" ||
2218
+ reservation.status !== (recoveryAward ? "committed" : "reserved") ||
2219
+ (!recoveryAward &&
2220
+ objective.reservedBudgetUnits < reservation.budgetReservationUnits) ||
2221
+ (recoveryAward &&
2222
+ (fence === undefined ||
2223
+ fence.phase !== "award_pending" ||
2224
+ fence.activeAwardId !== award.awardId ||
2225
+ fence.recoveryCertificateId !==
2226
+ award.recipientAward.envelope.payload.recoveryCertificateId)))
2227
+ throw new TypeError("Mesh acceptance deadline binding is invalid");
2228
+ const allocation = restoreMeshAllocationState({
2229
+ ...state.allocation,
2230
+ workAllocations: createFrozenRecord([
2231
+ ...recordEntries(state.allocation.workAllocations).filter(([key]) => key !== work.workKey),
2232
+ [
2233
+ work.workKey,
2234
+ Object.freeze({
2235
+ ...work,
2236
+ phase: recoveryAward ? "recovering" : "ready",
2237
+ activeAwardId: undefined,
2238
+ reservationId: recoveryAward ? reservation.reservationId : undefined,
2239
+ updatedAt: logicalTime,
2240
+ }),
2241
+ ],
2242
+ ]),
2243
+ localAwards: createFrozenRecord([
2244
+ ...recordEntries(state.allocation.localAwards).filter(([key]) => key !== award.awardId),
2245
+ [
2246
+ award.awardId,
2247
+ Object.freeze({ ...award, status: "timed_out" }),
2248
+ ],
2249
+ ]),
2250
+ reservations: recoveryAward
2251
+ ? state.allocation.reservations
2252
+ : createFrozenRecord([
2253
+ ...recordEntries(state.allocation.reservations).filter(([key]) => key !== reservation.reservationId),
2254
+ [
2255
+ reservation.reservationId,
2256
+ Object.freeze({
2257
+ ...reservation,
2258
+ status: "released",
2259
+ releasedAt: logicalTime,
2260
+ }),
2261
+ ],
2262
+ ]),
2263
+ ...(fenceKey === undefined || fence === undefined
2264
+ ? {}
2265
+ : {
2266
+ assignmentFenceHeads: createFrozenRecord([
2267
+ ...recordEntries(state.allocation.assignmentFenceHeads).filter(([key]) => key !== fenceKey),
2268
+ [
2269
+ fenceKey,
2270
+ Object.freeze({
2271
+ ...fence,
2272
+ activeAwardId: undefined,
2273
+ phase: "recovering",
2274
+ }),
2275
+ ],
2276
+ ]),
2277
+ }),
2278
+ lastLogicalTime: logicalTime,
2279
+ });
2280
+ const sequence = state.coordination.localEventSequence + 1;
2281
+ const coordination = Object.freeze({
2282
+ ...state.coordination,
2283
+ timers: createFrozenRecord(recordEntries(state.coordination.timers).filter(([key]) => key !== timer.timerId)),
2284
+ journal: Object.freeze([
2285
+ ...state.coordination.journal,
2286
+ Object.freeze({
2287
+ sequence,
2288
+ occurredAt: logicalTime,
2289
+ kind: "timer.fired",
2290
+ timerId: timer.timerId,
2291
+ domainRecordKey: timer.domainRecordKey,
2292
+ }),
2293
+ ]),
2294
+ localEventSequence: sequence,
2295
+ lastLogicalTime: logicalTime,
2296
+ });
2297
+ const objectives = recoveryAward
2298
+ ? Object.freeze({ ...state.objectives, lastLogicalTime: logicalTime })
2299
+ : Object.freeze({
2300
+ ...state.objectives,
2301
+ objectives: createFrozenRecord([
2302
+ ...recordEntries(state.objectives.objectives).filter(([key]) => key !== objective.objectiveId),
2303
+ [
2304
+ objective.objectiveId,
2305
+ Object.freeze({
2306
+ ...objective,
2307
+ reservedBudgetUnits: objective.reservedBudgetUnits -
2308
+ reservation.budgetReservationUnits,
2309
+ }),
2310
+ ],
2311
+ ]),
2312
+ lastLogicalTime: logicalTime,
2313
+ });
2314
+ return Object.freeze({
2315
+ accepted: true,
2316
+ timer,
2317
+ state: createMeshAllocationRuntimeState(coordination, Object.freeze({ ...state.discovery, lastLogicalTime: logicalTime }), objectives, allocation),
2318
+ });
2319
+ }
2320
+ function validatePrepared(recipients, state, work, verifiedAt, receivedAt, previousOffer) {
2321
+ if (!Array.isArray(recipients) ||
2322
+ !recipients.length ||
2323
+ recipients.length > state.allocation.limits.maximumRecipientsPerOffer)
2324
+ return { code: "recipient_capacity_exceeded" };
2325
+ const first = recipients[0]?.envelope;
2326
+ const parsed = first && validateSignedMeshEnvelope(first);
2327
+ if (!parsed?.ok || parsed.value.payload.type !== "work.offer")
2328
+ return { code: "offer_invalid" };
2329
+ const payload = parsed.value.payload;
2330
+ const bidDeadlineAt = logicalDeadline(payload.bidDeadline, verifiedAt, receivedAt);
2331
+ if (bidDeadlineAt === undefined)
2332
+ return { code: "offer_invalid" };
2333
+ const matches = matchMeshDiscoveryCapabilities(createMeshDiscoveryRuntimeState(state.coordination, state.discovery), {
2334
+ capabilityKeys: work.requiredCapabilityKeys,
2335
+ attributes: work.matchingAttributes,
2336
+ fanout: Math.min(state.allocation.limits.maximumRecipientsPerOffer, state.discovery.limits.maximumFanout),
2337
+ }, receivedAt).matches.map((match) => match.peerId);
2338
+ const ids = recipients.map((item) => item.recipientPeerId);
2339
+ if (new Set(ids).size !== ids.length ||
2340
+ ids.length !== matches.length ||
2341
+ ids.some((id, index) => id !== matches[index]))
2342
+ return { code: "offer_invalid" };
2343
+ const entries = [];
2344
+ const retainedMessageIds = new Set([
2345
+ ...Object.values(state.coordination.domainRecords).map((record) => record.messageId),
2346
+ ...Object.values(state.allocation.localOffers).flatMap((offer) => Object.values(offer.recipientOffers).map((recipient) => recipient.messageId)),
2347
+ ...Object.values(state.allocation.acceptedBidEvidence).map((evidence) => evidence.envelope.messageId),
2348
+ ]);
2349
+ const preparedMessageIds = new Set();
2350
+ for (const item of recipients) {
2351
+ const result = validateMeshEnvelopeContext(item.envelope, {
2352
+ tenantId: state.allocation.identity.tenantId,
2353
+ meshId: state.allocation.identity.meshId,
2354
+ peerId: item.recipientPeerId,
2355
+ receivedAt: verifiedAt,
2356
+ });
2357
+ if (!result.ok ||
2358
+ result.value.payload.type !== "work.offer" ||
2359
+ item.preparedAt !== receivedAt ||
2360
+ result.value.sender.peerId !== state.allocation.identity.peerId ||
2361
+ result.value.sender.instanceId !== state.allocation.identity.instanceId ||
2362
+ result.value.proof.keyId !== state.allocation.identity.keyId ||
2363
+ result.value.audience.kind !== "peer" ||
2364
+ result.value.audience.peerId !== item.recipientPeerId ||
2365
+ (payload.offerAttempt === 1
2366
+ ? result.value.causationId !== undefined
2367
+ : result.value.causationId !==
2368
+ previousOffer?.recipientOffers[item.recipientPeerId]?.messageId) ||
2369
+ !samePayload(result.value.payload, payload) ||
2370
+ !canonicalDigest(result.value))
2371
+ return { code: "offer_invalid" };
2372
+ if (retainedMessageIds.has(result.value.messageId) ||
2373
+ preparedMessageIds.has(result.value.messageId))
2374
+ return { code: "offer_duplicate_conflict" };
2375
+ preparedMessageIds.add(result.value.messageId);
2376
+ entries.push({
2377
+ recipientPeerId: item.recipientPeerId,
2378
+ preparedAt: item.preparedAt,
2379
+ envelope: result.value,
2380
+ });
2381
+ }
2382
+ return { payload, entries: Object.freeze(entries), bidDeadlineAt };
2383
+ }
2384
+ function validateContext(state, request) {
2385
+ if (!request ||
2386
+ typeof request !== "object" ||
2387
+ !request.envelope ||
2388
+ typeof request.verifiedAt !== "string" ||
2389
+ !Number.isSafeInteger(request.receivedAt) ||
2390
+ request.receivedAt < 0 ||
2391
+ Object.keys(request).some((key) => key !== "envelope" &&
2392
+ key !== "verifiedAt" &&
2393
+ key !== "receivedAt" &&
2394
+ key !== "supportedCriticalExtensions") ||
2395
+ (request.supportedCriticalExtensions !== undefined &&
2396
+ (!Array.isArray(request.supportedCriticalExtensions) ||
2397
+ request.supportedCriticalExtensions.some((value) => typeof value !== "string" || !value) ||
2398
+ new Set(request.supportedCriticalExtensions).size !==
2399
+ request.supportedCriticalExtensions.length)))
2400
+ throw new TypeError("Invalid Mesh verified allocation request");
2401
+ const result = validateMeshEnvelopeContext(request.envelope, {
2402
+ tenantId: state.allocation.identity.tenantId,
2403
+ meshId: state.allocation.identity.meshId,
2404
+ peerId: state.allocation.identity.peerId,
2405
+ receivedAt: request.verifiedAt,
2406
+ subscribedTopics: state.discovery.subscriptions,
2407
+ ...(request.supportedCriticalExtensions === undefined
2408
+ ? {}
2409
+ : { supportedCriticalExtensions: request.supportedCriticalExtensions }),
2410
+ });
2411
+ if (!result.ok) {
2412
+ const code = result.issues[0]?.code;
2413
+ return code === "scope_mismatch"
2414
+ ? "scope_mismatch"
2415
+ : code === "message_expired"
2416
+ ? "message_expired"
2417
+ : code === "message_from_future"
2418
+ ? "message_from_future"
2419
+ : code === "unknown_critical_extension"
2420
+ ? "unknown_critical_extension"
2421
+ : code === "invalid_audience"
2422
+ ? "audience_mismatch"
2423
+ : "invalid_verified_envelope";
2424
+ }
2425
+ const admission = state.discovery.admittedPeers[request.envelope.sender.peerId];
2426
+ if (!admission)
2427
+ return "sender_not_admitted";
2428
+ if (!admission.instanceIds.includes(request.envelope.sender.instanceId))
2429
+ return "sender_instance_not_admitted";
2430
+ return compare(request.verifiedAt, admission.validUntil) >= 0
2431
+ ? "sender_admission_expired"
2432
+ : undefined;
2433
+ }
2434
+ function matchesOffer(bid, offer) {
2435
+ const work = offer.work;
2436
+ return (bid.offerId === offer.offerId &&
2437
+ bid.objectiveId === offer.objectiveId &&
2438
+ bid.objectiveDocumentId === offer.objectiveDocumentId &&
2439
+ bid.objectiveRevision === offer.objectiveRevision &&
2440
+ bid.workItemId === work.workItemId &&
2441
+ bid.workItemRevision === work.workItemRevision &&
2442
+ bid.ownerPeerId === work.ownerPeerId &&
2443
+ bid.ownerEpoch === work.ownerEpoch &&
2444
+ bid.offerAttempt === offer.offerAttempt);
2445
+ }
2446
+ function hasActiveCapability(state, offer, envelope, verifiedAt, logicalTime) {
2447
+ const capability = state.discovery.capabilities[JSON.stringify([
2448
+ envelope.payload.bidderPeerId,
2449
+ envelope.payload.capabilityId,
2450
+ ])];
2451
+ return (capability !== undefined &&
2452
+ compare(verifiedAt, capability.validUntil) < 0 &&
2453
+ hasEligibleCapabilityAt(state, offer, envelope, logicalTime));
2454
+ }
2455
+ function hasEligibleCapabilityAt(state, offer, envelope, logicalTime) {
2456
+ const payload = envelope.payload;
2457
+ const capability = state.discovery.capabilities[JSON.stringify([payload.bidderPeerId, payload.capabilityId])];
2458
+ const card = state.discovery.peerCards[payload.bidderPeerId];
2459
+ if (!capability ||
2460
+ capability.status !== "active" ||
2461
+ capability.ownerPeerId !== payload.bidderPeerId ||
2462
+ capability.instanceId !== envelope.sender.instanceId ||
2463
+ capability.advertisementId !== payload.advertisementId ||
2464
+ capability.capabilityRevision !== payload.capabilityRevision ||
2465
+ capability.expiresAt <= logicalTime ||
2466
+ !offer.work.requiredCapabilityKeys.includes(capability.capabilityKey) ||
2467
+ !Object.entries(offer.work.matchingAttributes).every(([key, value]) => capability.attributes[key] === value) ||
2468
+ !card ||
2469
+ card.status !== "active" ||
2470
+ card.instanceId !== envelope.sender.instanceId ||
2471
+ card.expiresAt <= logicalTime) {
2472
+ return false;
2473
+ }
2474
+ const result = matchMeshDiscoveryCapabilities(createMeshDiscoveryRuntimeState(state.coordination, state.discovery), {
2475
+ capabilityKeys: offer.work.requiredCapabilityKeys,
2476
+ attributes: offer.work.matchingAttributes,
2477
+ fanout: state.discovery.limits.maximumFanout,
2478
+ }, logicalTime);
2479
+ const evaluation = result.evaluations.find((entry) => entry.peerId === payload.bidderPeerId);
2480
+ return (evaluation?.reason === "eligible" || evaluation?.reason === "fanout_limited");
2481
+ }
2482
+ function matchesReceivedOffer(bid, offer) {
2483
+ const payload = offer.envelope.payload;
2484
+ return (bid.offerId === offer.offerId &&
2485
+ bid.objectiveId === payload.objectiveId &&
2486
+ bid.objectiveDocumentId === payload.objectiveDocumentId &&
2487
+ bid.objectiveRevision === payload.objectiveRevision &&
2488
+ bid.workItemId === payload.workItemId &&
2489
+ bid.workItemRevision === payload.workItemRevision &&
2490
+ bid.ownerPeerId === payload.ownerPeerId &&
2491
+ bid.ownerEpoch === payload.ownerEpoch &&
2492
+ bid.offerAttempt === payload.offerAttempt &&
2493
+ bid.bidDeadline === payload.bidDeadline &&
2494
+ bid.workDeadline === payload.workDeadline);
2495
+ }
2496
+ function sameReceivedOfferScope(left, right) {
2497
+ return (left.objectiveId === right.objectiveId &&
2498
+ left.objectiveRevision === right.objectiveRevision &&
2499
+ left.workItemId === right.workItemId &&
2500
+ left.workItemRevision === right.workItemRevision &&
2501
+ left.ownerPeerId === right.ownerPeerId &&
2502
+ left.ownerEpoch === right.ownerEpoch);
2503
+ }
2504
+ function sameReceivedOfferWorkTerms(left, right) {
2505
+ return (left.objectiveDocumentId === right.objectiveDocumentId &&
2506
+ sameReceivedOfferScope(left, right) &&
2507
+ sameArray(left.requiredCapabilityKeys, right.requiredCapabilityKeys) &&
2508
+ sameRecord(left.matchingAttributes, right.matchingAttributes) &&
2509
+ sameArray(left.completionCriteria, right.completionCriteria) &&
2510
+ left.inputSummary === right.inputSummary &&
2511
+ left.inputReference === right.inputReference &&
2512
+ left.budgetReservationUnits === right.budgetReservationUnits &&
2513
+ left.workDeadline === right.workDeadline);
2514
+ }
2515
+ function matchesAwardOffer(award, offer) {
2516
+ const payload = offer.envelope.payload;
2517
+ return (award.offerId === offer.offerId &&
2518
+ award.objectiveId === payload.objectiveId &&
2519
+ award.objectiveDocumentId === payload.objectiveDocumentId &&
2520
+ award.objectiveRevision === payload.objectiveRevision &&
2521
+ award.workItemId === payload.workItemId &&
2522
+ award.workItemRevision === payload.workItemRevision &&
2523
+ award.ownerPeerId === payload.ownerPeerId &&
2524
+ award.ownerEpoch === payload.ownerEpoch &&
2525
+ award.offerAttempt === payload.offerAttempt &&
2526
+ award.workDeadline === payload.workDeadline &&
2527
+ award.budgetReservationUnits === payload.budgetReservationUnits);
2528
+ }
2529
+ function matchesAwardResponse(response, award) {
2530
+ const payload = award.envelope.payload;
2531
+ return (response.awardId === award.awardId &&
2532
+ response.objectiveId === payload.objectiveId &&
2533
+ response.objectiveDocumentId === payload.objectiveDocumentId &&
2534
+ response.objectiveRevision === payload.objectiveRevision &&
2535
+ response.workItemId === payload.workItemId &&
2536
+ response.workItemRevision === payload.workItemRevision &&
2537
+ response.ownerPeerId === payload.ownerPeerId &&
2538
+ response.ownerEpoch === payload.ownerEpoch &&
2539
+ response.assigneePeerId === payload.assigneePeerId &&
2540
+ response.assignmentEpoch === payload.assignmentEpoch &&
2541
+ response.assignmentAuthorityId === payload.assignmentAuthorityId &&
2542
+ response.fencingToken === payload.fencingToken &&
2543
+ response.acceptanceDeadline === payload.acceptanceDeadline);
2544
+ }
2545
+ function allocationWriteCapacity(state, requiresTimer = false) {
2546
+ if (Object.keys(state.coordination.domainRecords).length >=
2547
+ state.coordination.limits.maximumDomainRecords)
2548
+ return "domain_capacity_exceeded";
2549
+ if (state.coordination.journal.length >=
2550
+ state.coordination.limits.maximumJournalEntries)
2551
+ return "journal_capacity_exceeded";
2552
+ if (requiresTimer &&
2553
+ Object.keys(state.coordination.timers).length >=
2554
+ state.coordination.limits.maximumTimers)
2555
+ return "timer_capacity_exceeded";
2556
+ if (state.coordination.localEventSequence >= Number.MAX_SAFE_INTEGER)
2557
+ throw new RangeError("Mesh coordination event sequence exhausted");
2558
+ return undefined;
2559
+ }
2560
+ function acceptAssigneeWrite(state, logicalTime, domainRecordKey, recordType, recordId, objectiveId, envelope, allocationSnapshot, effect, timer, removeTimerId) {
2561
+ const allocation = restoreMeshAllocationState(allocationSnapshot);
2562
+ const sequence = state.coordination.localEventSequence + 1;
2563
+ const coordination = Object.freeze({
2564
+ ...state.coordination,
2565
+ domainRecords: createFrozenRecord([
2566
+ ...recordEntries(state.coordination.domainRecords),
2567
+ [
2568
+ domainRecordKey,
2569
+ Object.freeze({
2570
+ recordKey: domainRecordKey,
2571
+ recordType,
2572
+ recordId,
2573
+ contentDigest: payloadDigest(envelope),
2574
+ messageId: envelope.messageId,
2575
+ acceptedAt: logicalTime,
2576
+ objectiveId,
2577
+ }),
2578
+ ],
2579
+ ]),
2580
+ timers: timer === undefined && removeTimerId === undefined
2581
+ ? state.coordination.timers
2582
+ : createFrozenRecord([
2583
+ ...recordEntries(state.coordination.timers).filter(([key]) => key !== removeTimerId),
2584
+ ...(timer === undefined ? [] : [[timer.timerId, timer]]),
2585
+ ]),
2586
+ journal: Object.freeze([
2587
+ ...state.coordination.journal,
2588
+ Object.freeze({
2589
+ sequence,
2590
+ occurredAt: logicalTime,
2591
+ kind: "domain.accepted",
2592
+ domainRecordKey,
2593
+ }),
2594
+ ]),
2595
+ localEventSequence: sequence,
2596
+ lastLogicalTime: logicalTime,
2597
+ });
2598
+ return Object.freeze({
2599
+ accepted: true,
2600
+ duplicate: false,
2601
+ state: createMeshAllocationRuntimeState(coordination, Object.freeze({ ...state.discovery, lastLogicalTime: logicalTime }), Object.freeze({ ...state.objectives, lastLogicalTime: logicalTime }), allocation),
2602
+ effects: Object.freeze(effect === undefined ? [] : [effect]),
2603
+ });
2604
+ }
2605
+ function assertRuntime(state, time) {
2606
+ if (!state ||
2607
+ typeof state !== "object" ||
2608
+ !Object.isFrozen(state) ||
2609
+ Object.keys(state).sort().join(",") !==
2610
+ "allocation,coordination,discovery,objectives") {
2611
+ throw new TypeError("Mesh allocation runtime state is required");
2612
+ }
2613
+ createMeshAllocationRuntimeState(state.coordination, state.discovery, state.objectives, state.allocation);
2614
+ assertMeshLogicalTime(time);
2615
+ if (time < state.coordination.lastLogicalTime ||
2616
+ time < state.discovery.lastLogicalTime ||
2617
+ time < state.objectives.lastLogicalTime ||
2618
+ time < state.allocation.lastLogicalTime)
2619
+ throw new RangeError("Mesh allocation logical time cannot move backwards");
2620
+ }
2621
+ function reject(state, code) {
2622
+ return Object.freeze({ accepted: false, code, state });
2623
+ }
2624
+ function samePayload(a, b) {
2625
+ return JSON.stringify(a) === JSON.stringify(b);
2626
+ }
2627
+ function sameData(left, right) {
2628
+ if (left === right)
2629
+ return true;
2630
+ if (Array.isArray(left) || Array.isArray(right))
2631
+ return (Array.isArray(left) &&
2632
+ Array.isArray(right) &&
2633
+ left.length === right.length &&
2634
+ left.every((value, index) => sameData(value, right[index])));
2635
+ if (!left || !right || typeof left !== "object" || typeof right !== "object")
2636
+ return false;
2637
+ const leftKeys = Object.keys(left).sort(utf16);
2638
+ const rightKeys = Object.keys(right).sort(utf16);
2639
+ return (sameArray(leftKeys, rightKeys) &&
2640
+ leftKeys.every((key) => sameData(left[key], right[key])));
2641
+ }
2642
+ function canonicalDigest(envelope) {
2643
+ const canonical = canonicalizeMeshPayload(envelope.payload);
2644
+ return (canonical.ok &&
2645
+ envelope.payloadHash === `sha256:${sha256Base64Url(canonical.value)}`);
2646
+ }
2647
+ function payloadDigest(envelope) {
2648
+ const canonical = canonicalizeMeshPayload(envelope.payload);
2649
+ if (!canonical.ok)
2650
+ throw new TypeError("Invalid Mesh bid payload");
2651
+ return sha256Base64Url(canonical.value);
2652
+ }
2653
+ function compare(left, right) {
2654
+ const result = compareMeshTimestamps(left, right);
2655
+ if (!result.ok)
2656
+ throw new TypeError("Invalid Mesh allocation timestamp");
2657
+ return result.value;
2658
+ }
2659
+ function utf16(left, right) {
2660
+ return left < right ? -1 : left > right ? 1 : 0;
2661
+ }
2662
+ function sameArray(left, right) {
2663
+ return (left.length === right.length &&
2664
+ left.every((value, index) => value === right[index]));
2665
+ }
2666
+ function sameRecord(left, right) {
2667
+ const keys = Object.keys(left);
2668
+ return (keys.length === Object.keys(right).length &&
2669
+ keys.every((key) => left[key] === right[key]));
2670
+ }
2671
+ function sameInput(payload, work) {
2672
+ return (payload.inputSummary === work.inputSummary &&
2673
+ payload.inputReference === work.inputReference);
2674
+ }
2675
+ function createInitialLeaseActivation(authority) {
2676
+ const scope = JSON.stringify([
2677
+ authority.objectiveId,
2678
+ authority.objectiveRevision,
2679
+ authority.workItemId,
2680
+ authority.workItemRevision,
2681
+ authority.ownerPeerId,
2682
+ authority.ownerEpoch,
2683
+ authority.awardId,
2684
+ authority.assignmentEpoch,
2685
+ ]);
2686
+ const timerId = `lease.expiry:${sha256Base64Url(utf8Encoder.encode(scope))}`;
2687
+ const head = Object.freeze({
2688
+ executionScopeKey: scope,
2689
+ objectiveId: authority.objectiveId,
2690
+ objectiveDocumentId: authority.objectiveDocumentId,
2691
+ objectiveRevision: authority.objectiveRevision,
2692
+ workItemId: authority.workItemId,
2693
+ workItemRevision: authority.workItemRevision,
2694
+ ownerPeerId: authority.ownerPeerId,
2695
+ ownerEpoch: authority.ownerEpoch,
2696
+ assigneePeerId: authority.assigneePeerId,
2697
+ awardId: authority.awardId,
2698
+ assignmentEpoch: authority.assignmentEpoch,
2699
+ assignmentAuthorityId: authority.assignmentAuthorityId,
2700
+ fencingToken: authority.fencingToken,
2701
+ acceptanceId: authority.acceptanceId,
2702
+ acceptanceMessageId: authority.acceptanceMessageId,
2703
+ originalLeaseExpiresAt: authority.leaseExpiresAt,
2704
+ originalLeaseExpiresAtLogical: authority.leaseExpiresAtLogical,
2705
+ workDeadline: authority.workDeadline,
2706
+ workDeadlineAt: authority.workDeadlineAt,
2707
+ leaseRenewalSequence: 0,
2708
+ currentLeaseExpiresAt: authority.leaseExpiresAt,
2709
+ currentLeaseExpiresAtLogical: authority.leaseExpiresAtLogical,
2710
+ status: "active",
2711
+ expiryTimerId: timerId,
2712
+ expiryTimerGeneration: 1,
2713
+ });
2714
+ return Object.freeze({
2715
+ head,
2716
+ timer: Object.freeze({
2717
+ timerId,
2718
+ kind: "lease.expiry",
2719
+ dueAt: authority.leaseExpiresAtLogical,
2720
+ generation: 1,
2721
+ domainRecordKey: JSON.stringify(["work.accept", authority.acceptanceId]),
2722
+ }),
2723
+ });
2724
+ }
2725
+ function workKey(objectiveId, workItemId) {
2726
+ return JSON.stringify([objectiveId, workItemId]);
2727
+ }
2728
+ function policyKey(objectiveId, revision) {
2729
+ return JSON.stringify([objectiveId, revision]);
2730
+ }
2731
+ function bidKey(offerId, bidderPeerId) {
2732
+ return JSON.stringify([offerId, bidderPeerId]);
2733
+ }
2734
+ function bidDomainKey(bidId) {
2735
+ return JSON.stringify(["work.bid", bidId]);
2736
+ }
2737
+ function awardDomainKey(awardId) {
2738
+ return JSON.stringify(["work.award", awardId]);
2739
+ }
2740
+ function offerDomainKey(offerId) {
2741
+ return JSON.stringify(["work.offer", offerId]);
2742
+ }
2743
+ //# sourceMappingURL=coordination-allocation.js.map