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

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (102) hide show
  1. package/README.md +247 -2
  2. package/dist/coordination-allocation-contracts.d.ts +598 -0
  3. package/dist/coordination-allocation-contracts.d.ts.map +1 -0
  4. package/dist/coordination-allocation-contracts.js +2 -0
  5. package/dist/coordination-allocation-contracts.js.map +1 -0
  6. package/dist/coordination-allocation-state.d.ts +19 -0
  7. package/dist/coordination-allocation-state.d.ts.map +1 -0
  8. package/dist/coordination-allocation-state.js +5496 -0
  9. package/dist/coordination-allocation-state.js.map +1 -0
  10. package/dist/coordination-allocation.d.ts +10 -0
  11. package/dist/coordination-allocation.d.ts.map +1 -0
  12. package/dist/coordination-allocation.js +2743 -0
  13. package/dist/coordination-allocation.js.map +1 -0
  14. package/dist/coordination-contracts.d.ts +78 -0
  15. package/dist/coordination-contracts.d.ts.map +1 -0
  16. package/dist/coordination-contracts.js +2 -0
  17. package/dist/coordination-contracts.js.map +1 -0
  18. package/dist/coordination-discovery-contracts.d.ts +150 -0
  19. package/dist/coordination-discovery-contracts.d.ts.map +1 -0
  20. package/dist/coordination-discovery-contracts.js +2 -0
  21. package/dist/coordination-discovery-contracts.js.map +1 -0
  22. package/dist/coordination-discovery-state.d.ts +12 -0
  23. package/dist/coordination-discovery-state.d.ts.map +1 -0
  24. package/dist/coordination-discovery-state.js +542 -0
  25. package/dist/coordination-discovery-state.js.map +1 -0
  26. package/dist/coordination-discovery.d.ts +19 -0
  27. package/dist/coordination-discovery.d.ts.map +1 -0
  28. package/dist/coordination-discovery.js +850 -0
  29. package/dist/coordination-discovery.js.map +1 -0
  30. package/dist/coordination-execution.d.ts +6 -0
  31. package/dist/coordination-execution.d.ts.map +1 -0
  32. package/dist/coordination-execution.js +854 -0
  33. package/dist/coordination-execution.js.map +1 -0
  34. package/dist/coordination-inbound-contracts.d.ts +150 -0
  35. package/dist/coordination-inbound-contracts.d.ts.map +1 -0
  36. package/dist/coordination-inbound-contracts.js +2 -0
  37. package/dist/coordination-inbound-contracts.js.map +1 -0
  38. package/dist/coordination-inbound-state.d.ts +26 -0
  39. package/dist/coordination-inbound-state.d.ts.map +1 -0
  40. package/dist/coordination-inbound-state.js +331 -0
  41. package/dist/coordination-inbound-state.js.map +1 -0
  42. package/dist/coordination-inbound.d.ts +16 -0
  43. package/dist/coordination-inbound.d.ts.map +1 -0
  44. package/dist/coordination-inbound.js +944 -0
  45. package/dist/coordination-inbound.js.map +1 -0
  46. package/dist/coordination-lease-renewal.d.ts +8 -0
  47. package/dist/coordination-lease-renewal.d.ts.map +1 -0
  48. package/dist/coordination-lease-renewal.js +518 -0
  49. package/dist/coordination-lease-renewal.js.map +1 -0
  50. package/dist/coordination-objective-topic-contracts.d.ts +70 -0
  51. package/dist/coordination-objective-topic-contracts.d.ts.map +1 -0
  52. package/dist/coordination-objective-topic-contracts.js +2 -0
  53. package/dist/coordination-objective-topic-contracts.js.map +1 -0
  54. package/dist/coordination-objective-topic.d.ts +6 -0
  55. package/dist/coordination-objective-topic.d.ts.map +1 -0
  56. package/dist/coordination-objective-topic.js +561 -0
  57. package/dist/coordination-objective-topic.js.map +1 -0
  58. package/dist/coordination-objective-work-contracts.d.ts +212 -0
  59. package/dist/coordination-objective-work-contracts.d.ts.map +1 -0
  60. package/dist/coordination-objective-work-contracts.js +2 -0
  61. package/dist/coordination-objective-work-contracts.js.map +1 -0
  62. package/dist/coordination-objective-work-state.d.ts +10 -0
  63. package/dist/coordination-objective-work-state.d.ts.map +1 -0
  64. package/dist/coordination-objective-work-state.js +1075 -0
  65. package/dist/coordination-objective-work-state.js.map +1 -0
  66. package/dist/coordination-objective-work-time.d.ts +9 -0
  67. package/dist/coordination-objective-work-time.d.ts.map +1 -0
  68. package/dist/coordination-objective-work-time.js +48 -0
  69. package/dist/coordination-objective-work-time.js.map +1 -0
  70. package/dist/coordination-objective-work.d.ts +11 -0
  71. package/dist/coordination-objective-work.d.ts.map +1 -0
  72. package/dist/coordination-objective-work.js +1090 -0
  73. package/dist/coordination-objective-work.js.map +1 -0
  74. package/dist/coordination-recovery.d.ts +13 -0
  75. package/dist/coordination-recovery.d.ts.map +1 -0
  76. package/dist/coordination-recovery.js +951 -0
  77. package/dist/coordination-recovery.js.map +1 -0
  78. package/dist/coordination-reducer.d.ts +8 -0
  79. package/dist/coordination-reducer.d.ts.map +1 -0
  80. package/dist/coordination-reducer.js +81 -0
  81. package/dist/coordination-reducer.js.map +1 -0
  82. package/dist/coordination-state.d.ts +13 -0
  83. package/dist/coordination-state.d.ts.map +1 -0
  84. package/dist/coordination-state.js +335 -0
  85. package/dist/coordination-state.js.map +1 -0
  86. package/dist/coordination-topic-contracts.d.ts +82 -0
  87. package/dist/coordination-topic-contracts.d.ts.map +1 -0
  88. package/dist/coordination-topic-contracts.js +2 -0
  89. package/dist/coordination-topic-contracts.js.map +1 -0
  90. package/dist/coordination-topic.d.ts +6 -0
  91. package/dist/coordination-topic.d.ts.map +1 -0
  92. package/dist/coordination-topic.js +561 -0
  93. package/dist/coordination-topic.js.map +1 -0
  94. package/dist/coordination.d.ts +21 -0
  95. package/dist/coordination.d.ts.map +1 -0
  96. package/dist/coordination.js +21 -0
  97. package/dist/coordination.js.map +1 -0
  98. package/dist/sha256.d.ts +3 -0
  99. package/dist/sha256.d.ts.map +1 -0
  100. package/dist/sha256.js +117 -0
  101. package/dist/sha256.js.map +1 -0
  102. package/package.json +7 -3
