@agentplat/mesh-sim 0.3.0-beta.2 → 0.3.0-beta.5

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 (185) hide show
  1. package/README.md +447 -2
  2. package/dist/collective-closed-loop-action.d.ts +80 -0
  3. package/dist/collective-closed-loop-action.d.ts.map +1 -0
  4. package/dist/collective-closed-loop-action.js +471 -0
  5. package/dist/collective-closed-loop-action.js.map +1 -0
  6. package/dist/collective-closed-loop-contracts.d.ts +75 -0
  7. package/dist/collective-closed-loop-contracts.d.ts.map +1 -0
  8. package/dist/collective-closed-loop-contracts.js +420 -0
  9. package/dist/collective-closed-loop-contracts.js.map +1 -0
  10. package/dist/collective-closed-loop-fault-matrix.d.ts +101 -0
  11. package/dist/collective-closed-loop-fault-matrix.d.ts.map +1 -0
  12. package/dist/collective-closed-loop-fault-matrix.js +253 -0
  13. package/dist/collective-closed-loop-fault-matrix.js.map +1 -0
  14. package/dist/collective-closed-loop-recovery.d.ts +70 -0
  15. package/dist/collective-closed-loop-recovery.d.ts.map +1 -0
  16. package/dist/collective-closed-loop-recovery.js +823 -0
  17. package/dist/collective-closed-loop-recovery.js.map +1 -0
  18. package/dist/collective-closed-loop-reference.d.ts +54 -0
  19. package/dist/collective-closed-loop-reference.d.ts.map +1 -0
  20. package/dist/collective-closed-loop-reference.js +1696 -0
  21. package/dist/collective-closed-loop-reference.js.map +1 -0
  22. package/dist/collective-closed-loop-replanning.d.ts +27 -0
  23. package/dist/collective-closed-loop-replanning.d.ts.map +1 -0
  24. package/dist/collective-closed-loop-replanning.js +151 -0
  25. package/dist/collective-closed-loop-replanning.js.map +1 -0
  26. package/dist/collective-closed-loop-resilience-campaign.d.ts +22 -0
  27. package/dist/collective-closed-loop-resilience-campaign.d.ts.map +1 -0
  28. package/dist/collective-closed-loop-resilience-campaign.js +196 -0
  29. package/dist/collective-closed-loop-resilience-campaign.js.map +1 -0
  30. package/dist/collective-closed-loop-resilience-contracts.d.ts +138 -0
  31. package/dist/collective-closed-loop-resilience-contracts.d.ts.map +1 -0
  32. package/dist/collective-closed-loop-resilience-contracts.js +677 -0
  33. package/dist/collective-closed-loop-resilience-contracts.js.map +1 -0
  34. package/dist/collective-closed-loop-runner.d.ts +133 -0
  35. package/dist/collective-closed-loop-runner.d.ts.map +1 -0
  36. package/dist/collective-closed-loop-runner.js +1429 -0
  37. package/dist/collective-closed-loop-runner.js.map +1 -0
  38. package/dist/collective-closed-loop-runtime.d.ts +95 -0
  39. package/dist/collective-closed-loop-runtime.d.ts.map +1 -0
  40. package/dist/collective-closed-loop-runtime.js +1192 -0
  41. package/dist/collective-closed-loop-runtime.js.map +1 -0
  42. package/dist/collective-effect-provenance.d.ts +5 -0
  43. package/dist/collective-effect-provenance.d.ts.map +1 -0
  44. package/dist/collective-effect-provenance.js +32 -0
  45. package/dist/collective-effect-provenance.js.map +1 -0
  46. package/dist/collective-environment.d.ts +49 -0
  47. package/dist/collective-environment.d.ts.map +1 -0
  48. package/dist/collective-environment.js +907 -0
  49. package/dist/collective-environment.js.map +1 -0
  50. package/dist/collective-progressive-scale-overlay.d.ts +39 -0
  51. package/dist/collective-progressive-scale-overlay.d.ts.map +1 -0
  52. package/dist/collective-progressive-scale-overlay.js +147 -0
  53. package/dist/collective-progressive-scale-overlay.js.map +1 -0
  54. package/dist/collective-progressive-scale.d.ts +202 -0
  55. package/dist/collective-progressive-scale.d.ts.map +1 -0
  56. package/dist/collective-progressive-scale.js +755 -0
  57. package/dist/collective-progressive-scale.js.map +1 -0
  58. package/dist/collective-statistical-campaign-aggregation.d.ts +97 -0
  59. package/dist/collective-statistical-campaign-aggregation.d.ts.map +1 -0
  60. package/dist/collective-statistical-campaign-aggregation.js +446 -0
  61. package/dist/collective-statistical-campaign-aggregation.js.map +1 -0
  62. package/dist/collective-statistical-campaign-artifact-stream.d.ts +66 -0
  63. package/dist/collective-statistical-campaign-artifact-stream.d.ts.map +1 -0
  64. package/dist/collective-statistical-campaign-artifact-stream.js +207 -0
  65. package/dist/collective-statistical-campaign-artifact-stream.js.map +1 -0
  66. package/dist/collective-statistical-campaign-bundle.d.ts +151 -0
  67. package/dist/collective-statistical-campaign-bundle.d.ts.map +1 -0
  68. package/dist/collective-statistical-campaign-bundle.js +885 -0
  69. package/dist/collective-statistical-campaign-bundle.js.map +1 -0
  70. package/dist/collective-statistical-campaign-executor.d.ts +131 -0
  71. package/dist/collective-statistical-campaign-executor.d.ts.map +1 -0
  72. package/dist/collective-statistical-campaign-executor.js +856 -0
  73. package/dist/collective-statistical-campaign-executor.js.map +1 -0
  74. package/dist/collective-statistical-campaign-normative-analysis.d.ts +85 -0
  75. package/dist/collective-statistical-campaign-normative-analysis.d.ts.map +1 -0
  76. package/dist/collective-statistical-campaign-normative-analysis.js +483 -0
  77. package/dist/collective-statistical-campaign-normative-analysis.js.map +1 -0
  78. package/dist/collective-statistical-campaign-normative-operation.d.ts +89 -0
  79. package/dist/collective-statistical-campaign-normative-operation.d.ts.map +1 -0
  80. package/dist/collective-statistical-campaign-normative-operation.js +266 -0
  81. package/dist/collective-statistical-campaign-normative-operation.js.map +1 -0
  82. package/dist/collective-statistical-campaign-normative-registry.d.ts +24 -0
  83. package/dist/collective-statistical-campaign-normative-registry.d.ts.map +1 -0
  84. package/dist/collective-statistical-campaign-normative-registry.js +163 -0
  85. package/dist/collective-statistical-campaign-normative-registry.js.map +1 -0
  86. package/dist/collective-statistical-campaign-registered-adapter.d.ts +16 -0
  87. package/dist/collective-statistical-campaign-registered-adapter.d.ts.map +1 -0
  88. package/dist/collective-statistical-campaign-registered-adapter.js +518 -0
  89. package/dist/collective-statistical-campaign-registered-adapter.js.map +1 -0
  90. package/dist/collective-statistical-campaign-scale.d.ts +85 -0
  91. package/dist/collective-statistical-campaign-scale.d.ts.map +1 -0
  92. package/dist/collective-statistical-campaign-scale.js +349 -0
  93. package/dist/collective-statistical-campaign-scale.js.map +1 -0
  94. package/dist/collective-trace-journal.d.ts +20 -0
  95. package/dist/collective-trace-journal.d.ts.map +1 -0
  96. package/dist/collective-trace-journal.js +105 -0
  97. package/dist/collective-trace-journal.js.map +1 -0
  98. package/dist/index.d.ts +25 -0
  99. package/dist/index.d.ts.map +1 -1
  100. package/dist/index.js +25 -0
  101. package/dist/index.js.map +1 -1
  102. package/dist/multi-domain-environment-contracts.d.ts +122 -0
  103. package/dist/multi-domain-environment-contracts.d.ts.map +1 -0
  104. package/dist/multi-domain-environment-contracts.js +14 -0
  105. package/dist/multi-domain-environment-contracts.js.map +1 -0
  106. package/dist/multi-domain-environment-runtime.d.ts +25 -0
  107. package/dist/multi-domain-environment-runtime.d.ts.map +1 -0
  108. package/dist/multi-domain-environment-runtime.js +493 -0
  109. package/dist/multi-domain-environment-runtime.js.map +1 -0
  110. package/dist/multi-domain-environment-validation.d.ts +33 -0
  111. package/dist/multi-domain-environment-validation.d.ts.map +1 -0
  112. package/dist/multi-domain-environment-validation.js +415 -0
  113. package/dist/multi-domain-environment-validation.js.map +1 -0
  114. package/dist/multi-domain-environment.d.ts +4 -0
  115. package/dist/multi-domain-environment.d.ts.map +1 -0
  116. package/dist/multi-domain-environment.js +4 -0
  117. package/dist/multi-domain-environment.js.map +1 -0
  118. package/dist/reducer-scenario.d.ts.map +1 -1
  119. package/dist/reducer-scenario.js +67 -19
  120. package/dist/reducer-scenario.js.map +1 -1
  121. package/dist/scalable-evaluation-contracts.d.ts +835 -0
  122. package/dist/scalable-evaluation-contracts.d.ts.map +1 -0
  123. package/dist/scalable-evaluation-contracts.js +22 -0
  124. package/dist/scalable-evaluation-contracts.js.map +1 -0
  125. package/dist/scalable-evaluation-durability.d.ts +16 -0
  126. package/dist/scalable-evaluation-durability.d.ts.map +1 -0
  127. package/dist/scalable-evaluation-durability.js +339 -0
  128. package/dist/scalable-evaluation-durability.js.map +1 -0
  129. package/dist/scalable-evaluation-evidence.d.ts +93 -0
  130. package/dist/scalable-evaluation-evidence.d.ts.map +1 -0
  131. package/dist/scalable-evaluation-evidence.js +470 -0
  132. package/dist/scalable-evaluation-evidence.js.map +1 -0
  133. package/dist/scalable-evaluation-reference-integrated.d.ts +87 -0
  134. package/dist/scalable-evaluation-reference-integrated.d.ts.map +1 -0
  135. package/dist/scalable-evaluation-reference-integrated.js +1318 -0
  136. package/dist/scalable-evaluation-reference-integrated.js.map +1 -0
  137. package/dist/scalable-evaluation-runner.d.ts +8 -0
  138. package/dist/scalable-evaluation-runner.d.ts.map +1 -0
  139. package/dist/scalable-evaluation-runner.js +1810 -0
  140. package/dist/scalable-evaluation-runner.js.map +1 -0
  141. package/dist/scalable-evaluation-runtime.d.ts +86 -0
  142. package/dist/scalable-evaluation-runtime.d.ts.map +1 -0
  143. package/dist/scalable-evaluation-runtime.js +1310 -0
  144. package/dist/scalable-evaluation-runtime.js.map +1 -0
  145. package/dist/scalable-evaluation-validation.d.ts +59 -0
  146. package/dist/scalable-evaluation-validation.d.ts.map +1 -0
  147. package/dist/scalable-evaluation-validation.js +377 -0
  148. package/dist/scalable-evaluation-validation.js.map +1 -0
  149. package/dist/scalable-evaluation.d.ts +8 -0
  150. package/dist/scalable-evaluation.d.ts.map +1 -0
  151. package/dist/scalable-evaluation.js +8 -0
  152. package/dist/scalable-evaluation.js.map +1 -0
  153. package/dist/sharded-simulation-contracts.d.ts +240 -0
  154. package/dist/sharded-simulation-contracts.d.ts.map +1 -0
  155. package/dist/sharded-simulation-contracts.js +199 -0
  156. package/dist/sharded-simulation-contracts.js.map +1 -0
  157. package/dist/sharded-simulation-runtime.d.ts +64 -0
  158. package/dist/sharded-simulation-runtime.d.ts.map +1 -0
  159. package/dist/sharded-simulation-runtime.js +755 -0
  160. package/dist/sharded-simulation-runtime.js.map +1 -0
  161. package/dist/sharded-simulation-validation.d.ts +10 -0
  162. package/dist/sharded-simulation-validation.d.ts.map +1 -0
  163. package/dist/sharded-simulation-validation.js +250 -0
  164. package/dist/sharded-simulation-validation.js.map +1 -0
  165. package/dist/sharded-simulation.d.ts +46 -0
  166. package/dist/sharded-simulation.d.ts.map +1 -0
  167. package/dist/sharded-simulation.js +32 -0
  168. package/dist/sharded-simulation.js.map +1 -0
  169. package/dist/verified-benchmark-contracts.d.ts +132 -0
  170. package/dist/verified-benchmark-contracts.d.ts.map +1 -0
  171. package/dist/verified-benchmark-contracts.js +2 -0
  172. package/dist/verified-benchmark-contracts.js.map +1 -0
  173. package/dist/verified-benchmark-runtime.d.ts +20 -0
  174. package/dist/verified-benchmark-runtime.d.ts.map +1 -0
  175. package/dist/verified-benchmark-runtime.js +199 -0
  176. package/dist/verified-benchmark-runtime.js.map +1 -0
  177. package/dist/verified-benchmark-validation.d.ts +64 -0
  178. package/dist/verified-benchmark-validation.d.ts.map +1 -0
  179. package/dist/verified-benchmark-validation.js +481 -0
  180. package/dist/verified-benchmark-validation.js.map +1 -0
  181. package/dist/verified-benchmark.d.ts +4 -0
  182. package/dist/verified-benchmark.d.ts.map +1 -0
  183. package/dist/verified-benchmark.js +4 -0
  184. package/dist/verified-benchmark.js.map +1 -0
  185. package/package.json +18 -8