@@ -0,0 +1,854 @@
1
+ import { canonicalizeMeshPayload, compareMeshTimestamps, validateMeshEnvelopeContext, validateSignedMeshEnvelope, } from "@agentplat/mesh-protocol";
2
+ import { createMeshAllocationRuntimeState, meshAllocationRetainsMessageId, meshAssignmentFenceKey, restoreMeshAllocationState, } from "./coordination-allocation-state.js";
3
+ import { sha256Base64Url } from "./sha256.js";
4
+ import { assertMeshLogicalTime, createFrozenRecord, recordEntries, } from "./state.js";
5
+ /** Applies a locally prepared execution lifecycle envelope before dispatch. */
6
+ export function evaluateMeshExecutionCommand(state, command, verifiedAt, receivedAt) {
7
+ assertExecutionRuntime(state, receivedAt);
8
+ if (!command ||
9
+ typeof command !== "object" ||
10
+ Object.keys(command).sort().join(",") !== "envelope,kind,preparedAt" ||
11
+ command.kind !== "allocation.execution" ||
12
+ !Number.isSafeInteger(command.preparedAt) ||
13
+ command.preparedAt < 0)
14
+ throw new TypeError("Invalid Mesh execution command");
15
+ const parsed = validateSignedMeshEnvelope(command.envelope);
16
+ if (!parsed.ok || !isExecutionPayload(parsed.value.payload))
17
+ return reject(state, "execution_invalid");
18
+ if (command.preparedAt > receivedAt)
19
+ return reject(state, "execution_invalid");
20
+ return applyExecution(state, parsed.value, verifiedAt, receivedAt, "local");
21
+ }
22
+ /** Applies one authenticated remote execution lifecycle record. */
23
+ export function evaluateVerifiedMeshExecutionEnvelope(state, request) {
24
+ if (!request ||
25
+ typeof request !== "object" ||
26
+ !request.envelope ||
27
+ typeof request.verifiedAt !== "string" ||
28
+ !Number.isSafeInteger(request.receivedAt) ||
29
+ request.receivedAt < 0 ||
30
+ Object.keys(request).some((key) => key !== "envelope" &&
31
+ key !== "verifiedAt" &&
32
+ key !== "receivedAt" &&
33
+ key !== "supportedCriticalExtensions") ||
34
+ (request.supportedCriticalExtensions !== undefined &&
35
+ (!Array.isArray(request.supportedCriticalExtensions) ||
36
+ request.supportedCriticalExtensions.some((value) => typeof value !== "string" || value.length === 0) ||
37
+ new Set(request.supportedCriticalExtensions).size !==
38
+ request.supportedCriticalExtensions.length)))
39
+ throw new TypeError("Invalid Mesh verified execution request");
40
+ assertExecutionRuntime(state, request.receivedAt);
41
+ if (!isExecutionPayload(request.envelope.payload))
42
+ return reject(state, "execution_invalid");
43
+ return applyExecution(state, request.envelope, request.verifiedAt, request.receivedAt, "received", request.supportedCriticalExtensions);
44
+ }
45
+ function applyExecution(state, envelope, verifiedAt, recordedAt, direction, supportedCriticalExtensions) {
46
+ const payload = envelope.payload;
47
+ const recordType = typeFor(payload);
48
+ const recordId = idFor(payload);
49
+ const recordKey = JSON.stringify([payload.type, recordId]);
50
+ const existing = state.allocation.executionRecords[recordId];
51
+ if (existing)
52
+ return existing.direction === direction &&
53
+ sameData(existing.envelope, envelope)
54
+ ? acceptedDuplicate(state)
55
+ : reject(state, "execution_duplicate_conflict");
56
+ if (meshAllocationRetainsMessageId(state, envelope.messageId))
57
+ return reject(state, "execution_duplicate_conflict");
58
+ if (state.coordination.domainRecords[recordKey])
59
+ return reject(state, "domain_record_conflict");
60
+ const context = validateMeshEnvelopeContext(envelope, {
61
+ tenantId: state.allocation.identity.tenantId,
62
+ meshId: state.allocation.identity.meshId,
63
+ peerId: envelope.audience.kind === "peer"
64
+ ? envelope.audience.peerId
65
+ : state.allocation.identity.peerId,
66
+ receivedAt: verifiedAt,
67
+ ...(supportedCriticalExtensions === undefined
68
+ ? {}
69
+ : { supportedCriticalExtensions }),
70
+ });
71
+ if (!context.ok)
72
+ return reject(state, contextCode(context.issues[0]?.code ?? ""));
73
+ if (!canonicalDigest(envelope))
74
+ return reject(state, "execution_invalid");
75
+ if (direction === "local" &&
76
+ (envelope.sender.peerId !== state.allocation.identity.peerId ||
77
+ envelope.sender.instanceId !== state.allocation.identity.instanceId ||
78
+ envelope.proof.keyId !== state.allocation.identity.keyId))
79
+ return reject(state, "execution_authority_invalid");
80
+ const authority = resolveAuthority(state, payload);
81
+ if (!authority)
82
+ return reject(state, "execution_authority_invalid");
83
+ const scope = executionScopeKey(authority);
84
+ const prior = state.allocation.executionHeads[scope];
85
+ if (prior && !sameAuthority(prior, authority))
86
+ return reject(state, "execution_authority_invalid");
87
+ const leaseBound = requiresActiveLease(payload);
88
+ if ((leaseBound &&
89
+ (recordedAt >= authority.leaseExpiresAtLogical ||
90
+ compare(verifiedAt, authority.leaseExpiresAt) >= 0)) ||
91
+ recordedAt >= authority.workDeadlineAt ||
92
+ compare(verifiedAt, authority.workDeadline) >= 0)
93
+ return reject(state, "execution_deadline_elapsed");
94
+ if (!hasValidAudienceAndRole(state, envelope, authority, direction))
95
+ return reject(state, "execution_authority_invalid");
96
+ if (!hasValidAuthorityFields(payload, authority))
97
+ return reject(state, "execution_authority_invalid");
98
+ const pending = payload.type === "work.cancel" &&
99
+ payload.assignmentState === "award_pending";
100
+ if (pending) {
101
+ if (!cancelPendingValid(state, envelope, authority, direction))
102
+ return reject(state, "execution_authority_invalid");
103
+ }
104
+ else if (!validTransition(state, prior, envelope, authority)) {
105
+ return reject(state, "execution_phase_invalid");
106
+ }
107
+ if (Object.keys(state.allocation.executionRecords).length >=
108
+ state.allocation.limits.maximumExecutionRecords)
109
+ return reject(state, "execution_capacity_exceeded");
110
+ if (Object.values(state.allocation.executionRecords).filter((record) => executionScopeForPayload(record.envelope.payload) === scope).length >= state.allocation.limits.maximumExecutionRecordsPerAssignment)
111
+ return reject(state, "execution_records_per_assignment_exceeded");
112
+ if (!prior &&
113
+ !pending &&
114
+ Object.keys(state.allocation.executionHeads).length >=
115
+ state.allocation.limits.maximumExecutionHeads)
116
+ return reject(state, "execution_head_capacity_exceeded");
117
+ if (Object.keys(state.coordination.domainRecords).length >=
118
+ state.coordination.limits.maximumDomainRecords)
119
+ return reject(state, "domain_capacity_exceeded");
120
+ if (state.coordination.journal.length >=
121
+ state.coordination.limits.maximumJournalEntries)
122
+ return reject(state, "journal_capacity_exceeded");
123
+ if (state.coordination.localEventSequence >= Number.MAX_SAFE_INTEGER)
124
+ throw new RangeError("Mesh coordination event sequence exhausted");
125
+ const executionRecord = Object.freeze({
126
+ recordType,
127
+ recordId,
128
+ direction,
129
+ recordedAt,
130
+ validityVerifiedAt: verifiedAt,
131
+ ...(supportedCriticalExtensions === undefined
132
+ ? {}
133
+ : {
134
+ supportedCriticalExtensions: Object.freeze([
135
+ ...supportedCriticalExtensions,
136
+ ]),
137
+ }),
138
+ envelope,
139
+ });
140
+ const nextHead = pending
141
+ ? undefined
142
+ : updateHead(prior, authority, payload, recordId, recordedAt);
143
+ const pendingState = pending
144
+ ? cancelPending(state, payload, recordedAt, direction)
145
+ : applyOperationalTransition(state, payload, recordedAt);
146
+ if (!pendingState)
147
+ return reject(state, "execution_authority_invalid");
148
+ const terminalLeaseHead = nextHead !== undefined && nextHead.phase !== "active"
149
+ ? pendingState.allocation.leaseHeads[scope]
150
+ : undefined;
151
+ const terminalLeaseProjection = terminalLeaseHead === undefined
152
+ ? undefined
153
+ : withoutLeaseTimer(terminalLeaseHead);
154
+ const terminalFence = terminalLeaseProjection === undefined
155
+ ? undefined
156
+ : pendingState.allocation.assignmentFenceHeads[meshAssignmentFenceKey(terminalLeaseProjection)];
157
+ const allocation = restoreMeshAllocationState({
158
+ ...pendingState.allocation,
159
+ executionRecords: createFrozenRecord([
160
+ ...recordEntries(pendingState.allocation.executionRecords),
161
+ [recordId, executionRecord],
162
+ ]),
163
+ ...(nextHead === undefined
164
+ ? {}
165
+ : {
166
+ executionHeads: createFrozenRecord([
167
+ ...recordEntries(pendingState.allocation.executionHeads).filter(([key]) => key !== scope),
168
+ [scope, nextHead],
169
+ ]),
170
+ }),
171
+ ...(terminalLeaseProjection === undefined
172
+ ? {}
173
+ : {
174
+ leaseHeads: createFrozenRecord([
175
+ ...recordEntries(pendingState.allocation.leaseHeads).filter(([key]) => key !== scope),
176
+ [
177
+ scope,
178
+ Object.freeze({
179
+ ...terminalLeaseProjection,
180
+ status: "terminal",
181
+ }),
182
+ ],
183
+ ]),
184
+ }),
185
+ ...(terminalFence === undefined
186
+ ? {}
187
+ : {
188
+ assignmentFenceHeads: createFrozenRecord([
189
+ ...recordEntries(pendingState.allocation.assignmentFenceHeads).filter(([key]) => key !== terminalFence.assignmentFenceKey),
190
+ [
191
+ terminalFence.assignmentFenceKey,
192
+ Object.freeze({
193
+ ...terminalFence,
194
+ phase: "terminal",
195
+ }),
196
+ ],
197
+ ]),
198
+ }),
199
+ lastLogicalTime: recordedAt,
200
+ });
201
+ const sequence = pendingState.coordination.localEventSequence + 1;
202
+ const coordination = Object.freeze({
203
+ ...pendingState.coordination,
204
+ ...(terminalLeaseHead?.expiryTimerId === undefined
205
+ ? {}
206
+ : {
207
+ timers: createFrozenRecord(recordEntries(pendingState.coordination.timers).filter(([timerId]) => timerId !== terminalLeaseHead.expiryTimerId)),
208
+ }),
209
+ domainRecords: createFrozenRecord([
210
+ ...recordEntries(pendingState.coordination.domainRecords),
211
+ [
212
+ recordKey,
213
+ Object.freeze({
214
+ recordKey,
215
+ recordType: payload.type,
216
+ recordId,
217
+ contentDigest: digest(envelope),
218
+ messageId: envelope.messageId,
219
+ acceptedAt: recordedAt,
220
+ objectiveId: payload.objectiveId,
221
+ }),
222
+ ],
223
+ ]),
224
+ journal: Object.freeze([
225
+ ...pendingState.coordination.journal,
226
+ Object.freeze({
227
+ sequence,
228
+ occurredAt: recordedAt,
229
+ kind: direction === "local"
230
+ ? "command.accepted"
231
+ : "domain.accepted",
232
+ domainRecordKey: recordKey,
233
+ }),
234
+ ]),
235
+ localEventSequence: sequence,
236
+ lastLogicalTime: recordedAt,
237
+ });
238
+ const next = createMeshAllocationRuntimeState(coordination, Object.freeze({
239
+ ...pendingState.discovery,
240
+ lastLogicalTime: recordedAt,
241
+ }), Object.freeze({
242
+ ...pendingState.objectives,
243
+ lastLogicalTime: recordedAt,
244
+ }), allocation);
245
+ const effect = direction === "local"
246
+ ? Object.freeze({
247
+ kind: "allocation.execution.dispatch",
248
+ recordId,
249
+ recordType,
250
+ recipientPeerId: authority.ownerPeerId === state.allocation.identity.peerId
251
+ ? authority.assigneePeerId
252
+ : authority.ownerPeerId,
253
+ messageId: envelope.messageId,
254
+ envelope,
255
+ })
256
+ : undefined;
257
+ return Object.freeze({
258
+ accepted: true,
259
+ duplicate: false,
260
+ state: next,
261
+ effects: Object.freeze(effect === undefined ? [] : [effect]),
262
+ });
263
+ }
264
+ function withoutLeaseTimer(head) {
265
+ const { expiryTimerId: _expiryTimerId, expiryTimerGeneration: _expiryTimerGeneration, ...withoutTimer } = head;
266
+ return withoutTimer;
267
+ }
268
+ function resolveAuthority(state, payload) {
269
+ const localAward = state.allocation.localAwards[payload.awardId];
270
+ const accepted = state.allocation.assignmentResponses[payload.awardId];
271
+ const reservation = localAward && state.allocation.reservations[localAward.reservationId];
272
+ if (localAward &&
273
+ accepted?.kind === "work.accept" &&
274
+ reservation?.status === "committed" &&
275
+ localAward.status === "accepted")
276
+ return currentLeaseAuthority(state, Object.freeze({
277
+ objectiveId: localAward.objectiveId,
278
+ objectiveDocumentId: localAward.objectiveDocumentId,
279
+ objectiveRevision: localAward.objectiveRevision,
280
+ workItemId: localAward.work.workItemId,
281
+ workItemRevision: localAward.work.workItemRevision,
282
+ ownerPeerId: localAward.work.ownerPeerId,
283
+ ownerEpoch: localAward.work.ownerEpoch,
284
+ assigneePeerId: localAward.assigneePeerId,
285
+ awardId: localAward.awardId,
286
+ assignmentEpoch: localAward.assignmentEpoch,
287
+ assignmentAuthorityId: localAward.assignmentAuthorityId,
288
+ fencingToken: localAward.fencingToken,
289
+ acceptanceId: accepted.responseId,
290
+ acceptanceMessageId: accepted.envelope.messageId,
291
+ workDeadline: localAward.workDeadline,
292
+ workDeadlineAt: localAward.work.workDeadlineAt,
293
+ leaseExpiresAt: localAward.leaseExpiresAt,
294
+ leaseExpiresAtLogical: localAward.leaseExpiresAtLogical,
295
+ }), payload);
296
+ const localAuthority = state.allocation.assigneeAuthorities[payload.awardId];
297
+ const response = state.allocation.localAssignmentResponses[payload.awardId];
298
+ if (localAuthority && response?.kind === "work.accept")
299
+ return currentLeaseAuthority(state, Object.freeze({
300
+ objectiveId: localAuthority.objectiveId,
301
+ objectiveDocumentId: localAuthority.objectiveDocumentId,
302
+ objectiveRevision: localAuthority.objectiveRevision,
303
+ workItemId: localAuthority.workItemId,
304
+ workItemRevision: localAuthority.workItemRevision,
305
+ ownerPeerId: localAuthority.ownerPeerId,
306
+ ownerEpoch: localAuthority.ownerEpoch,
307
+ assigneePeerId: localAuthority.assigneePeerId,
308
+ awardId: localAuthority.awardId,
309
+ assignmentEpoch: localAuthority.assignmentEpoch,
310
+ assignmentAuthorityId: localAuthority.assignmentAuthorityId,
311
+ fencingToken: localAuthority.fencingToken,
312
+ acceptanceId: localAuthority.acceptanceId,
313
+ acceptanceMessageId: response.envelope.messageId,
314
+ workDeadline: localAuthority.workDeadline,
315
+ workDeadlineAt: localAuthority.workDeadlineAt,
316
+ leaseExpiresAt: localAuthority.leaseExpiresAt,
317
+ leaseExpiresAtLogical: localAuthority.leaseExpiresAtLogical,
318
+ }), payload);
319
+ const receivedAward = state.allocation.receivedAwards[payload.awardId];
320
+ const receivedOffer = receivedAward && state.allocation.receivedOffers[receivedAward.offerId];
321
+ if (receivedAward &&
322
+ receivedOffer &&
323
+ payload.type === "work.cancel" &&
324
+ payload.assignmentState === "award_pending" &&
325
+ receivedAward.status === "awaiting_response") {
326
+ const award = receivedAward.envelope.payload;
327
+ return Object.freeze({
328
+ objectiveId: award.objectiveId,
329
+ objectiveDocumentId: award.objectiveDocumentId,
330
+ objectiveRevision: award.objectiveRevision,
331
+ workItemId: award.workItemId,
332
+ workItemRevision: award.workItemRevision,
333
+ ownerPeerId: award.ownerPeerId,
334
+ ownerEpoch: award.ownerEpoch,
335
+ assigneePeerId: award.assigneePeerId,
336
+ awardId: award.awardId,
337
+ assignmentEpoch: award.assignmentEpoch,
338
+ assignmentAuthorityId: award.assignmentAuthorityId,
339
+ fencingToken: award.fencingToken,
340
+ acceptanceId: "pending",
341
+ acceptanceMessageId: receivedAward.envelope.messageId,
342
+ workDeadline: receivedOffer.envelope.payload.workDeadline,
343
+ workDeadlineAt: receivedOffer.workDeadlineAt,
344
+ leaseExpiresAt: award.leaseExpiresAt,
345
+ leaseExpiresAtLogical: receivedAward.leaseExpiresAtLogical,
346
+ });
347
+ }
348
+ if (localAward &&
349
+ payload.type === "work.cancel" &&
350
+ payload.assignmentState === "award_pending" &&
351
+ localAward.status === "awaiting_acceptance")
352
+ return Object.freeze({
353
+ objectiveId: localAward.objectiveId,
354
+ objectiveDocumentId: localAward.objectiveDocumentId,
355
+ objectiveRevision: localAward.objectiveRevision,
356
+ workItemId: localAward.work.workItemId,
357
+ workItemRevision: localAward.work.workItemRevision,
358
+ ownerPeerId: localAward.work.ownerPeerId,
359
+ ownerEpoch: localAward.work.ownerEpoch,
360
+ assigneePeerId: localAward.assigneePeerId,
361
+ awardId: localAward.awardId,
362
+ assignmentEpoch: localAward.assignmentEpoch,
363
+ assignmentAuthorityId: localAward.assignmentAuthorityId,
364
+ fencingToken: localAward.fencingToken,
365
+ acceptanceId: "pending",
366
+ acceptanceMessageId: localAward.recipientAward.messageId,
367
+ workDeadline: localAward.workDeadline,
368
+ workDeadlineAt: localAward.work.workDeadlineAt,
369
+ leaseExpiresAt: localAward.leaseExpiresAt,
370
+ leaseExpiresAtLogical: localAward.leaseExpiresAtLogical,
371
+ });
372
+ return undefined;
373
+ }
374
+ /** Resolves execution only through the current active lease projection. */
375
+ function currentLeaseAuthority(state, authority, payload) {
376
+ const head = state.allocation.leaseHeads[executionScopeKey(authority)];
377
+ const fence = head === undefined
378
+ ? undefined
379
+ : state.allocation.assignmentFenceHeads[meshAssignmentFenceKey(head)];
380
+ const currentExpiredOwnerCancellation = payload.type === "work.cancel" &&
381
+ payload.assignmentState === "active" &&
382
+ head?.status === "expired" &&
383
+ fence?.phase === "expired";
384
+ if (head === undefined ||
385
+ fence === undefined ||
386
+ (fence.phase !== "active" && !currentExpiredOwnerCancellation) ||
387
+ fence.assignmentEpoch !== head.assignmentEpoch ||
388
+ fence.assignmentAuthorityId !== head.assignmentAuthorityId ||
389
+ fence.fencingToken !== head.fencingToken ||
390
+ fence.assigneePeerId !== head.assigneePeerId ||
391
+ fence.activeAwardId !== head.awardId ||
392
+ head.acceptanceId !== authority.acceptanceId ||
393
+ head.acceptanceMessageId !== authority.acceptanceMessageId ||
394
+ head.assignmentAuthorityId !== authority.assignmentAuthorityId ||
395
+ head.fencingToken !== authority.fencingToken)
396
+ return undefined;
397
+ return Object.freeze({
398
+ ...authority,
399
+ leaseExpiresAt: head.currentLeaseExpiresAt,
400
+ leaseExpiresAtLogical: head.currentLeaseExpiresAtLogical,
401
+ });
402
+ }
403
+ function validTransition(state, prior, envelope, authority) {
404
+ const payload = envelope.payload;
405
+ const resumeCheckpoint = recoveryResumeCheckpoint(state, authority);
406
+ const resumeCheckpointId = resumeCheckpoint?.checkpointId;
407
+ if (prior && prior.phase !== "active")
408
+ return false;
409
+ if (payload.type === "work.progress")
410
+ return (envelope.causationId === authority.acceptanceMessageId &&
411
+ payload.progressSequence === (prior?.latestProgressSequence ?? 0) + 1 &&
412
+ (prior?.latestCheckpointId === undefined
413
+ ? payload.checkpointId === resumeCheckpointId
414
+ : payload.checkpointId === undefined ||
415
+ payload.checkpointId === prior.latestCheckpointId));
416
+ if (payload.type === "work.checkpoint")
417
+ return prior?.latestCheckpointId === undefined
418
+ ? payload.checkpointSequence ===
419
+ (resumeCheckpoint?.checkpointSequence ?? 0) + 1 &&
420
+ payload.previousCheckpointId === resumeCheckpointId &&
421
+ envelope.causationId === authority.acceptanceMessageId
422
+ : payload.checkpointSequence ===
423
+ (prior.latestCheckpointSequence ?? 0) + 1 &&
424
+ payload.previousCheckpointId === prior.latestCheckpointId &&
425
+ envelope.causationId ===
426
+ recordMessageId(state, prior.latestCheckpointId);
427
+ if (payload.type === "work.result")
428
+ return (payload.checkpointId ===
429
+ (prior?.latestCheckpointId ?? resumeCheckpointId) &&
430
+ envelope.causationId ===
431
+ (prior?.latestCheckpointId === undefined
432
+ ? authority.acceptanceMessageId
433
+ : recordMessageId(state, prior.latestCheckpointId)));
434
+ if (payload.type === "work.release")
435
+ return (envelope.causationId ===
436
+ currentLeaseCausationMessageId(state, authority) &&
437
+ payload.releaseDisposition === "close");
438
+ return (payload.assignmentState === "active" &&
439
+ envelope.causationId === currentLeaseCausationMessageId(state, authority));
440
+ }
441
+ function recoveryResumeCheckpoint(state, authority) {
442
+ const payload = state.allocation.localAwards[authority.awardId]?.recipientAward.envelope
443
+ .payload ??
444
+ state.allocation.receivedAwards[authority.awardId]?.envelope.payload;
445
+ const checkpointId = payload?.authorityKind === "recovery_certificate"
446
+ ? payload.resumeCheckpointId
447
+ : undefined;
448
+ if (checkpointId === undefined)
449
+ return undefined;
450
+ const record = state.allocation.executionRecords[checkpointId] ??
451
+ Object.values(state.allocation.witnessAssignments)
452
+ .map((witness) => witness.latestCheckpoint)
453
+ .find((checkpoint) => checkpoint?.recordId === checkpointId);
454
+ if (record?.envelope.payload.type !== "work.checkpoint" ||
455
+ record.recordId !== checkpointId)
456
+ throw new TypeError("Mesh recovery checkpoint evidence is missing");
457
+ return Object.freeze({
458
+ checkpointId,
459
+ checkpointSequence: record.envelope.payload.checkpointSequence,
460
+ });
461
+ }
462
+ function currentLeaseCausationMessageId(state, authority) {
463
+ const leaseHead = state.allocation.leaseHeads[executionScopeKey(authority)];
464
+ if (leaseHead?.latestLeaseRenewalId === undefined)
465
+ return authority.acceptanceMessageId;
466
+ const renewal = state.allocation.leaseRenewals[leaseHead.latestLeaseRenewalId];
467
+ if (renewal === undefined)
468
+ throw new TypeError("Mesh current lease renewal evidence is missing");
469
+ return renewal.envelope.messageId;
470
+ }
471
+ function requiresActiveLease(payload) {
472
+ return (payload.type === "work.progress" ||
473
+ payload.type === "work.checkpoint" ||
474
+ payload.type === "work.result" ||
475
+ (payload.type === "work.release" && payload.releaseAuthority === "assignee"));
476
+ }
477
+ function updateHead(prior, authority, payload, recordId, at) {
478
+ const head = Object.freeze({
479
+ executionScopeKey: executionScopeKey(authority),
480
+ ...authority,
481
+ phase: "active",
482
+ ...(prior?.latestProgressId === undefined
483
+ ? {}
484
+ : { latestProgressId: prior.latestProgressId }),
485
+ ...(prior?.latestProgressSequence === undefined
486
+ ? {}
487
+ : { latestProgressSequence: prior.latestProgressSequence }),
488
+ ...(prior?.latestCheckpointId === undefined
489
+ ? {}
490
+ : { latestCheckpointId: prior.latestCheckpointId }),
491
+ ...(prior?.latestCheckpointSequence === undefined
492
+ ? {}
493
+ : { latestCheckpointSequence: prior.latestCheckpointSequence }),
494
+ });
495
+ if (payload.type === "work.progress")
496
+ return Object.freeze({
497
+ ...head,
498
+ latestProgressId: recordId,
499
+ latestProgressSequence: payload.progressSequence,
500
+ });
501
+ if (payload.type === "work.checkpoint")
502
+ return Object.freeze({
503
+ ...head,
504
+ latestCheckpointId: recordId,
505
+ latestCheckpointSequence: payload.checkpointSequence,
506
+ });
507
+ if (payload.type === "work.result")
508
+ return Object.freeze({
509
+ ...head,
510
+ phase: "completed",
511
+ resultId: recordId,
512
+ terminalRecordId: recordId,
513
+ terminalAt: at,
514
+ });
515
+ return Object.freeze({
516
+ ...head,
517
+ phase: payload.type === "work.release" ? "released" : "cancelled",
518
+ terminalRecordId: recordId,
519
+ terminalAt: at,
520
+ });
521
+ }
522
+ function hasValidAuthorityFields(payload, authority) {
523
+ return (payload.objectiveId === authority.objectiveId &&
524
+ payload.objectiveDocumentId === authority.objectiveDocumentId &&
525
+ payload.objectiveRevision === authority.objectiveRevision &&
526
+ payload.workItemId === authority.workItemId &&
527
+ payload.workItemRevision === authority.workItemRevision &&
528
+ payload.ownerPeerId === authority.ownerPeerId &&
529
+ payload.ownerEpoch === authority.ownerEpoch &&
530
+ payload.assigneePeerId === authority.assigneePeerId &&
531
+ payload.awardId === authority.awardId &&
532
+ payload.assignmentEpoch === authority.assignmentEpoch &&
533
+ payload.assignmentAuthorityId === authority.assignmentAuthorityId &&
534
+ payload.fencingToken === authority.fencingToken &&
535
+ payload.leaseExpiresAt === authority.leaseExpiresAt &&
536
+ (payload.type !== "work.cancel" || payload.assignmentState === "active"
537
+ ? payload.acceptanceId === authority.acceptanceId
538
+ : true));
539
+ }
540
+ function hasValidAudienceAndRole(state, envelope, authority, direction) {
541
+ const sender = direction === "local"
542
+ ? state.allocation.identity.peerId
543
+ : envelope.sender.peerId;
544
+ const recipient = envelope.audience;
545
+ if (recipient.kind !== "peer" ||
546
+ (direction === "received" &&
547
+ recipient.peerId !== state.allocation.identity.peerId))
548
+ return false;
549
+ const payload = envelope.payload;
550
+ const expectedSender = payload.type === "work.release"
551
+ ? payload.releaseAuthority === "owner"
552
+ ? authority.ownerPeerId
553
+ : authority.assigneePeerId
554
+ : payload.type === "work.cancel"
555
+ ? authority.ownerPeerId
556
+ : authority.assigneePeerId;
557
+ const expectedRecipient = expectedSender === authority.ownerPeerId
558
+ ? authority.assigneePeerId
559
+ : authority.ownerPeerId;
560
+ return sender === expectedSender && recipient.peerId === expectedRecipient;
561
+ }
562
+ function cancelPendingValid(state, envelope, authority, direction) {
563
+ const payload = envelope.payload;
564
+ if (payload.type !== "work.cancel" ||
565
+ payload.assignmentState !== "award_pending")
566
+ return false;
567
+ const award = state.allocation.localAwards[payload.awardId];
568
+ const work = award &&
569
+ state.allocation.workAllocations[JSON.stringify([award.objectiveId, award.work.workItemId])];
570
+ const reservation = award && state.allocation.reservations[award.reservationId];
571
+ const received = state.allocation.receivedAwards[payload.awardId];
572
+ return Boolean(direction === "local"
573
+ ? award &&
574
+ work &&
575
+ reservation &&
576
+ award.status === "awaiting_acceptance" &&
577
+ work.phase === "award_pending" &&
578
+ work.activeAwardId === award.awardId &&
579
+ reservation.status === "reserved" &&
580
+ envelope.causationId === award.recipientAward.messageId &&
581
+ payload.assignmentAuthorityId === award.assignmentAuthorityId &&
582
+ authority.ownerPeerId === state.allocation.identity.peerId
583
+ : received &&
584
+ received.status === "awaiting_response" &&
585
+ envelope.causationId === received.envelope.messageId &&
586
+ hasValidAuthorityFields(payload, authority) &&
587
+ authority.assigneePeerId === state.allocation.identity.peerId);
588
+ }
589
+ function applyOperationalTransition(state, payload, at) {
590
+ const phase = payload.type === "work.result"
591
+ ? "completed"
592
+ : payload.type === "work.release"
593
+ ? "released"
594
+ : payload.type === "work.cancel"
595
+ ? "cancelled"
596
+ : undefined;
597
+ if (phase === undefined)
598
+ return state;
599
+ const key = JSON.stringify([payload.objectiveId, payload.workItemId]);
600
+ const allocationWork = state.allocation.workAllocations[key];
601
+ const objectiveWork = state.objectives.workItems[key];
602
+ if (allocationWork === undefined || objectiveWork === undefined)
603
+ return state;
604
+ if (allocationWork.phase !== "active" ||
605
+ allocationWork.activeAwardId !== payload.awardId ||
606
+ allocationWork.activeAcceptanceId !==
607
+ ("acceptanceId" in payload ? payload.acceptanceId : undefined) ||
608
+ objectiveWork.status !== "ready")
609
+ return state;
610
+ return Object.freeze({
611
+ ...state,
612
+ coordination: Object.freeze({
613
+ ...state.coordination,
614
+ timers: createFrozenRecord(recordEntries(state.coordination.timers).filter(([timerId]) => timerId !== objectiveWork.expiryTimerId)),
615
+ }),
616
+ objectives: Object.freeze({
617
+ ...state.objectives,
618
+ workItems: createFrozenRecord([
619
+ ...recordEntries(state.objectives.workItems).filter(([workKey]) => workKey !== key),
620
+ [
621
+ key,
622
+ Object.freeze({
623
+ ...objectiveWork,
624
+ status: phase,
625
+ expiryTimerId: undefined,
626
+ expiryTimerGeneration: undefined,
627
+ terminalAt: at,
628
+ updatedAt: at,
629
+ }),
630
+ ],
631
+ ]),
632
+ }),
633
+ allocation: Object.freeze({
634
+ ...state.allocation,
635
+ workAllocations: createFrozenRecord([
636
+ ...recordEntries(state.allocation.workAllocations).filter(([workKey]) => workKey !== key),
637
+ [
638
+ key,
639
+ Object.freeze({
640
+ ...allocationWork,
641
+ phase,
642
+ updatedAt: at,
643
+ }),
644
+ ],
645
+ ]),
646
+ }),
647
+ });
648
+ }
649
+ function cancelPending(state, payload, at, direction) {
650
+ if (direction === "received") {
651
+ const received = state.allocation.receivedAwards[payload.awardId];
652
+ if (!received)
653
+ return undefined;
654
+ return Object.freeze({
655
+ ...state,
656
+ coordination: Object.freeze({
657
+ ...state.coordination,
658
+ timers: createFrozenRecord(recordEntries(state.coordination.timers).filter(([key]) => key !== received.acceptanceDeadlineTimerId)),
659
+ }),
660
+ allocation: Object.freeze({
661
+ ...state.allocation,
662
+ receivedAwards: createFrozenRecord([
663
+ ...recordEntries(state.allocation.receivedAwards).filter(([key]) => key !== received.awardId),
664
+ [
665
+ received.awardId,
666
+ Object.freeze({ ...received, status: "cancelled" }),
667
+ ],
668
+ ]),
669
+ }),
670
+ });
671
+ }
672
+ const award = state.allocation.localAwards[payload.awardId];
673
+ if (!award)
674
+ return undefined;
675
+ const workKey = JSON.stringify([award.objectiveId, award.work.workItemId]);
676
+ const work = state.allocation.workAllocations[workKey];
677
+ const reservation = state.allocation.reservations[award.reservationId];
678
+ const objective = state.objectives.objectives[award.objectiveId];
679
+ if (!work || !reservation || !objective)
680
+ return undefined;
681
+ const coordination = Object.freeze({
682
+ ...state.coordination,
683
+ timers: createFrozenRecord(recordEntries(state.coordination.timers).filter(([key]) => key !== award.acceptanceDeadlineTimerId)),
684
+ });
685
+ const allocation = Object.freeze({
686
+ ...state.allocation,
687
+ workAllocations: createFrozenRecord([
688
+ ...recordEntries(state.allocation.workAllocations).filter(([key]) => key !== workKey),
689
+ [
690
+ workKey,
691
+ Object.freeze({
692
+ ...work,
693
+ phase: "ready",
694
+ activeAwardId: undefined,
695
+ reservationId: undefined,
696
+ updatedAt: at,
697
+ }),
698
+ ],
699
+ ]),
700
+ localAwards: createFrozenRecord([
701
+ ...recordEntries(state.allocation.localAwards).filter(([key]) => key !== award.awardId),
702
+ [
703
+ award.awardId,
704
+ Object.freeze({ ...award, status: "cancelled" }),
705
+ ],
706
+ ]),
707
+ reservations: createFrozenRecord([
708
+ ...recordEntries(state.allocation.reservations).filter(([key]) => key !== reservation.reservationId),
709
+ [
710
+ reservation.reservationId,
711
+ Object.freeze({
712
+ ...reservation,
713
+ status: "released",
714
+ releasedAt: at,
715
+ }),
716
+ ],
717
+ ]),
718
+ });
719
+ const objectives = Object.freeze({
720
+ ...state.objectives,
721
+ objectives: createFrozenRecord([
722
+ ...recordEntries(state.objectives.objectives).filter(([key]) => key !== objective.objectiveId),
723
+ [
724
+ objective.objectiveId,
725
+ Object.freeze({
726
+ ...objective,
727
+ reservedBudgetUnits: objective.reservedBudgetUnits - reservation.budgetReservationUnits,
728
+ }),
729
+ ],
730
+ ]),
731
+ });
732
+ return Object.freeze({
733
+ coordination,
734
+ discovery: state.discovery,
735
+ objectives,
736
+ allocation,
737
+ });
738
+ }
739
+ function executionScopeKey(authority) {
740
+ return JSON.stringify([
741
+ authority.objectiveId,
742
+ authority.objectiveRevision,
743
+ authority.workItemId,
744
+ authority.workItemRevision,
745
+ authority.ownerPeerId,
746
+ authority.ownerEpoch,
747
+ authority.awardId,
748
+ authority.assignmentEpoch,
749
+ ]);
750
+ }
751
+ function executionScopeForPayload(payload) {
752
+ return JSON.stringify([
753
+ payload.objectiveId,
754
+ payload.objectiveRevision,
755
+ payload.workItemId,
756
+ payload.workItemRevision,
757
+ payload.ownerPeerId,
758
+ payload.ownerEpoch,
759
+ payload.awardId,
760
+ payload.assignmentEpoch,
761
+ ]);
762
+ }
763
+ function recordMessageId(state, id) {
764
+ return id === undefined
765
+ ? undefined
766
+ : state.allocation.executionRecords[id]?.envelope.messageId;
767
+ }
768
+ function compare(left, right) {
769
+ const result = compareMeshTimestamps(left, right);
770
+ if (!result.ok)
771
+ throw new TypeError("Invalid execution timestamp");
772
+ return result.value;
773
+ }
774
+ function typeFor(payload) {
775
+ return payload.type.slice("work.".length);
776
+ }
777
+ function idFor(payload) {
778
+ switch (payload.type) {
779
+ case "work.progress":
780
+ return payload.progressId;
781
+ case "work.checkpoint":
782
+ return payload.checkpointId;
783
+ case "work.result":
784
+ return payload.resultId;
785
+ case "work.release":
786
+ return payload.releaseId;
787
+ case "work.cancel":
788
+ return payload.cancellationId;
789
+ }
790
+ }
791
+ function isExecutionPayload(payload) {
792
+ return Boolean(payload &&
793
+ typeof payload === "object" &&
794
+ [
795
+ "work.progress",
796
+ "work.checkpoint",
797
+ "work.result",
798
+ "work.release",
799
+ "work.cancel",
800
+ ].includes(payload.type));
801
+ }
802
+ function sameAuthority(head, authority) {
803
+ return (executionScopeKey(head) === executionScopeKey(authority) &&
804
+ head.fencingToken === authority.fencingToken &&
805
+ head.acceptanceId === authority.acceptanceId &&
806
+ head.acceptanceMessageId === authority.acceptanceMessageId &&
807
+ head.leaseExpiresAtLogical === authority.leaseExpiresAtLogical &&
808
+ head.workDeadlineAt === authority.workDeadlineAt);
809
+ }
810
+ function canonicalDigest(envelope) {
811
+ const canonical = canonicalizeMeshPayload(envelope.payload);
812
+ return (canonical.ok &&
813
+ envelope.payloadHash === `sha256:${sha256Base64Url(canonical.value)}`);
814
+ }
815
+ function digest(envelope) {
816
+ const canonical = canonicalizeMeshPayload(envelope.payload);
817
+ if (!canonical.ok)
818
+ throw new TypeError("Invalid execution payload");
819
+ return sha256Base64Url(canonical.value);
820
+ }
821
+ function contextCode(code) {
822
+ return [
823
+ "scope_mismatch",
824
+ "message_expired",
825
+ "message_from_future",
826
+ "unknown_critical_extension",
827
+ ].includes(code)
828
+ ? code
829
+ : "invalid_verified_envelope";
830
+ }
831
+ function sameData(left, right) {
832
+ return JSON.stringify(left) === JSON.stringify(right);
833
+ }
834
+ function acceptedDuplicate(state) {
835
+ return Object.freeze({
836
+ accepted: true,
837
+ duplicate: true,
838
+ state,
839
+ effects: Object.freeze([]),
840
+ });
841
+ }
842
+ function reject(state, code) {
843
+ return Object.freeze({ accepted: false, code, state });
844
+ }
845
+ function assertExecutionRuntime(state, time) {
846
+ createMeshAllocationRuntimeState(state.coordination, state.discovery, state.objectives, state.allocation);
847
+ assertMeshLogicalTime(time);
848
+ if (time < state.coordination.lastLogicalTime ||
849
+ time < state.discovery.lastLogicalTime ||
850
+ time < state.objectives.lastLogicalTime ||
851
+ time < state.allocation.lastLogicalTime)
852
+ throw new RangeError("Mesh execution logical time cannot move backwards");
853
+ }
854
+ //# sourceMappingURL=coordination-execution.js.map