@@ -0,0 +1,1192 @@
1
+ import { acceptDelegationMandateV1, createCollectiveAuthorityStateV1, } from '@agentplat/collective-control';
2
+ import { DELEGATION_MANDATE_REFERENCE_PREFIX_V1, createGovernedMeshObjectiveInboundProcessorV1, } from '@agentplat/collective-control/mesh';
3
+ import { createPlanningReducerCommandV1, createPlanningReducerStateV1, digestPlanningJsonV1, reducePlanningCommandV1, validateMissionIntentV1, validateMissionObservationV1, validatePlanFragmentProposalV1, validatePlanSelectionPolicyV1, } from '@agentplat/collective-planning';
4
+ import { validateCollectiveProtectedEffectAttemptV1, validateCollectiveProtectedEffectReceiptV1, } from '@agentplat/collective-planning/evaluation';
5
+ import { InMemoryPlanningFragmentRepositoryV1, PLANNING_MESH_CAPABILITY_PROFILE_V1, PLANNING_WORK_EXTENSION_KEY_V1, createPlanningAdaptiveRoleV1, createPlanningLocalWorkProjectionV1, planningWorkItemIdV1, selectPlanningOfferRecipientsV1, } from '@agentplat/collective-planning/mesh';
6
+ import { DEFAULT_MESH_CRYPTO_POLICY, createWebCryptoMeshEnvelopeSigner, verifyMeshEnvelope, } from '@agentplat/mesh-crypto';
7
+ import { canonicalizeMeshPayload, validateSignedMeshEnvelope, } from '@agentplat/mesh-protocol';
8
+ import { createMeshAllocationInboundProcessor, createMeshAllocationInboundRuntimeState, createMeshAllocationRuntimeState, createMeshAllocationState, createMeshCoordinationInboundState, createMeshCoordinationState, createMeshDiscoveryRuntimeState, createMeshDiscoveryState, createMeshObjectiveInboundProcessor, createMeshObjectiveInboundRuntimeState, createMeshObjectiveWorkRuntimeState, createMeshObjectiveWorkState, evaluateMeshAllocationCommand, evaluateMeshObjectiveWorkCommand, evaluateVerifiedMeshDiscoveryEnvelope, restoreMeshAllocationState, restoreMeshDiscoveryState, restoreMeshObjectiveWorkState, selectMeshAllocationBid, } from '@agentplat/mesh/coordination';
9
+ import { collectiveTraceJournalForOwnerV2 } from './collective-trace-journal.js';
10
+ import { assertCollectiveEffectReceiptProvenanceV1 } from './collective-effect-provenance.js';
11
+ const MINIMUM_PEERS = 3;
12
+ // Keep the runtime admission bound aligned with the public reference scenario
13
+ // and registered empirical campaign, which both support scales through 500.
14
+ const MAXIMUM_PEERS = 500;
15
+ const MAXIMUM_NEIGHBORS = MAXIMUM_PEERS - 1;
16
+ const MAXIMUM_RECIPIENTS = 32;
17
+ const MAXIMUM_INTERACTIONS = 5_000;
18
+ const WIRE_VERSION = 0;
19
+ /**
20
+ * Binds externally managed key handles to the closed-loop runtime. Reusing the
21
+ * returned object reuses exactly the same signing authority during replay.
22
+ */
23
+ export function createCollectiveClosedLoopRuntimeRunnerV1(input) {
24
+ const crypto = input.crypto ?? globalThis.crypto;
25
+ if (!crypto?.subtle)
26
+ throw new TypeError('closed_loop_crypto_unavailable');
27
+ return Object.freeze({
28
+ signer: input.signer ??
29
+ createWebCryptoMeshEnvelopeSigner({
30
+ signingPolicy: { allowedWireVersions: [WIRE_VERSION] },
31
+ }),
32
+ resolver: input.resolver,
33
+ cryptoPolicy: input.cryptoPolicy ?? DEFAULT_MESH_CRYPTO_POLICY,
34
+ crypto,
35
+ privateKeys: Object.freeze({ ...input.privateKeys }),
36
+ mandateVerification: Object.freeze({ ...input.mandateVerification }),
37
+ });
38
+ }
39
+ /**
40
+ * Executes the nominal peer-local planning and Mesh allocation spine. It stops
41
+ * after a real checkpoint while assignment authority remains active.
42
+ */
43
+ export async function runCollectiveClosedLoopMeshRuntimeV1(rawInput) {
44
+ const input = validateInput(rawInput);
45
+ const owner = input.peers[0];
46
+ const context = {
47
+ input,
48
+ owner,
49
+ peersById: new Map(input.peers.map((peer) => [peer.peerId, peer])),
50
+ sequences: new Map(),
51
+ baseWallTimeMs: Date.parse(input.missionIntent.validFrom),
52
+ logicalTimeMs: 1,
53
+ interactionCount: 0,
54
+ };
55
+ const authorityState = installMandate(input);
56
+ const planning = createPlanning(context);
57
+ let planningStates = planning.states;
58
+ const projection = planning.projection;
59
+ const repository = new InMemoryPlanningFragmentRepositoryV1();
60
+ await repository.put(projection.repositoryRecord);
61
+ const retainedProjection = await repository.get(projection.repositoryRecord.contentReference);
62
+ if (!retainedProjection ||
63
+ retainedProjection.fragmentDigest !==
64
+ projection.repositoryRecord.fragmentDigest)
65
+ throw new Error('closed_loop_planning_repository_mismatch');
66
+ let meshStates = Object.fromEntries(input.peers.map((peer) => [peer.peerId, createPeerMeshState(context, peer)]));
67
+ const allocationInbound = createMeshAllocationInboundProcessor({
68
+ resolver: input.runner.resolver,
69
+ cryptoPolicy: input.runner.cryptoPolicy,
70
+ crypto: input.runner.crypto,
71
+ supportedCriticalExtensions: [PLANNING_WORK_EXTENSION_KEY_V1],
72
+ });
73
+ const discoverablePeers = owner.neighborPeerIds
74
+ .map((peerId) => context.peersById.get(peerId))
75
+ .filter((peer) => peer !== undefined && peer.peerId !== owner.peerId);
76
+ for (const peer of discoverablePeers) {
77
+ meshStates[owner.peerId] = await discoverPeer(context, meshStates[owner.peerId], peer);
78
+ }
79
+ const discoveredPeerIds = Object.freeze(discoverablePeers.map((peer) => peer.peerId).sort());
80
+ const requiredCapabilityKeys = projection.work.requiredCapabilityKeys;
81
+ const recipients = selectPlanningOfferRecipientsV1({
82
+ discovery: meshStates[owner.peerId].discovery,
83
+ logicalTimeMs: context.logicalTimeMs,
84
+ verifiedAt: wallTime(context, 1_000),
85
+ localSupportedCriticalExtensions: [PLANNING_WORK_EXTENSION_KEY_V1],
86
+ requiredCapabilityKeys,
87
+ maximumRecipients: Math.min(MAXIMUM_RECIPIENTS, discoverablePeers.length),
88
+ });
89
+ if (recipients.length === 0)
90
+ throw new Error('closed_loop_no_eligible_recipients');
91
+ const objectiveEnvelope = await signEnvelope(context, {
92
+ senderPeerId: owner.peerId,
93
+ type: 'objective.announce',
94
+ audience: { kind: 'mesh', topic: 'objective' },
95
+ objectiveId: input.missionIntent.objective.objectiveId,
96
+ payload: objectivePayload(context, recipients.map((recipient) => recipient.peerId)),
97
+ sentAtOffsetMs: 2_000,
98
+ expiresAtOffsetMs: 120_000,
99
+ });
100
+ const objectiveInbound = createGovernedMeshObjectiveInboundProcessorV1({
101
+ processor: createMeshObjectiveInboundProcessor({
102
+ resolver: input.runner.resolver,
103
+ cryptoPolicy: input.runner.cryptoPolicy,
104
+ crypto: input.runner.crypto,
105
+ }),
106
+ authority: { read: () => authorityState },
107
+ });
108
+ // Only the owner and the selected offer recipients need this objective in
109
+ // the bounded planning round. Broadcasting it to every member of a
110
+ // 500-peer mesh is observationally irrelevant to the resulting allocation.
111
+ const objectiveRecipients = new Set([
112
+ owner.peerId,
113
+ ...recipients.map((recipient) => recipient.peerId),
114
+ ]);
115
+ for (const peer of input.peers) {
116
+ if (!objectiveRecipients.has(peer.peerId))
117
+ continue;
118
+ const receivedAt = advance(context);
119
+ const admitted = await objectiveInbound.process(createMeshObjectiveInboundRuntimeState(meshStates[peer.peerId].coordination, meshStates[peer.peerId].discovery, meshStates[peer.peerId].objectives, meshStates[peer.peerId].inbound), {
120
+ envelope: objectiveEnvelope.signed,
121
+ verifiedAt: wallTime(context, 3_000),
122
+ receivedAt,
123
+ });
124
+ countInteraction(context, objectiveEnvelope.signed, peer.peerId);
125
+ if (!admitted.accepted)
126
+ throw new Error(`closed_loop_objective_rejected:${admitted.code}`);
127
+ meshStates[peer.peerId] = replaceMeshDomains(meshStates[peer.peerId], admitted.state, receivedAt);
128
+ }
129
+ const workCreatedAt = advance(context);
130
+ const createdWork = evaluateMeshObjectiveWorkCommand(createMeshObjectiveWorkRuntimeState(meshStates[owner.peerId].coordination, meshStates[owner.peerId].discovery, meshStates[owner.peerId].objectives), { kind: 'work.create', input: projection.work }, { verifiedAt: wallTime(context, 4_000), receivedAt: workCreatedAt });
131
+ if (!createdWork.accepted)
132
+ throw new Error(`closed_loop_work_rejected:${createdWork.code}`);
133
+ meshStates[owner.peerId] = replaceMeshDomains(meshStates[owner.peerId], createdWork.state, workCreatedAt);
134
+ const offerId = scopedId(context, 'offer', 1);
135
+ const offerPayload = createOfferPayload(context, projection, offerId);
136
+ const preparedOffers = await Promise.all(recipients.map(async (recipient) => ({
137
+ recipientPeerId: recipient.peerId,
138
+ preparedAt: context.logicalTimeMs + 1,
139
+ envelope: (await signEnvelope(context, {
140
+ senderPeerId: owner.peerId,
141
+ type: 'work.offer',
142
+ audience: { kind: 'peer', peerId: recipient.peerId },
143
+ objectiveId: input.missionIntent.objective.objectiveId,
144
+ payload: offerPayload,
145
+ extensions: projection.extensions,
146
+ criticalExtensions: projection.criticalExtensions,
147
+ sentAtOffsetMs: 5_000,
148
+ expiresAtOffsetMs: 60_000,
149
+ })).signed,
150
+ })));
151
+ const offeredAt = advance(context);
152
+ const offered = evaluateMeshAllocationCommand(allocationRuntime(meshStates[owner.peerId]), {
153
+ kind: 'allocation.offer',
154
+ objectiveId: input.missionIntent.objective.objectiveId,
155
+ workItemId: projection.workItemId,
156
+ expectedWorkItemRevision: projection.workItemRevision,
157
+ recipients: preparedOffers.map((prepared) => ({
158
+ ...prepared,
159
+ preparedAt: offeredAt,
160
+ })),
161
+ }, wallTime(context, 5_000), offeredAt, [PLANNING_WORK_EXTENSION_KEY_V1], recipients.map((recipient) => recipient.peerId));
162
+ if (!offered.accepted)
163
+ throw new Error(`closed_loop_offer_rejected:${offered.code}`);
164
+ meshStates[owner.peerId] = retainInbound(meshStates[owner.peerId], offered.state);
165
+ for (const prepared of preparedOffers) {
166
+ const admitted = await allocationInbound.process(meshStates[prepared.recipientPeerId], {
167
+ envelope: prepared.envelope,
168
+ verifiedAt: wallTime(context, 5_000),
169
+ receivedAt: offeredAt,
170
+ });
171
+ countInteraction(context, prepared.envelope, prepared.recipientPeerId);
172
+ if (!admitted.accepted)
173
+ throw new Error(`closed_loop_planning_offer_rejected:${admitted.code}`);
174
+ meshStates[prepared.recipientPeerId] = admitted.state;
175
+ }
176
+ const offersByPeer = new Map(preparedOffers.map((prepared) => [prepared.recipientPeerId, prepared]));
177
+ const bidsById = new Map();
178
+ for (const [index, recipient] of recipients.entries()) {
179
+ const peerId = recipient.peerId;
180
+ const preparedOffer = offersByPeer.get(peerId);
181
+ if (!preparedOffer)
182
+ throw new Error('closed_loop_offer_state_missing');
183
+ const bidId = scopedId(context, `bid-${peerId}`, 1);
184
+ const bidEnvelope = await signEnvelope(context, {
185
+ senderPeerId: peerId,
186
+ type: 'work.bid',
187
+ audience: { kind: 'peer', peerId: owner.peerId },
188
+ objectiveId: input.missionIntent.objective.objectiveId,
189
+ causationId: preparedOffer.envelope.messageId,
190
+ payload: {
191
+ type: 'work.bid',
192
+ bidId,
193
+ bidRevision: 1,
194
+ offerId,
195
+ objectiveId: input.missionIntent.objective.objectiveId,
196
+ objectiveDocumentId: input.missionIntent.objective.objectiveDocumentId,
197
+ objectiveRevision: input.missionIntent.objective.objectiveRevision,
198
+ workItemId: projection.workItemId,
199
+ workItemRevision: projection.workItemRevision,
200
+ ownerPeerId: owner.peerId,
201
+ ownerEpoch: 1,
202
+ offerAttempt: 1,
203
+ bidderPeerId: peerId,
204
+ advertisementId: capabilityAdvertisementId(peerId, requiredCapabilityKeys[0]),
205
+ capabilityId: capabilityId(peerId, requiredCapabilityKeys[0]),
206
+ capabilityRevision: 1,
207
+ capacityReservationUnits: 1,
208
+ budgetUnits: 1,
209
+ bidDeadline: offerPayload.bidDeadline,
210
+ workDeadline: offerPayload.workDeadline,
211
+ expectedCompletionAt: wallTime(context, 600_000 + index * 1_000),
212
+ bidExpiresAt: wallTime(context, 50_000),
213
+ assumptions: [],
214
+ },
215
+ sentAtOffsetMs: 6_000,
216
+ expiresAtOffsetMs: 50_000,
217
+ });
218
+ const preparedAt = advance(context);
219
+ const localBid = evaluateMeshAllocationCommand(allocationRuntime(meshStates[peerId]), {
220
+ kind: 'allocation.bid',
221
+ offerId,
222
+ preparedAt,
223
+ envelope: bidEnvelope.signed,
224
+ }, wallTime(context, 7_000), preparedAt);
225
+ if (!localBid.accepted)
226
+ throw new Error(`closed_loop_local_bid_rejected:${localBid.code}`);
227
+ meshStates[peerId] = retainInbound(meshStates[peerId], localBid.state);
228
+ const receivedAt = advance(context);
229
+ const receivedBid = await allocationInbound.process(meshStates[owner.peerId], {
230
+ envelope: bidEnvelope.signed,
231
+ verifiedAt: wallTime(context, 7_000),
232
+ receivedAt,
233
+ });
234
+ countInteraction(context, bidEnvelope.signed, owner.peerId);
235
+ if (!receivedBid.accepted)
236
+ throw new Error(`closed_loop_bid_rejected:${receivedBid.code}`);
237
+ meshStates[owner.peerId] = receivedBid.state;
238
+ bidsById.set(bidId, bidEnvelope.signed);
239
+ }
240
+ const selected = selectMeshAllocationBid(allocationRuntime(meshStates[owner.peerId]), { offerId, evaluatedAt: advance(context) });
241
+ if (selected.reason !== 'selected' || !selected.bid)
242
+ throw new Error(`closed_loop_bid_not_selected:${selected.reason}`);
243
+ const winnerPeerId = selected.bid.bidderPeerId;
244
+ const offerRecipientPeerIds = Object.freeze(recipients.map((recipient) => recipient.peerId).sort());
245
+ const bidderPeerIds = Object.freeze([...offerRecipientPeerIds]);
246
+ const winningBidEnvelope = bidsById.get(selected.bid.bidId);
247
+ if (!winningBidEnvelope)
248
+ throw new Error('closed_loop_selected_bid_evidence_missing');
249
+ const awardId = scopedId(context, 'award', 1);
250
+ const leaseExpiresAt = wallTime(context, 1_800_000);
251
+ const acceptanceDeadline = wallTime(context, 35_000);
252
+ const awardEnvelope = await signEnvelope(context, {
253
+ senderPeerId: owner.peerId,
254
+ type: 'work.award',
255
+ audience: { kind: 'peer', peerId: winnerPeerId },
256
+ objectiveId: input.missionIntent.objective.objectiveId,
257
+ causationId: winningBidEnvelope.messageId,
258
+ payload: {
259
+ type: 'work.award',
260
+ awardId,
261
+ offerId,
262
+ bidId: selected.bid.bidId,
263
+ bidRevision: selected.bid.bidRevision,
264
+ objectiveId: input.missionIntent.objective.objectiveId,
265
+ objectiveDocumentId: input.missionIntent.objective.objectiveDocumentId,
266
+ objectiveRevision: input.missionIntent.objective.objectiveRevision,
267
+ workItemId: projection.workItemId,
268
+ workItemRevision: projection.workItemRevision,
269
+ ownerPeerId: owner.peerId,
270
+ ownerEpoch: 1,
271
+ offerAttempt: 1,
272
+ assigneePeerId: winnerPeerId,
273
+ assignmentEpoch: 1,
274
+ authorityKind: 'award',
275
+ assignmentAuthorityId: awardId,
276
+ fencingToken: awardId,
277
+ budgetReservationUnits: offerPayload.budgetReservationUnits,
278
+ workDeadline: offerPayload.workDeadline,
279
+ leaseStartsAt: wallTime(context, 10_000),
280
+ leaseExpiresAt,
281
+ acceptanceDeadline,
282
+ },
283
+ sentAtOffsetMs: 10_000,
284
+ expiresAtOffsetMs: 35_000,
285
+ });
286
+ const awardedAt = advance(context);
287
+ const awarded = evaluateMeshAllocationCommand(allocationRuntime(meshStates[owner.peerId]), {
288
+ kind: 'allocation.award',
289
+ offerId,
290
+ bidId: selected.bid.bidId,
291
+ bidRevision: selected.bid.bidRevision,
292
+ recipient: {
293
+ recipientPeerId: winnerPeerId,
294
+ preparedAt: awardedAt,
295
+ envelope: awardEnvelope.signed,
296
+ },
297
+ }, wallTime(context, 10_000), awardedAt);
298
+ if (!awarded.accepted)
299
+ throw new Error(`closed_loop_award_rejected:${awarded.code}`);
300
+ meshStates[owner.peerId] = retainInbound(meshStates[owner.peerId], awarded.state);
301
+ const receivedAward = await allocationInbound.process(meshStates[winnerPeerId], {
302
+ envelope: awardEnvelope.signed,
303
+ verifiedAt: wallTime(context, 10_000),
304
+ receivedAt: awardedAt,
305
+ });
306
+ countInteraction(context, awardEnvelope.signed, winnerPeerId);
307
+ if (!receivedAward.accepted)
308
+ throw new Error(`closed_loop_received_award_rejected:${receivedAward.code}`);
309
+ meshStates[winnerPeerId] = receivedAward.state;
310
+ const acceptanceId = scopedId(context, 'acceptance', 1);
311
+ const acceptanceEnvelope = await signEnvelope(context, {
312
+ senderPeerId: winnerPeerId,
313
+ type: 'work.accept',
314
+ audience: { kind: 'peer', peerId: owner.peerId },
315
+ objectiveId: input.missionIntent.objective.objectiveId,
316
+ causationId: awardEnvelope.signed.messageId,
317
+ payload: {
318
+ type: 'work.accept',
319
+ acceptanceId,
320
+ awardId,
321
+ objectiveId: input.missionIntent.objective.objectiveId,
322
+ objectiveDocumentId: input.missionIntent.objective.objectiveDocumentId,
323
+ objectiveRevision: input.missionIntent.objective.objectiveRevision,
324
+ workItemId: projection.workItemId,
325
+ workItemRevision: projection.workItemRevision,
326
+ ownerPeerId: owner.peerId,
327
+ ownerEpoch: 1,
328
+ assigneePeerId: winnerPeerId,
329
+ assignmentEpoch: 1,
330
+ assignmentAuthorityId: awardId,
331
+ fencingToken: awardId,
332
+ acceptanceDeadline,
333
+ },
334
+ sentAtOffsetMs: 11_000,
335
+ expiresAtOffsetMs: 35_000,
336
+ });
337
+ const acceptedAt = advance(context);
338
+ const locallyAccepted = evaluateMeshAllocationCommand(allocationRuntime(meshStates[winnerPeerId]), {
339
+ kind: 'allocation.assignment_response',
340
+ awardId,
341
+ preparedAt: acceptedAt,
342
+ envelope: acceptanceEnvelope.signed,
343
+ }, wallTime(context, 11_000), acceptedAt);
344
+ if (!locallyAccepted.accepted)
345
+ throw new Error(`closed_loop_local_acceptance_rejected:${locallyAccepted.code}`);
346
+ meshStates[winnerPeerId] = retainInbound(meshStates[winnerPeerId], locallyAccepted.state);
347
+ const ownerAccepted = await allocationInbound.process(meshStates[owner.peerId], {
348
+ envelope: acceptanceEnvelope.signed,
349
+ verifiedAt: wallTime(context, 11_000),
350
+ receivedAt: acceptedAt,
351
+ });
352
+ countInteraction(context, acceptanceEnvelope.signed, owner.peerId);
353
+ if (!ownerAccepted.accepted)
354
+ throw new Error(`closed_loop_acceptance_rejected:${ownerAccepted.code}`);
355
+ meshStates[owner.peerId] = ownerAccepted.state;
356
+ const checkpointId = scopedId(context, 'checkpoint', 1);
357
+ const checkpointEnvelope = await signEnvelope(context, {
358
+ senderPeerId: winnerPeerId,
359
+ type: 'work.checkpoint',
360
+ audience: { kind: 'peer', peerId: owner.peerId },
361
+ objectiveId: input.missionIntent.objective.objectiveId,
362
+ causationId: acceptanceEnvelope.signed.messageId,
363
+ payload: {
364
+ type: 'work.checkpoint',
365
+ checkpointId,
366
+ checkpointSequence: 1,
367
+ objectiveId: input.missionIntent.objective.objectiveId,
368
+ objectiveDocumentId: input.missionIntent.objective.objectiveDocumentId,
369
+ objectiveRevision: input.missionIntent.objective.objectiveRevision,
370
+ workItemId: projection.workItemId,
371
+ workItemRevision: projection.workItemRevision,
372
+ ownerPeerId: owner.peerId,
373
+ ownerEpoch: 1,
374
+ assigneePeerId: winnerPeerId,
375
+ awardId,
376
+ acceptanceId,
377
+ assignmentEpoch: 1,
378
+ assignmentAuthorityId: awardId,
379
+ fencingToken: awardId,
380
+ leaseExpiresAt,
381
+ checkpointDigest: await contentDigest(input.runner.crypto, `${scopedId(context, 'checkpoint-content', 1)}:${projection.workItemId}`),
382
+ checkpointSummary: 'The governed effect boundary is ready.',
383
+ },
384
+ sentAtOffsetMs: 12_000,
385
+ expiresAtOffsetMs: 60_000,
386
+ });
387
+ const checkpointedAt = advance(context);
388
+ const locallyCheckpointed = evaluateMeshAllocationCommand(allocationRuntime(meshStates[winnerPeerId]), {
389
+ kind: 'allocation.execution',
390
+ preparedAt: checkpointedAt,
391
+ envelope: checkpointEnvelope.signed,
392
+ }, wallTime(context, 12_000), checkpointedAt);
393
+ if (!locallyCheckpointed.accepted)
394
+ throw new Error(`closed_loop_local_checkpoint_rejected:${locallyCheckpointed.code}`);
395
+ meshStates[winnerPeerId] = retainInbound(meshStates[winnerPeerId], locallyCheckpointed.state);
396
+ const ownerCheckpointed = await allocationInbound.process(meshStates[owner.peerId], {
397
+ envelope: checkpointEnvelope.signed,
398
+ verifiedAt: wallTime(context, 12_000),
399
+ receivedAt: checkpointedAt,
400
+ });
401
+ countInteraction(context, checkpointEnvelope.signed, owner.peerId);
402
+ if (!ownerCheckpointed.accepted)
403
+ throw new Error(`closed_loop_checkpoint_rejected:${ownerCheckpointed.code}`);
404
+ meshStates[owner.peerId] = ownerCheckpointed.state;
405
+ const authorities = currentAuthorities(meshStates, owner.peerId, winnerPeerId, input.missionIntent.objective.objectiveId, projection.workItemId, checkpointId);
406
+ const winnerPlanning = planningStates[owner.peerId];
407
+ const winnerHeadDigest = winnerPlanning.planView.selectedHeads.find((head) => head.semanticSlotKey === planning.proposal.semanticSlotKey)?.fragmentDigest;
408
+ const winnerFragment = winnerPlanning.planView.fragments.find((fragment) => fragment.fragmentDigest === winnerHeadDigest);
409
+ if (!winnerFragment)
410
+ throw new Error('closed_loop_winner_fragment_missing');
411
+ const winnerWorkMapping = winnerPlanning.planView.workMappings.find((mapping) => mapping.fragmentDigest === winnerFragment.fragmentDigest);
412
+ if (!winnerWorkMapping)
413
+ throw new Error('closed_loop_winner_work_mapping_missing');
414
+ const adaptiveRole = createPlanningAdaptiveRoleV1({
415
+ source: {
416
+ workContractId: scopedId(context, 'work-contract', 1),
417
+ identity: identity(context, winnerPeerId),
418
+ objective: authorities.objective,
419
+ workItem: authorities.workItem,
420
+ execution: authorities.execution,
421
+ fenceHead: authorities.fenceHead,
422
+ mandate: input.mandate,
423
+ roleKey: winnerFragment.roleKey,
424
+ trustPolicyId: scopedId(context, 'trust-policy', 1),
425
+ inferencePolicyId: scopedId(context, 'inference-policy', 1),
426
+ maximumActionBudgetUnits: input.mandate.statement.budget.maximumActionBudgetUnits,
427
+ createdAtLogicalMs: checkpointedAt,
428
+ },
429
+ missionIntent: input.missionIntent,
430
+ planView: winnerPlanning.planView,
431
+ fragment: winnerFragment,
432
+ repositoryRecord: projection.repositoryRecord,
433
+ extension: projection.extension,
434
+ roleBindingId: scopedId(context, 'role-binding', 1),
435
+ targetStatus: 'assigned',
436
+ });
437
+ planningStates = {
438
+ ...planningStates,
439
+ [owner.peerId]: applyPlanning(winnerPlanning, {
440
+ schemaVersion: 1,
441
+ kind: 'fragment.assignment.observe',
442
+ expectedStateDigest: null,
443
+ fragmentId: winnerFragment.fragmentId,
444
+ previousFragmentDigest: winnerFragment.fragmentDigest,
445
+ expectedWorkMapping: winnerWorkMapping,
446
+ roleBinding: adaptiveRole.roleBinding,
447
+ }),
448
+ };
449
+ const immutablePlanningStates = Object.freeze({ ...planningStates });
450
+ const immutableMeshStates = Object.freeze({ ...meshStates });
451
+ let finalization;
452
+ let finalizationBinding;
453
+ const handle = Object.freeze({
454
+ schemaVersion: 1,
455
+ winnerPeerId,
456
+ discoveredPeerIds,
457
+ offerRecipientPeerIds,
458
+ bidderPeerIds,
459
+ workContract: adaptiveRole.workContract,
460
+ adaptiveRole,
461
+ roleBinding: adaptiveRole.roleBinding,
462
+ authorityState,
463
+ ...authorities,
464
+ planningStates: immutablePlanningStates,
465
+ meshStates: immutableMeshStates,
466
+ logicalTimeMs: checkpointedAt,
467
+ interactionCount: context.interactionCount,
468
+ async finalizeAfterCommittedEffect(finalizeInput) {
469
+ const attempt = validateCollectiveProtectedEffectAttemptV1(finalizeInput.effectAttempt);
470
+ const rawReceipt = finalizeInput.effectReceipt;
471
+ const receipt = validateCollectiveProtectedEffectReceiptV1(rawReceipt);
472
+ assertEffectBoundToWork(attempt, receipt, adaptiveRole.workContract);
473
+ assertCollectiveEffectReceiptProvenanceV1(handle, attempt, rawReceipt);
474
+ if (receipt.status !== 'committed' ||
475
+ receipt.outputDigest !== finalizeInput.resultDigest)
476
+ throw new Error('closed_loop_effect_not_committed');
477
+ const binding = `${attempt.attemptDigest}:${receipt.receiptDigest}:${finalizeInput.resultDigest}:${finalizeInput.resultSummary}`;
478
+ if (finalization) {
479
+ if (binding !== finalizationBinding)
480
+ throw new Error('closed_loop_finalization_conflict');
481
+ return finalization;
482
+ }
483
+ finalizationBinding = binding;
484
+ finalization = finalizeMeshResult({
485
+ context,
486
+ ownerPeerId: owner.peerId,
487
+ winnerPeerId,
488
+ discoveredPeerIds,
489
+ offerRecipientPeerIds,
490
+ bidderPeerIds,
491
+ workContract: adaptiveRole.workContract,
492
+ roleBinding: adaptiveRole.roleBinding,
493
+ checkpoint: authorities.checkpoint,
494
+ checkpointId,
495
+ awardId,
496
+ acceptanceId,
497
+ leaseExpiresAt,
498
+ resultDigest: finalizeInput.resultDigest,
499
+ resultSummary: finalizeInput.resultSummary,
500
+ planningStates: immutablePlanningStates,
501
+ meshStates: immutableMeshStates,
502
+ allocationInbound,
503
+ });
504
+ return finalization;
505
+ },
506
+ });
507
+ return handle;
508
+ }
509
+ function validateInput(input) {
510
+ assertExactDataRecord(input, [
511
+ 'schemaVersion',
512
+ 'missionIntent',
513
+ 'selectionPolicy',
514
+ 'mandate',
515
+ 'peers',
516
+ 'observations',
517
+ 'planningProposal',
518
+ 'planningMode',
519
+ 'runner',
520
+ 'seed',
521
+ 'maximumLogicalTimeMs',
522
+ ], 'closed_loop_runtime_input');
523
+ if (input.schemaVersion !== 1)
524
+ throw new TypeError('closed_loop_schema_version_invalid');
525
+ const missionIntent = validateMissionIntentV1(input.missionIntent);
526
+ const selectionPolicy = validatePlanSelectionPolicyV1(input.selectionPolicy);
527
+ const observations = input.observations.map((observation) => validateMissionObservationV1(observation));
528
+ const planningProposal = validatePlanFragmentProposalV1(input.planningProposal);
529
+ if (input.planningMode !== 'adaptive_collective' &&
530
+ input.planningMode !== 'centralized_planner')
531
+ throw new TypeError('closed_loop_planning_mode_invalid');
532
+ if (missionIntent.selectionPolicyDigest !== selectionPolicy.policyDigest)
533
+ throw new TypeError('closed_loop_selection_policy_mismatch');
534
+ if (input.peers.length < MINIMUM_PEERS ||
535
+ input.peers.length > MAXIMUM_PEERS)
536
+ throw new RangeError('closed_loop_peer_count_invalid');
537
+ if (!Number.isSafeInteger(input.seed) || input.seed < 0)
538
+ throw new TypeError('closed_loop_seed_invalid');
539
+ if (!Number.isSafeInteger(input.maximumLogicalTimeMs) ||
540
+ input.maximumLogicalTimeMs < 1)
541
+ throw new RangeError('closed_loop_logical_time_limit_invalid');
542
+ const peerIds = new Set();
543
+ for (const peer of input.peers) {
544
+ if (peer.schemaVersion !== 1 ||
545
+ peer.peerId.length === 0 ||
546
+ peer.peerInstanceId.length === 0 ||
547
+ peerIds.has(peer.peerId) ||
548
+ new Set(peer.capabilityKeys).size !== peer.capabilityKeys.length ||
549
+ new Set(peer.neighborPeerIds).size !== peer.neighborPeerIds.length ||
550
+ peer.neighborPeerIds.length > MAXIMUM_NEIGHBORS ||
551
+ peer.neighborPeerIds.includes(peer.peerId) ||
552
+ !input.runner.privateKeys[peer.peerId])
553
+ throw new TypeError('closed_loop_peer_invalid');
554
+ peerIds.add(peer.peerId);
555
+ }
556
+ for (const peer of input.peers)
557
+ if (peer.neighborPeerIds.some((peerId) => !peerIds.has(peerId)))
558
+ throw new TypeError('closed_loop_neighbor_invalid');
559
+ const owner = input.peers[0];
560
+ const peersById = new Map(input.peers.map((peer) => [peer.peerId, peer]));
561
+ for (const observation of observations) {
562
+ const observer = peersById.get(observation.observerPeerId);
563
+ if (!observer ||
564
+ observer.peerInstanceId !== observation.observerInstanceId ||
565
+ observation.missionIntentId !== missionIntent.missionIntentId ||
566
+ observation.intentRevision !== missionIntent.revision ||
567
+ observation.intentDigest !== missionIntent.intentDigest)
568
+ throw new TypeError('closed_loop_observation_binding_invalid');
569
+ }
570
+ const eligibleObservationDigests = new Set(observations
571
+ .filter((observation) => input.planningMode === 'centralized_planner' ||
572
+ (observation.observerPeerId === owner.peerId &&
573
+ observation.observerInstanceId === owner.peerInstanceId))
574
+ .map((observation) => observation.observationDigest));
575
+ if (planningProposal.proposerPeerId !== owner.peerId ||
576
+ planningProposal.proposerInstanceId !== owner.peerInstanceId ||
577
+ planningProposal.missionIntentId !== missionIntent.missionIntentId ||
578
+ planningProposal.intentRevision !== missionIntent.revision ||
579
+ planningProposal.intentDigest !== missionIntent.intentDigest ||
580
+ planningProposal.basisObservationDigests.length === 0 ||
581
+ planningProposal.basisObservationDigests.some((digest) => !eligibleObservationDigests.has(digest)))
582
+ throw new TypeError('closed_loop_planning_proposal_binding_invalid');
583
+ if (input.mandate.statement.tenantId !== missionIntent.tenantId ||
584
+ input.mandate.statement.policyDomainId !== missionIntent.policyDomainId ||
585
+ input.mandate.statement.issuerId !== owner.peerId ||
586
+ input.mandate.statement.objective.meshId !== missionIntent.objective.meshId ||
587
+ input.mandate.statement.objective.objectiveId !==
588
+ missionIntent.objective.objectiveId ||
589
+ input.mandate.statement.objective.objectiveDocumentId !==
590
+ missionIntent.objective.objectiveDocumentId ||
591
+ input.peers.some((peer) => !input.mandate.statement.subjectPeerIds.includes(peer.peerId)) ||
592
+ input.runner.mandateVerification.signedDigest !== input.mandate.mandateDigest ||
593
+ input.runner.mandateVerification.issuerId !==
594
+ input.mandate.statement.issuerId)
595
+ throw new TypeError('closed_loop_authority_binding_invalid');
596
+ const requiredCapability = missionIntent.permittedCapabilityKeys[0];
597
+ if (!requiredCapability ||
598
+ owner.neighborPeerIds.every((peerId) => !input.peers
599
+ .find((peer) => peer.peerId === peerId)
600
+ ?.capabilityKeys.includes(requiredCapability)))
601
+ throw new TypeError('closed_loop_capability_topology_invalid');
602
+ return Object.freeze({
603
+ ...input,
604
+ missionIntent,
605
+ selectionPolicy,
606
+ observations: Object.freeze(observations),
607
+ planningProposal,
608
+ peers: Object.freeze(input.peers.map((peer) => Object.freeze({ ...peer }))),
609
+ });
610
+ }
611
+ function installMandate(input) {
612
+ const decision = acceptDelegationMandateV1(createCollectiveAuthorityStateV1({
613
+ tenantId: input.missionIntent.tenantId,
614
+ policyDomainId: input.missionIntent.policyDomainId,
615
+ }), {
616
+ mandate: input.mandate,
617
+ verification: input.runner.mandateVerification,
618
+ acceptedAtLogicalMs: 1,
619
+ });
620
+ if (!decision.accepted)
621
+ throw new Error(`closed_loop_mandate_rejected:${decision.code}`);
622
+ return decision.state;
623
+ }
624
+ function createPlanning(context) {
625
+ const { input, owner } = context;
626
+ const admittedSubjects = input.peers.map((peer) => ({
627
+ schemaVersion: 1,
628
+ peerId: peer.peerId,
629
+ peerInstanceId: peer.peerInstanceId,
630
+ }));
631
+ const proposal = input.planningProposal;
632
+ const states = {};
633
+ for (const peer of input.peers) {
634
+ let state = createPlanningReducerStateV1({
635
+ schemaVersion: 1,
636
+ peerId: peer.peerId,
637
+ peerInstanceId: peer.peerInstanceId,
638
+ missionIntent: input.missionIntent,
639
+ selectionPolicy: input.selectionPolicy,
640
+ admittedSubjects,
641
+ logicalTimeMs: 1,
642
+ });
643
+ const proposalBasis = new Set(proposal.basisObservationDigests);
644
+ const localObservations = input.planningMode === 'centralized_planner' &&
645
+ peer.peerId === owner.peerId
646
+ ? input.observations.filter((observation) => proposalBasis.has(observation.observationDigest))
647
+ : input.observations.filter((observation) => observation.observerPeerId === peer.peerId &&
648
+ observation.observerInstanceId === peer.peerInstanceId);
649
+ for (const observation of localObservations)
650
+ state = applyPlanning(state, {
651
+ schemaVersion: 1,
652
+ kind: 'observation.record',
653
+ expectedStateDigest: null,
654
+ observation,
655
+ });
656
+ states[peer.peerId] = state;
657
+ }
658
+ let ownerState = applyPlanning(states[owner.peerId], {
659
+ schemaVersion: 1,
660
+ kind: 'proposal.record',
661
+ expectedStateDigest: null,
662
+ proposal,
663
+ });
664
+ ownerState = applyPlanning(ownerState, {
665
+ schemaVersion: 1,
666
+ kind: 'slot.evaluate',
667
+ expectedStateDigest: null,
668
+ semanticSlotKey: proposal.semanticSlotKey,
669
+ candidateProposalDigests: [proposal.proposalDigest],
670
+ decidedAtLogicalMs: 1,
671
+ });
672
+ const activeFragment = ownerState.planView.fragments.find((fragment) => fragment.status === 'active');
673
+ if (!activeFragment)
674
+ throw new Error('closed_loop_active_fragment_missing');
675
+ const workItemId = planningWorkItemIdV1(proposal.proposalDigest);
676
+ ownerState = applyPlanning(ownerState, {
677
+ schemaVersion: 1,
678
+ kind: 'fragment.project-to-work',
679
+ expectedStateDigest: null,
680
+ fragmentId: activeFragment.fragmentId,
681
+ previousFragmentDigest: activeFragment.fragmentDigest,
682
+ workTarget: {
683
+ schemaVersion: 1,
684
+ meshId: input.missionIntent.objective.meshId,
685
+ objectiveId: input.missionIntent.objective.objectiveId,
686
+ workItemId,
687
+ workItemRevision: 1,
688
+ },
689
+ transitionedAtLogicalMs: 1,
690
+ });
691
+ states[owner.peerId] = ownerState;
692
+ const offeredFragment = ownerState.planView.fragments.find((fragment) => fragment.fragmentId === activeFragment.fragmentId &&
693
+ fragment.status === 'offered');
694
+ if (!offeredFragment)
695
+ throw new Error('closed_loop_offered_fragment_missing');
696
+ return {
697
+ states,
698
+ proposal,
699
+ projection: createPlanningLocalWorkProjectionV1({
700
+ missionIntent: input.missionIntent,
701
+ sourcePlanView: ownerState.planView,
702
+ fragment: offeredFragment,
703
+ }),
704
+ };
705
+ }
706
+ function applyPlanning(state, input) {
707
+ const decision = reducePlanningCommandV1(state, createPlanningReducerCommandV1(input));
708
+ if (decision.status !== 'applied')
709
+ throw new Error(`closed_loop_planning_rejected:${decision.error?.message}`);
710
+ return decision.state;
711
+ }
712
+ function createPeerMeshState(context, peer) {
713
+ const local = identity(context, peer.peerId);
714
+ const admittedPeerIds = unique([
715
+ peer.peerId,
716
+ context.owner.peerId,
717
+ ...peer.neighborPeerIds,
718
+ ]);
719
+ return createMeshAllocationInboundRuntimeState(createMeshCoordinationState({ identity: local }), createMeshDiscoveryState({
720
+ identity: local,
721
+ subscriptions: ['membership', 'capability', 'objective'],
722
+ admittedPeers: admittedPeerIds.map((peerId) => ({
723
+ peerId,
724
+ instanceIds: [context.peersById.get(peerId)?.peerInstanceId ?? 'missing'],
725
+ validUntil: wallTime(context, 86_400_000),
726
+ })),
727
+ }), createMeshObjectiveWorkState({
728
+ identity: local,
729
+ issuerAuthorities: [
730
+ {
731
+ peerId: context.owner.peerId,
732
+ keyIds: [keyId(context.owner.peerId)],
733
+ validUntil: wallTime(context, 86_400_000),
734
+ },
735
+ ],
736
+ }), createMeshAllocationState({ identity: local }), createMeshCoordinationInboundState({ identity: local }));
737
+ }
738
+ async function discoverPeer(context, ownerState, peer) {
739
+ const capabilityIds = [
740
+ planningCapabilityId(peer.peerId),
741
+ ...peer.capabilityKeys.map((capability) => capabilityId(peer.peerId, capability)),
742
+ ].sort();
743
+ const card = await signEnvelope(context, {
744
+ senderPeerId: peer.peerId,
745
+ type: 'peer.card',
746
+ audience: { kind: 'mesh', topic: 'membership' },
747
+ payload: {
748
+ type: 'peer.card',
749
+ peerCardId: scopedId(context, `card-${peer.peerId}`, 1),
750
+ cardRevision: 1,
751
+ subjectPeerId: peer.peerId,
752
+ instanceId: peer.peerInstanceId,
753
+ protocolVersions: [WIRE_VERSION],
754
+ transportHints: [`https://${peer.peerId}.invalid/mesh`],
755
+ capabilityIds,
756
+ validFrom: wallTime(context, 0),
757
+ validUntil: wallTime(context, 120_000),
758
+ },
759
+ sentAtOffsetMs: 0,
760
+ expiresAtOffsetMs: 120_000,
761
+ });
762
+ ownerState = admitDiscovery(context, ownerState, card.verified, []);
763
+ const planning = await signEnvelope(context, {
764
+ senderPeerId: peer.peerId,
765
+ type: 'capability.advertise',
766
+ audience: { kind: 'mesh', topic: 'capability' },
767
+ payload: {
768
+ type: 'capability.advertise',
769
+ advertisementId: planningAdvertisementId(peer.peerId),
770
+ capabilityId: planningCapabilityId(peer.peerId),
771
+ capabilityRevision: 1,
772
+ ownerPeerId: peer.peerId,
773
+ capabilityKey: PLANNING_MESH_CAPABILITY_PROFILE_V1.capabilityKey,
774
+ version: PLANNING_MESH_CAPABILITY_PROFILE_V1.version,
775
+ variant: PLANNING_MESH_CAPABILITY_PROFILE_V1.variant,
776
+ inputMediaTypes: PLANNING_MESH_CAPABILITY_PROFILE_V1.inputMediaTypes,
777
+ outputMediaTypes: PLANNING_MESH_CAPABILITY_PROFILE_V1.outputMediaTypes,
778
+ attributes: PLANNING_MESH_CAPABILITY_PROFILE_V1.attributes,
779
+ validFrom: wallTime(context, 0),
780
+ validUntil: wallTime(context, 120_000),
781
+ maximumConcurrency: 1,
782
+ maximumPayloadBytes: 262_144,
783
+ },
784
+ sentAtOffsetMs: 0,
785
+ expiresAtOffsetMs: 120_000,
786
+ });
787
+ ownerState = admitDiscovery(context, ownerState, planning.verified, [
788
+ PLANNING_WORK_EXTENSION_KEY_V1,
789
+ ]);
790
+ for (const capabilityKey of peer.capabilityKeys) {
791
+ const capability = await signEnvelope(context, {
792
+ senderPeerId: peer.peerId,
793
+ type: 'capability.advertise',
794
+ audience: { kind: 'mesh', topic: 'capability' },
795
+ payload: {
796
+ type: 'capability.advertise',
797
+ advertisementId: capabilityAdvertisementId(peer.peerId, capabilityKey),
798
+ capabilityId: capabilityId(peer.peerId, capabilityKey),
799
+ capabilityRevision: 1,
800
+ ownerPeerId: peer.peerId,
801
+ capabilityKey,
802
+ version: '1',
803
+ inputMediaTypes: ['application/json'],
804
+ outputMediaTypes: ['application/json'],
805
+ attributes: {},
806
+ validFrom: wallTime(context, 0),
807
+ validUntil: wallTime(context, 120_000),
808
+ maximumConcurrency: 1,
809
+ maximumPayloadBytes: 262_144,
810
+ },
811
+ sentAtOffsetMs: 0,
812
+ expiresAtOffsetMs: 120_000,
813
+ });
814
+ ownerState = admitDiscovery(context, ownerState, capability.verified, []);
815
+ }
816
+ return ownerState;
817
+ }
818
+ function admitDiscovery(context, state, envelope, supportedCriticalExtensions) {
819
+ const receivedAt = advance(context);
820
+ const decision = evaluateVerifiedMeshDiscoveryEnvelope(createMeshDiscoveryRuntimeState(state.coordination, state.discovery), {
821
+ envelope,
822
+ verifiedAt: wallTime(context, 1_000),
823
+ receivedAt,
824
+ ...(supportedCriticalExtensions.length === 0
825
+ ? {}
826
+ : { supportedCriticalExtensions }),
827
+ });
828
+ countInteraction(context, envelope, context.owner.peerId);
829
+ if (!decision.accepted)
830
+ throw new Error(`closed_loop_discovery_rejected:${decision.code}`);
831
+ return replaceMeshDomains(state, {
832
+ coordination: decision.state.coordination,
833
+ discovery: decision.state.discovery,
834
+ objectives: state.objectives,
835
+ }, receivedAt);
836
+ }
837
+ function objectivePayload(context, offerRecipientPeerIds) {
838
+ const { missionIntent, mandate } = context.input;
839
+ // Recovery can only award a replacement that has both a received offer and
840
+ // its own bid. Bind the witness pool to that exact offer cohort, rather than
841
+ // to unrelated topology positions. The cohort is bounded by the recipient
842
+ // selection limit (32), which is also the protocol witness ceiling.
843
+ // Three offer recipients provide a two-witness quorum even when the
844
+ // original assignee is one of them, while keeping recovery fanout small and
845
+ // comfortably inside the protocol recipient limit.
846
+ const witnesses = [...new Set(offerRecipientPeerIds)].sort().slice(0, 3);
847
+ // The public objective contract requires at least three witnesses even for
848
+ // the minimum three-peer reference topology, where only two are in-mesh.
849
+ if (witnesses.length < 3)
850
+ witnesses.push(`witness:${context.input.seed}:external`);
851
+ witnesses.sort();
852
+ return {
853
+ type: 'objective.announce',
854
+ objectiveDocumentId: missionIntent.objective.objectiveDocumentId,
855
+ objectiveId: missionIntent.objective.objectiveId,
856
+ objectiveRevision: missionIntent.objective.objectiveRevision,
857
+ issuerPeerId: context.owner.peerId,
858
+ successCriteria: [...missionIntent.outcomeStatements],
859
+ permittedCapabilityKeys: [...missionIntent.permittedCapabilityKeys],
860
+ maximumWorkItems: Math.min(4, mandate.statement.budget.maximumConcurrentWorkReservations),
861
+ maximumConcurrentAssignments: Math.min(2, mandate.statement.budget.maximumConcurrentWorkReservations),
862
+ maximumBudgetUnits: mandate.statement.budget.totalBudgetUnits,
863
+ bidWindowMs: 60_000,
864
+ acceptanceWindowMs: 30_000,
865
+ maximumLeaseDurationMs: 3_600_000,
866
+ recoveryGraceMs: 60_000,
867
+ maximumLeaseRenewals: 3,
868
+ recoveryWitnessPeerIds: witnesses,
869
+ recoveryWitnessThreshold: Math.min(2, witnesses.length - 1),
870
+ contentReference: `${DELEGATION_MANDATE_REFERENCE_PREFIX_V1}${mandate.mandateDigest}`,
871
+ validFrom: missionIntent.validFrom,
872
+ validUntil: earlierTimestamp(missionIntent.validUntil, mandate.statement.validUntil),
873
+ };
874
+ }
875
+ function createOfferPayload(context, projection, offerId) {
876
+ const objective = context.input.missionIntent.objective;
877
+ if (projection.work.inputReference === undefined)
878
+ throw new Error('closed_loop_work_input_reference_missing');
879
+ return {
880
+ type: 'work.offer',
881
+ offerId,
882
+ objectiveId: objective.objectiveId,
883
+ objectiveDocumentId: objective.objectiveDocumentId,
884
+ objectiveRevision: objective.objectiveRevision,
885
+ workItemId: projection.workItemId,
886
+ workItemRevision: projection.workItemRevision,
887
+ ownerPeerId: context.owner.peerId,
888
+ ownerEpoch: 1,
889
+ offerAttempt: 1,
890
+ requiredCapabilityKeys: projection.work.requiredCapabilityKeys,
891
+ matchingAttributes: projection.work.matchingAttributes ?? {},
892
+ inputReference: projection.work.inputReference,
893
+ completionCriteria: projection.work.completionCriteria,
894
+ budgetReservationUnits: projection.work.budgetReservationUnits,
895
+ bidDeadline: wallTime(context, 60_000),
896
+ workDeadline: projection.work.workDeadline,
897
+ };
898
+ }
899
+ function currentAuthorities(meshStates, ownerPeerId, winnerPeerId, objectiveId, workItemId, checkpointId) {
900
+ const objective = meshStates[ownerPeerId].objectives.objectives[objectiveId];
901
+ const workItem = Object.values(meshStates[ownerPeerId].objectives.workItems).find((candidate) => candidate.workItemId === workItemId);
902
+ const execution = Object.values(meshStates[winnerPeerId].allocation.executionHeads).find((candidate) => candidate.workItemId === workItemId && candidate.phase === 'active');
903
+ const fenceHead = Object.values(meshStates[winnerPeerId].allocation.assignmentFenceHeads).find((candidate) => candidate.workItemId === workItemId && candidate.phase === 'active');
904
+ const checkpoint = meshStates[winnerPeerId].allocation.executionRecords[checkpointId];
905
+ if (!objective || !workItem || !execution || !fenceHead || !checkpoint)
906
+ throw new Error('closed_loop_authority_head_missing');
907
+ return { objective, workItem, execution, fenceHead, checkpoint };
908
+ }
909
+ async function finalizeMeshResult(input) {
910
+ const { context, workContract } = input;
911
+ const resultId = scopedId(context, 'result', 1);
912
+ const resultEnvelope = await signEnvelope(context, {
913
+ senderPeerId: input.winnerPeerId,
914
+ type: 'work.result',
915
+ audience: { kind: 'peer', peerId: input.ownerPeerId },
916
+ objectiveId: workContract.objective.objectiveId,
917
+ causationId: input.checkpoint.envelope.messageId,
918
+ payload: {
919
+ type: 'work.result',
920
+ resultId,
921
+ resultDigest: meshContentDigest(input.resultDigest),
922
+ objectiveId: workContract.objective.objectiveId,
923
+ objectiveDocumentId: workContract.objective.objectiveDocumentId,
924
+ objectiveRevision: workContract.objective.objectiveRevision,
925
+ workItemId: workContract.assignment.workItemId,
926
+ workItemRevision: workContract.assignment.workItemRevision,
927
+ ownerPeerId: workContract.assignment.ownerPeerId,
928
+ ownerEpoch: 1,
929
+ assigneePeerId: input.winnerPeerId,
930
+ awardId: input.awardId,
931
+ acceptanceId: input.acceptanceId,
932
+ assignmentEpoch: workContract.assignment.assignmentEpoch,
933
+ assignmentAuthorityId: workContract.assignment.assignmentAuthorityId,
934
+ fencingToken: workContract.assignment.fencingToken,
935
+ leaseExpiresAt: input.leaseExpiresAt,
936
+ checkpointId: input.checkpointId,
937
+ resultSummary: input.resultSummary,
938
+ },
939
+ sentAtOffsetMs: 14_000,
940
+ expiresAtOffsetMs: 90_000,
941
+ });
942
+ const finalizedAt = advance(context);
943
+ const local = evaluateMeshAllocationCommand(allocationRuntime(input.meshStates[input.winnerPeerId]), {
944
+ kind: 'allocation.execution',
945
+ preparedAt: finalizedAt,
946
+ envelope: resultEnvelope.signed,
947
+ }, wallTime(context, 14_000), finalizedAt);
948
+ if (!local.accepted)
949
+ throw new Error(`closed_loop_local_result_rejected:${local.code}`);
950
+ const nextMeshStates = {
951
+ ...input.meshStates,
952
+ [input.winnerPeerId]: retainInbound(input.meshStates[input.winnerPeerId], local.state),
953
+ };
954
+ const owner = await input.allocationInbound.process(nextMeshStates[input.ownerPeerId], {
955
+ envelope: resultEnvelope.signed,
956
+ verifiedAt: wallTime(context, 14_000),
957
+ receivedAt: finalizedAt,
958
+ });
959
+ countInteraction(context, resultEnvelope.signed, input.ownerPeerId);
960
+ if (!owner.accepted)
961
+ throw new Error(`closed_loop_result_rejected:${owner.code}`);
962
+ nextMeshStates[input.ownerPeerId] = owner.state;
963
+ const result = nextMeshStates[input.winnerPeerId].allocation.executionRecords[resultId];
964
+ if (!result || result.recordType !== 'result')
965
+ throw new Error('closed_loop_result_evidence_missing');
966
+ return Object.freeze({
967
+ schemaVersion: 1,
968
+ winnerPeerId: input.winnerPeerId,
969
+ discoveredPeerIds: input.discoveredPeerIds,
970
+ offerRecipientPeerIds: input.offerRecipientPeerIds,
971
+ bidderPeerIds: input.bidderPeerIds,
972
+ workContract,
973
+ roleBinding: input.roleBinding,
974
+ result,
975
+ planningStates: input.planningStates,
976
+ meshStates: Object.freeze(nextMeshStates),
977
+ logicalTimeMs: finalizedAt,
978
+ interactionCount: context.interactionCount,
979
+ });
980
+ }
981
+ function assertEffectBoundToWork(attempt, receipt, workContract) {
982
+ if (receipt.attemptDigest !== attempt.attemptDigest ||
983
+ receipt.idempotencyKey !== attempt.idempotencyKey ||
984
+ attempt.tenantId !== workContract.tenantId ||
985
+ attempt.peerId !== workContract.assignment.assignedPeerId ||
986
+ attempt.peerInstanceId !== workContract.assignment.assignedInstanceId ||
987
+ attempt.workItemId !== workContract.assignment.workItemId ||
988
+ attempt.workItemRevision !== workContract.assignment.workItemRevision ||
989
+ attempt.workContractId !== workContract.workContractId ||
990
+ attempt.workContractDigest !== workContract.workContractDigest ||
991
+ attempt.assignmentEpoch !== workContract.assignment.assignmentEpoch ||
992
+ attempt.authorityGeneration !== workContract.assignment.authorityGeneration ||
993
+ attempt.fencingToken !== workContract.assignment.fencingToken)
994
+ throw new Error('closed_loop_effect_binding_mismatch');
995
+ }
996
+ async function signEnvelope(context, input) {
997
+ const peer = context.peersById.get(input.senderPeerId);
998
+ const privateKey = context.input.runner.privateKeys[input.senderPeerId];
999
+ if (!peer || !privateKey)
1000
+ throw new Error('closed_loop_signing_identity_missing');
1001
+ const sequence = (context.sequences.get(peer.peerId) ?? 0) + 1;
1002
+ context.sequences.set(peer.peerId, sequence);
1003
+ const payloadHash = await payloadDigest(context.input.runner.crypto, input.payload);
1004
+ const envelope = {
1005
+ protocol: 'agentplat.mesh',
1006
+ wireVersion: WIRE_VERSION,
1007
+ messageId: deterministicMessageId(context.input.seed, context.input.peers.findIndex((candidate) => candidate.peerId === peer.peerId), sequence),
1008
+ tenantId: context.input.missionIntent.tenantId,
1009
+ meshId: context.input.missionIntent.objective.meshId,
1010
+ ...(input.objectiveId === undefined
1011
+ ? {}
1012
+ : { objectiveId: input.objectiveId }),
1013
+ type: input.type,
1014
+ sender: { peerId: peer.peerId, instanceId: peer.peerInstanceId },
1015
+ audience: input.audience,
1016
+ sequence,
1017
+ sentAt: wallTime(context, input.sentAtOffsetMs ?? 0),
1018
+ expiresAt: wallTime(context, input.expiresAtOffsetMs ?? 120_000),
1019
+ ...(input.causationId === undefined
1020
+ ? {}
1021
+ : { causationId: input.causationId }),
1022
+ ...(input.extensions === undefined ? {} : { extensions: input.extensions }),
1023
+ ...(input.criticalExtensions === undefined
1024
+ ? {}
1025
+ : { criticalExtensions: input.criticalExtensions }),
1026
+ payloadHash,
1027
+ payload: input.payload,
1028
+ proof: { algorithm: 'Ed25519', keyId: keyId(peer.peerId), value: '' },
1029
+ };
1030
+ const preflight = validateSignedMeshEnvelope({
1031
+ ...envelope,
1032
+ payloadHash: `sha256:${'A'.repeat(43)}`,
1033
+ proof: { ...envelope.proof, value: 'A'.repeat(86) },
1034
+ }, { acceptedWireVersions: [WIRE_VERSION] });
1035
+ if (!preflight.ok)
1036
+ throw new Error(`closed_loop_envelope_invalid:${input.type}:${preflight.issues
1037
+ .map((issue) => `${issue.code}:${issue.path}`)
1038
+ .join(',')}`);
1039
+ const signed = await context.input.runner.signer.sign({ envelope, privateKey });
1040
+ const verified = await verifyMeshEnvelope({
1041
+ envelope: signed,
1042
+ resolver: context.input.runner.resolver,
1043
+ policy: context.input.runner.cryptoPolicy,
1044
+ verifiedAt: wallTime(context, Math.max(input.sentAtOffsetMs ?? 0, 1_000)),
1045
+ crypto: context.input.runner.crypto,
1046
+ });
1047
+ if (!verified.verified)
1048
+ throw new Error(`closed_loop_signature_rejected:${verified.code}`);
1049
+ return Object.freeze({ signed, verified: verified.envelope });
1050
+ }
1051
+ function replaceMeshDomains(state, domains, logicalTimeMs) {
1052
+ return createMeshAllocationInboundRuntimeState(domains.coordination, restoreMeshDiscoveryState({
1053
+ ...domains.discovery,
1054
+ lastLogicalTime: logicalTimeMs,
1055
+ }), restoreMeshObjectiveWorkState({
1056
+ ...domains.objectives,
1057
+ lastLogicalTime: logicalTimeMs,
1058
+ }), restoreMeshAllocationState({
1059
+ ...state.allocation,
1060
+ lastLogicalTime: logicalTimeMs,
1061
+ }), state.inbound);
1062
+ }
1063
+ function retainInbound(state, domains) {
1064
+ return createMeshAllocationInboundRuntimeState(domains.coordination, domains.discovery, domains.objectives, domains.allocation, state.inbound);
1065
+ }
1066
+ function allocationRuntime(state) {
1067
+ return createMeshAllocationRuntimeState(state.coordination, state.discovery, state.objectives, state.allocation);
1068
+ }
1069
+ function identity(context, peerId) {
1070
+ const peer = context.peersById.get(peerId);
1071
+ if (!peer)
1072
+ throw new Error('closed_loop_peer_missing');
1073
+ return Object.freeze({
1074
+ tenantId: context.input.missionIntent.tenantId,
1075
+ meshId: context.input.missionIntent.objective.meshId,
1076
+ peerId,
1077
+ instanceId: peer.peerInstanceId,
1078
+ keyId: keyId(peerId),
1079
+ });
1080
+ }
1081
+ function advance(context) {
1082
+ context.logicalTimeMs += 1;
1083
+ if (context.logicalTimeMs > context.input.maximumLogicalTimeMs)
1084
+ throw new RangeError('closed_loop_logical_time_exceeded');
1085
+ return context.logicalTimeMs;
1086
+ }
1087
+ function countInteraction(context, record, peerId) {
1088
+ context.interactionCount += 1;
1089
+ if (context.interactionCount > MAXIMUM_INTERACTIONS)
1090
+ throw new RangeError('closed_loop_interaction_limit_exceeded');
1091
+ const journal = collectiveTraceJournalForOwnerV2(context.input.runner);
1092
+ if (!journal)
1093
+ return;
1094
+ journal.append({
1095
+ logicalTimeMs: context.logicalTimeMs,
1096
+ peerId,
1097
+ component: 'mesh',
1098
+ kind: 'mesh.message.delivered',
1099
+ status: 'accepted',
1100
+ reasonCode: null,
1101
+ recordDigest: digestPlanningJsonV1('environment-state-v1', {
1102
+ messageId: record.messageId,
1103
+ type: record.type,
1104
+ payloadHash: record.payloadHash,
1105
+ sequence: record.sequence,
1106
+ }),
1107
+ stateDigestBefore: null,
1108
+ stateDigestAfter: null,
1109
+ faultBinding: null,
1110
+ });
1111
+ }
1112
+ function assertExactDataRecord(value, expectedKeys, errorCode) {
1113
+ if (value === null || typeof value !== 'object' || Array.isArray(value))
1114
+ throw new TypeError(errorCode);
1115
+ const prototype = Object.getPrototypeOf(value);
1116
+ const actual = Object.getOwnPropertyNames(value).sort();
1117
+ const expected = [...expectedKeys].sort();
1118
+ if ((prototype !== Object.prototype && prototype !== null) ||
1119
+ Object.getOwnPropertySymbols(value).length !== 0 ||
1120
+ actual.length !== expected.length ||
1121
+ actual.some((key, index) => key !== expected[index]))
1122
+ throw new TypeError(errorCode);
1123
+ for (const key of actual) {
1124
+ const descriptor = Object.getOwnPropertyDescriptor(value, key);
1125
+ if (!descriptor || !descriptor.enumerable || !('value' in descriptor))
1126
+ throw new TypeError(errorCode);
1127
+ }
1128
+ }
1129
+ function wallTime(context, offsetMs) {
1130
+ return new Date(context.baseWallTimeMs + offsetMs).toISOString();
1131
+ }
1132
+ function earlierTimestamp(left, right) {
1133
+ return Date.parse(left) <= Date.parse(right) ? left : right;
1134
+ }
1135
+ function scopedId(context, kind, revision) {
1136
+ return `${kind}:${context.input.seed}:${revision}`;
1137
+ }
1138
+ function keyId(peerId) {
1139
+ return `key:${peerId}`;
1140
+ }
1141
+ function planningCapabilityId(peerId) {
1142
+ return `capability:planning:${peerId}`;
1143
+ }
1144
+ function planningAdvertisementId(peerId) {
1145
+ return `advertisement:planning:${peerId}`;
1146
+ }
1147
+ function capabilityId(peerId, capabilityKey) {
1148
+ return `capability:${peerId}:${capabilityKey}`;
1149
+ }
1150
+ function capabilityAdvertisementId(peerId, capabilityKey) {
1151
+ return `advertisement:${peerId}:${capabilityKey}`;
1152
+ }
1153
+ function unique(values) {
1154
+ return [...new Set(values)];
1155
+ }
1156
+ function deterministicMessageId(seed, peerOrdinal, sequence) {
1157
+ const bytes = new Uint8Array(16);
1158
+ const view = new DataView(bytes.buffer);
1159
+ view.setUint32(0, seed >>> 0);
1160
+ view.setUint32(4, peerOrdinal >>> 0);
1161
+ view.setUint32(8, Math.floor(seed / 0x1_0000_0000) >>> 0);
1162
+ view.setUint32(12, sequence >>> 0);
1163
+ return base64Url(bytes);
1164
+ }
1165
+ async function payloadDigest(crypto, payload) {
1166
+ const canonical = canonicalizeMeshPayload(payload);
1167
+ if (!canonical.ok)
1168
+ throw new TypeError('closed_loop_payload_invalid');
1169
+ const bytes = Uint8Array.from(canonical.value);
1170
+ const digest = await crypto.subtle.digest('SHA-256', bytes.buffer);
1171
+ return `sha256:${base64Url(new Uint8Array(digest))}`;
1172
+ }
1173
+ async function contentDigest(crypto, value) {
1174
+ const digest = await crypto.subtle.digest('SHA-256', new TextEncoder().encode(value));
1175
+ return `sha256:${base64Url(new Uint8Array(digest))}`;
1176
+ }
1177
+ function meshContentDigest(value) {
1178
+ if (!/^sha256:[0-9a-f]{64}$/u.test(value))
1179
+ throw new TypeError('closed_loop_result_digest_invalid');
1180
+ const hex = value.slice('sha256:'.length);
1181
+ const bytes = new Uint8Array(32);
1182
+ for (let index = 0; index < bytes.length; index += 1)
1183
+ bytes[index] = Number.parseInt(hex.slice(index * 2, index * 2 + 2), 16);
1184
+ return `sha256:${base64Url(bytes)}`;
1185
+ }
1186
+ function base64Url(bytes) {
1187
+ let binary = '';
1188
+ for (const byte of bytes)
1189
+ binary += String.fromCharCode(byte);
1190
+ return btoa(binary).replaceAll('+', '-').replaceAll('/', '_').replace(/=+$/u, '');
1191
+ }
1192
+ //# sourceMappingURL=collective-closed-loop-runtime.js.map