@garydevenay/emporion 0.0.1

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/LICENSE +21 -0
  2. package/README.md +173 -0
  3. package/dist/src/cli.d.ts +7 -0
  4. package/dist/src/cli.js +2101 -0
  5. package/dist/src/cli.js.map +1 -0
  6. package/dist/src/config.d.ts +2 -0
  7. package/dist/src/config.js +41 -0
  8. package/dist/src/config.js.map +1 -0
  9. package/dist/src/demo.d.ts +1 -0
  10. package/dist/src/demo.js +140 -0
  11. package/dist/src/demo.js.map +1 -0
  12. package/dist/src/did.d.ts +13 -0
  13. package/dist/src/did.js +155 -0
  14. package/dist/src/did.js.map +1 -0
  15. package/dist/src/errors.d.ts +23 -0
  16. package/dist/src/errors.js +25 -0
  17. package/dist/src/errors.js.map +1 -0
  18. package/dist/src/handshake.d.ts +9 -0
  19. package/dist/src/handshake.js +131 -0
  20. package/dist/src/handshake.js.map +1 -0
  21. package/dist/src/identity.d.ts +17 -0
  22. package/dist/src/identity.js +105 -0
  23. package/dist/src/identity.js.map +1 -0
  24. package/dist/src/index.d.ts +5 -0
  25. package/dist/src/index.js +5 -0
  26. package/dist/src/index.js.map +1 -0
  27. package/dist/src/logger.d.ts +11 -0
  28. package/dist/src/logger.js +41 -0
  29. package/dist/src/logger.js.map +1 -0
  30. package/dist/src/persistent-agent.d.ts +2 -0
  31. package/dist/src/persistent-agent.js +24 -0
  32. package/dist/src/persistent-agent.js.map +1 -0
  33. package/dist/src/protocol/company.d.ts +36 -0
  34. package/dist/src/protocol/company.js +211 -0
  35. package/dist/src/protocol/company.js.map +1 -0
  36. package/dist/src/protocol/contracts.d.ts +119 -0
  37. package/dist/src/protocol/contracts.js +404 -0
  38. package/dist/src/protocol/contracts.js.map +1 -0
  39. package/dist/src/protocol/credential-reference.d.ts +71 -0
  40. package/dist/src/protocol/credential-reference.js +136 -0
  41. package/dist/src/protocol/credential-reference.js.map +1 -0
  42. package/dist/src/protocol/dissemination.d.ts +47 -0
  43. package/dist/src/protocol/dissemination.js +111 -0
  44. package/dist/src/protocol/dissemination.js.map +1 -0
  45. package/dist/src/protocol/envelope.d.ts +71 -0
  46. package/dist/src/protocol/envelope.js +203 -0
  47. package/dist/src/protocol/envelope.js.map +1 -0
  48. package/dist/src/protocol/identity.d.ts +18 -0
  49. package/dist/src/protocol/identity.js +143 -0
  50. package/dist/src/protocol/identity.js.map +1 -0
  51. package/dist/src/protocol/index.d.ts +22 -0
  52. package/dist/src/protocol/index.js +12 -0
  53. package/dist/src/protocol/index.js.map +1 -0
  54. package/dist/src/protocol/market.d.ts +96 -0
  55. package/dist/src/protocol/market.js +293 -0
  56. package/dist/src/protocol/market.js.map +1 -0
  57. package/dist/src/protocol/messaging.d.ts +87 -0
  58. package/dist/src/protocol/messaging.js +296 -0
  59. package/dist/src/protocol/messaging.js.map +1 -0
  60. package/dist/src/protocol/repository.d.ts +87 -0
  61. package/dist/src/protocol/repository.js +651 -0
  62. package/dist/src/protocol/repository.js.map +1 -0
  63. package/dist/src/protocol/resolution.d.ts +86 -0
  64. package/dist/src/protocol/resolution.js +305 -0
  65. package/dist/src/protocol/resolution.js.map +1 -0
  66. package/dist/src/protocol/shared.d.ts +14 -0
  67. package/dist/src/protocol/shared.js +45 -0
  68. package/dist/src/protocol/shared.js.map +1 -0
  69. package/dist/src/protocol/versioning.d.ts +21 -0
  70. package/dist/src/protocol/versioning.js +69 -0
  71. package/dist/src/protocol/versioning.js.map +1 -0
  72. package/dist/src/storage.d.ts +36 -0
  73. package/dist/src/storage.js +156 -0
  74. package/dist/src/storage.js.map +1 -0
  75. package/dist/src/topics.d.ts +3 -0
  76. package/dist/src/topics.js +27 -0
  77. package/dist/src/topics.js.map +1 -0
  78. package/dist/src/transport.d.ts +114 -0
  79. package/dist/src/transport.js +291 -0
  80. package/dist/src/transport.js.map +1 -0
  81. package/dist/src/types.d.ts +95 -0
  82. package/dist/src/types.js +2 -0
  83. package/dist/src/types.js.map +1 -0
  84. package/dist/test/cli.test.d.ts +1 -0
  85. package/dist/test/cli.test.js +122 -0
  86. package/dist/test/cli.test.js.map +1 -0
  87. package/dist/test/economy.test.d.ts +1 -0
  88. package/dist/test/economy.test.js +661 -0
  89. package/dist/test/economy.test.js.map +1 -0
  90. package/dist/test/helpers.d.ts +12 -0
  91. package/dist/test/helpers.js +57 -0
  92. package/dist/test/helpers.js.map +1 -0
  93. package/dist/test/integration.test.d.ts +1 -0
  94. package/dist/test/integration.test.js +287 -0
  95. package/dist/test/integration.test.js.map +1 -0
  96. package/dist/test/protocol.test.d.ts +1 -0
  97. package/dist/test/protocol.test.js +448 -0
  98. package/dist/test/protocol.test.js.map +1 -0
  99. package/dist/test/unit.test.d.ts +1 -0
  100. package/dist/test/unit.test.js +87 -0
  101. package/dist/test/unit.test.js.map +1 -0
  102. package/package.json +60 -0
@@ -0,0 +1,661 @@
1
+ import assert from "node:assert/strict";
2
+ import test from "node:test";
3
+ import { AgentTransport, Protocol } from "../src/index.js";
4
+ import { loadIdentityMaterial } from "../src/identity.js";
5
+ import { runCli } from "../src/cli.js";
6
+ import { createBootstrapNode, createTempDir, removeTempDir, waitFor } from "./helpers.js";
7
+ function createSigner(identity) {
8
+ return {
9
+ did: identity.agentIdentity.did,
10
+ publicKey: identity.transportKeyPair.publicKey,
11
+ secretKey: identity.transportKeyPair.secretKey
12
+ };
13
+ }
14
+ function createIoCapture() {
15
+ const stdout = [];
16
+ const stderr = [];
17
+ return {
18
+ stdout,
19
+ stderr,
20
+ io: {
21
+ stdout(message) {
22
+ stdout.push(message);
23
+ },
24
+ stderr(message) {
25
+ stderr.push(message);
26
+ }
27
+ }
28
+ };
29
+ }
30
+ test("contract lifecycle supports evidence-backed milestone completion and contract-linked feedback", async () => {
31
+ const repositoryDir = await createTempDir("emporion-economy-contracts-");
32
+ const ownerDir = await createTempDir("emporion-economy-owner-");
33
+ const workerDir = await createTempDir("emporion-economy-worker-");
34
+ try {
35
+ const ownerIdentity = await loadIdentityMaterial(ownerDir, "11".repeat(32));
36
+ const workerIdentity = await loadIdentityMaterial(workerDir, "22".repeat(32));
37
+ const ownerSigner = createSigner(ownerIdentity);
38
+ const workerSigner = createSigner(workerIdentity);
39
+ const repository = await Protocol.ProtocolRepository.create(repositoryDir);
40
+ const listing = Protocol.signProtocolEnvelope(Protocol.createUnsignedEnvelope({
41
+ objectKind: "listing",
42
+ objectId: "listing-contract-1",
43
+ eventKind: "listing.published",
44
+ actorDid: workerIdentity.agentIdentity.did,
45
+ subjectId: "listing-contract-1",
46
+ issuedAt: "2026-03-07T12:00:00.000Z",
47
+ payload: {
48
+ marketplaceId: "coding",
49
+ sellerDid: workerIdentity.agentIdentity.did,
50
+ title: "Implement contract protocol",
51
+ paymentTerms: {
52
+ currency: "SAT",
53
+ amountSats: 2000,
54
+ settlementMethod: "lightning"
55
+ }
56
+ }
57
+ }), workerSigner);
58
+ await repository.appendEnvelope(listing);
59
+ const contractPayload = {
60
+ originRef: {
61
+ objectKind: "listing",
62
+ objectId: "listing-contract-1"
63
+ },
64
+ parties: [ownerIdentity.agentIdentity.did, workerIdentity.agentIdentity.did],
65
+ scope: "Implement the proof and resolution path",
66
+ milestones: [
67
+ {
68
+ milestoneId: "ms-1",
69
+ title: "Ship protocol reducer",
70
+ deliverableSchema: {
71
+ kind: "artifact",
72
+ requiredArtifactKinds: ["patch"]
73
+ },
74
+ proofPolicy: {
75
+ allowedModes: ["artifact-verifiable"],
76
+ verifierRefs: [
77
+ {
78
+ verifierId: "ci",
79
+ verifierKind: "deterministic",
80
+ algorithm: "tsc+test"
81
+ }
82
+ ],
83
+ minArtifacts: 1,
84
+ requireCounterpartyAcceptance: false
85
+ },
86
+ settlementAdapters: [
87
+ {
88
+ adapterType: "company-reserve-lock",
89
+ adapterId: "reserve-1",
90
+ network: "internal",
91
+ artifactRefs: []
92
+ }
93
+ ]
94
+ }
95
+ ],
96
+ deliverableSchema: {
97
+ kind: "artifact",
98
+ requiredArtifactKinds: ["patch"]
99
+ },
100
+ proofPolicy: {
101
+ allowedModes: ["artifact-verifiable"],
102
+ verifierRefs: [
103
+ {
104
+ verifierId: "ci",
105
+ verifierKind: "deterministic",
106
+ algorithm: "tsc+test"
107
+ }
108
+ ],
109
+ minArtifacts: 1,
110
+ requireCounterpartyAcceptance: false
111
+ },
112
+ resolutionPolicy: {
113
+ mode: "deterministic",
114
+ deterministicVerifierIds: ["ci"]
115
+ },
116
+ settlementPolicy: {
117
+ adapters: [
118
+ {
119
+ adapterType: "company-reserve-lock",
120
+ adapterId: "reserve-1",
121
+ network: "internal",
122
+ artifactRefs: []
123
+ }
124
+ ],
125
+ releaseCondition: "contract-completed"
126
+ },
127
+ deadlinePolicy: {
128
+ milestoneDeadlines: {
129
+ "ms-1": "2026-03-08T12:00:00.000Z"
130
+ }
131
+ }
132
+ };
133
+ const contractCreated = Protocol.signProtocolEnvelope(Protocol.createUnsignedEnvelope({
134
+ objectKind: "contract",
135
+ objectId: "contract-1",
136
+ eventKind: "contract.created",
137
+ actorDid: ownerIdentity.agentIdentity.did,
138
+ subjectId: "contract-1",
139
+ issuedAt: "2026-03-07T12:01:00.000Z",
140
+ payload: Protocol.contractCreatedPayloadToJson(contractPayload)
141
+ }), ownerSigner);
142
+ await repository.appendEnvelope(contractCreated);
143
+ const milestoneOpened = Protocol.signProtocolEnvelope(Protocol.createUnsignedEnvelope({
144
+ objectKind: "contract",
145
+ objectId: "contract-1",
146
+ eventKind: "contract.milestone-opened",
147
+ actorDid: ownerIdentity.agentIdentity.did,
148
+ subjectId: "contract-1",
149
+ issuedAt: "2026-03-07T12:02:00.000Z",
150
+ previousEventIds: [contractCreated.eventId],
151
+ payload: { milestoneId: "ms-1" }
152
+ }), ownerSigner);
153
+ await repository.appendEnvelope(milestoneOpened);
154
+ const evidenceRecorded = Protocol.signProtocolEnvelope(Protocol.createUnsignedEnvelope({
155
+ objectKind: "evidence-bundle",
156
+ objectId: "evidence-1",
157
+ eventKind: "evidence-bundle.recorded",
158
+ actorDid: workerIdentity.agentIdentity.did,
159
+ subjectId: "evidence-1",
160
+ issuedAt: "2026-03-07T12:03:00.000Z",
161
+ payload: Protocol.evidenceBundlePayloadToJson({
162
+ contractId: "contract-1",
163
+ milestoneId: "ms-1",
164
+ submitterDid: workerIdentity.agentIdentity.did,
165
+ artifactRefs: [
166
+ {
167
+ artifactId: "patch",
168
+ hash: "aa".repeat(32)
169
+ }
170
+ ],
171
+ verifierRefs: [
172
+ {
173
+ verifierId: "ci",
174
+ verifierKind: "deterministic",
175
+ algorithm: "tsc+test"
176
+ }
177
+ ],
178
+ proofModes: ["artifact-verifiable"],
179
+ hashes: {
180
+ repository: "bb".repeat(32)
181
+ },
182
+ executionTranscriptRefs: ["transcript-1"]
183
+ })
184
+ }), workerSigner);
185
+ await repository.appendEnvelope(evidenceRecorded);
186
+ const milestoneSubmitted = Protocol.signProtocolEnvelope(Protocol.createUnsignedEnvelope({
187
+ objectKind: "contract",
188
+ objectId: "contract-1",
189
+ eventKind: "contract.milestone-submitted",
190
+ actorDid: workerIdentity.agentIdentity.did,
191
+ subjectId: "contract-1",
192
+ issuedAt: "2026-03-07T12:04:00.000Z",
193
+ previousEventIds: [milestoneOpened.eventId],
194
+ payload: {
195
+ milestoneId: "ms-1",
196
+ evidenceBundleIds: ["evidence-1"]
197
+ }
198
+ }), workerSigner);
199
+ await repository.appendEnvelope(milestoneSubmitted);
200
+ const milestoneAccepted = Protocol.signProtocolEnvelope(Protocol.createUnsignedEnvelope({
201
+ objectKind: "contract",
202
+ objectId: "contract-1",
203
+ eventKind: "contract.milestone-accepted",
204
+ actorDid: ownerIdentity.agentIdentity.did,
205
+ subjectId: "contract-1",
206
+ issuedAt: "2026-03-07T12:05:00.000Z",
207
+ previousEventIds: [milestoneSubmitted.eventId],
208
+ payload: {
209
+ milestoneId: "ms-1",
210
+ evidenceBundleIds: ["evidence-1"]
211
+ }
212
+ }), ownerSigner);
213
+ await repository.appendEnvelope(milestoneAccepted);
214
+ const completed = Protocol.signProtocolEnvelope(Protocol.createUnsignedEnvelope({
215
+ objectKind: "contract",
216
+ objectId: "contract-1",
217
+ eventKind: "contract.completed",
218
+ actorDid: ownerIdentity.agentIdentity.did,
219
+ subjectId: "contract-1",
220
+ issuedAt: "2026-03-07T12:06:00.000Z",
221
+ previousEventIds: [milestoneAccepted.eventId],
222
+ payload: {}
223
+ }), ownerSigner);
224
+ await repository.appendEnvelope(completed);
225
+ const contractState = await repository.readObjectState("contract", "contract-1");
226
+ assert.ok(contractState && "status" in contractState);
227
+ assert.equal(contractState.status, "completed");
228
+ const feedbackRef = {
229
+ credentialId: "feedback-contract-1",
230
+ issuerDid: ownerIdentity.agentIdentity.did,
231
+ subjectDid: workerIdentity.agentIdentity.did,
232
+ relatedContractId: "contract-1",
233
+ relatedAgreementId: "agreement-contract-1",
234
+ completionArtifactRef: "evidence-1",
235
+ summary: {
236
+ score: 5,
237
+ maxScore: 5,
238
+ headline: "Shipped",
239
+ comment: "Delivered with machine-verifiable evidence"
240
+ },
241
+ issuedAt: "2026-03-07T12:07:00.000Z",
242
+ artifactHash: Protocol.createCredentialArtifactHash({
243
+ relatedContractId: "contract-1",
244
+ completionArtifactRef: "evidence-1"
245
+ })
246
+ };
247
+ Protocol.validateFeedbackCredentialRef(feedbackRef);
248
+ await repository.close();
249
+ }
250
+ finally {
251
+ await Promise.allSettled([removeTempDir(repositoryDir), removeTempDir(ownerDir), removeTempDir(workerDir)]);
252
+ }
253
+ });
254
+ test("disputes require authorized oracle attestations and messaging payloads decrypt only for members", async () => {
255
+ const repositoryDir = await createTempDir("emporion-economy-disputes-");
256
+ const ownerDir = await createTempDir("emporion-economy-owner-");
257
+ const workerDir = await createTempDir("emporion-economy-worker-");
258
+ const oracleDir = await createTempDir("emporion-economy-oracle-");
259
+ const outsiderDir = await createTempDir("emporion-economy-outsider-");
260
+ try {
261
+ const ownerIdentity = await loadIdentityMaterial(ownerDir, "31".repeat(32));
262
+ const workerIdentity = await loadIdentityMaterial(workerDir, "32".repeat(32));
263
+ const oracleIdentity = await loadIdentityMaterial(oracleDir, "33".repeat(32));
264
+ const outsiderIdentity = await loadIdentityMaterial(outsiderDir, "34".repeat(32));
265
+ const ownerSigner = createSigner(ownerIdentity);
266
+ const workerSigner = createSigner(workerIdentity);
267
+ const oracleSigner = createSigner(oracleIdentity);
268
+ const outsiderSigner = createSigner(outsiderIdentity);
269
+ const repository = await Protocol.ProtocolRepository.create(repositoryDir);
270
+ const listing = Protocol.signProtocolEnvelope(Protocol.createUnsignedEnvelope({
271
+ objectKind: "listing",
272
+ objectId: "listing-dispute-1",
273
+ eventKind: "listing.published",
274
+ actorDid: workerIdentity.agentIdentity.did,
275
+ subjectId: "listing-dispute-1",
276
+ issuedAt: "2026-03-07T13:00:00.000Z",
277
+ payload: {
278
+ marketplaceId: "coding",
279
+ sellerDid: workerIdentity.agentIdentity.did,
280
+ title: "Resolve oracle-backed contract",
281
+ paymentTerms: {
282
+ currency: "SAT",
283
+ amountSats: 3000,
284
+ settlementMethod: "lightning"
285
+ }
286
+ }
287
+ }), workerSigner);
288
+ await repository.appendEnvelope(listing);
289
+ const contractCreated = Protocol.signProtocolEnvelope(Protocol.createUnsignedEnvelope({
290
+ objectKind: "contract",
291
+ objectId: "contract-dispute-1",
292
+ eventKind: "contract.created",
293
+ actorDid: ownerIdentity.agentIdentity.did,
294
+ subjectId: "contract-dispute-1",
295
+ issuedAt: "2026-03-07T13:01:00.000Z",
296
+ payload: Protocol.contractCreatedPayloadToJson({
297
+ originRef: {
298
+ objectKind: "listing",
299
+ objectId: "listing-dispute-1"
300
+ },
301
+ parties: [ownerIdentity.agentIdentity.did, workerIdentity.agentIdentity.did],
302
+ scope: "Oracle-backed dispute flow",
303
+ milestones: [
304
+ {
305
+ milestoneId: "oracle-ms-1",
306
+ title: "Deliver oracle-reviewed artifact",
307
+ deliverableSchema: {
308
+ kind: "oracle-claim",
309
+ requiredArtifactKinds: ["report"]
310
+ },
311
+ proofPolicy: {
312
+ allowedModes: ["oracle-attested"],
313
+ verifierRefs: [
314
+ {
315
+ verifierId: "oracle-service",
316
+ verifierKind: "oracle-service",
317
+ verifierDid: oracleIdentity.agentIdentity.did
318
+ }
319
+ ],
320
+ requireCounterpartyAcceptance: false
321
+ },
322
+ settlementAdapters: [
323
+ {
324
+ adapterType: "dlc-outcome",
325
+ adapterId: "dlc-1",
326
+ network: "bitcoin",
327
+ artifactRefs: []
328
+ }
329
+ ]
330
+ }
331
+ ],
332
+ deliverableSchema: {
333
+ kind: "oracle-claim",
334
+ requiredArtifactKinds: ["report"]
335
+ },
336
+ proofPolicy: {
337
+ allowedModes: ["oracle-attested"],
338
+ verifierRefs: [
339
+ {
340
+ verifierId: "oracle-service",
341
+ verifierKind: "oracle-service",
342
+ verifierDid: oracleIdentity.agentIdentity.did
343
+ }
344
+ ],
345
+ requireCounterpartyAcceptance: false
346
+ },
347
+ resolutionPolicy: {
348
+ mode: "oracle",
349
+ deterministicVerifierIds: [],
350
+ oracleQuorum: {
351
+ oracleDids: [oracleIdentity.agentIdentity.did],
352
+ quorum: 1
353
+ }
354
+ },
355
+ settlementPolicy: {
356
+ adapters: [
357
+ {
358
+ adapterType: "dlc-outcome",
359
+ adapterId: "dlc-1",
360
+ network: "bitcoin",
361
+ artifactRefs: []
362
+ }
363
+ ],
364
+ releaseCondition: "oracle-ruled"
365
+ },
366
+ deadlinePolicy: {
367
+ milestoneDeadlines: {
368
+ "oracle-ms-1": "2026-03-08T13:00:00.000Z"
369
+ }
370
+ }
371
+ })
372
+ }), ownerSigner);
373
+ await repository.appendEnvelope(contractCreated);
374
+ const disputed = Protocol.signProtocolEnvelope(Protocol.createUnsignedEnvelope({
375
+ objectKind: "contract",
376
+ objectId: "contract-dispute-1",
377
+ eventKind: "contract.disputed",
378
+ actorDid: ownerIdentity.agentIdentity.did,
379
+ subjectId: "contract-dispute-1",
380
+ issuedAt: "2026-03-07T13:02:00.000Z",
381
+ previousEventIds: [contractCreated.eventId],
382
+ payload: {}
383
+ }), ownerSigner);
384
+ await repository.appendEnvelope(disputed);
385
+ const disputeOpened = Protocol.signProtocolEnvelope(Protocol.createUnsignedEnvelope({
386
+ objectKind: "dispute-case",
387
+ objectId: "dispute-1",
388
+ eventKind: "dispute.opened",
389
+ actorDid: ownerIdentity.agentIdentity.did,
390
+ subjectId: "dispute-1",
391
+ issuedAt: "2026-03-07T13:03:00.000Z",
392
+ payload: {
393
+ contractId: "contract-dispute-1",
394
+ milestoneId: "oracle-ms-1",
395
+ reason: "Oracle review required"
396
+ }
397
+ }), ownerSigner);
398
+ await repository.appendEnvelope(disputeOpened);
399
+ const oracleAttestation = Protocol.signProtocolEnvelope(Protocol.createUnsignedEnvelope({
400
+ objectKind: "oracle-attestation",
401
+ objectId: "oracle-attestation-1",
402
+ eventKind: "oracle-attestation.recorded",
403
+ actorDid: oracleIdentity.agentIdentity.did,
404
+ subjectId: "oracle-attestation-1",
405
+ issuedAt: "2026-03-07T13:04:00.000Z",
406
+ payload: Protocol.oracleAttestationPayloadToJson({
407
+ oracleDid: oracleIdentity.agentIdentity.did,
408
+ claimType: "work.completed",
409
+ subjectRef: {
410
+ objectKind: "contract",
411
+ objectId: "contract-dispute-1",
412
+ milestoneId: "oracle-ms-1"
413
+ },
414
+ outcome: "completed",
415
+ evidenceRefs: [],
416
+ issuedAt: "2026-03-07T13:04:00.000Z",
417
+ expiresAt: "2026-03-08T13:04:00.000Z"
418
+ })
419
+ }), oracleSigner);
420
+ await repository.appendEnvelope(oracleAttestation);
421
+ const badOracleAttestation = Protocol.signProtocolEnvelope(Protocol.createUnsignedEnvelope({
422
+ objectKind: "oracle-attestation",
423
+ objectId: "oracle-attestation-unauthorized",
424
+ eventKind: "oracle-attestation.recorded",
425
+ actorDid: outsiderIdentity.agentIdentity.did,
426
+ subjectId: "oracle-attestation-unauthorized",
427
+ issuedAt: "2026-03-07T13:05:00.000Z",
428
+ payload: Protocol.oracleAttestationPayloadToJson({
429
+ oracleDid: outsiderIdentity.agentIdentity.did,
430
+ claimType: "work.completed",
431
+ subjectRef: {
432
+ objectKind: "contract",
433
+ objectId: "contract-dispute-1"
434
+ },
435
+ outcome: "completed",
436
+ evidenceRefs: [],
437
+ issuedAt: "2026-03-07T13:05:00.000Z",
438
+ expiresAt: "2026-03-08T13:05:00.000Z"
439
+ })
440
+ }), outsiderSigner);
441
+ await repository.appendEnvelope(badOracleAttestation);
442
+ const ruled = Protocol.signProtocolEnvelope(Protocol.createUnsignedEnvelope({
443
+ objectKind: "dispute-case",
444
+ objectId: "dispute-1",
445
+ eventKind: "dispute.ruled",
446
+ actorDid: ownerIdentity.agentIdentity.did,
447
+ subjectId: "dispute-1",
448
+ issuedAt: "2026-03-07T13:06:00.000Z",
449
+ previousEventIds: [disputeOpened.eventId],
450
+ payload: Protocol.disputeRulingToJson({
451
+ outcome: "fulfilled",
452
+ resolutionMode: "oracle",
453
+ oracleAttestationIds: ["oracle-attestation-1"],
454
+ evidenceBundleIds: [],
455
+ approverDids: []
456
+ })
457
+ }), ownerSigner);
458
+ await repository.appendEnvelope(ruled);
459
+ await assert.rejects(async () => {
460
+ const badRuling = Protocol.signProtocolEnvelope(Protocol.createUnsignedEnvelope({
461
+ objectKind: "dispute-case",
462
+ objectId: "dispute-bad",
463
+ eventKind: "dispute.opened",
464
+ actorDid: ownerIdentity.agentIdentity.did,
465
+ subjectId: "dispute-bad",
466
+ issuedAt: "2026-03-07T13:07:00.000Z",
467
+ payload: {
468
+ contractId: "contract-dispute-1",
469
+ reason: "bad oracle"
470
+ }
471
+ }), ownerSigner);
472
+ await repository.appendEnvelope(badRuling);
473
+ const unauthorizedRuling = Protocol.signProtocolEnvelope(Protocol.createUnsignedEnvelope({
474
+ objectKind: "dispute-case",
475
+ objectId: "dispute-bad",
476
+ eventKind: "dispute.ruled",
477
+ actorDid: ownerIdentity.agentIdentity.did,
478
+ subjectId: "dispute-bad",
479
+ issuedAt: "2026-03-07T13:08:00.000Z",
480
+ previousEventIds: [badRuling.eventId],
481
+ payload: Protocol.disputeRulingToJson({
482
+ outcome: "fulfilled",
483
+ resolutionMode: "oracle",
484
+ oracleAttestationIds: ["oracle-attestation-unauthorized"],
485
+ evidenceBundleIds: [],
486
+ approverDids: []
487
+ })
488
+ }), ownerSigner);
489
+ await repository.appendEnvelope(unauthorizedRuling);
490
+ }, /not authorized by the contract/i);
491
+ const spaceCreated = Protocol.signProtocolEnvelope(Protocol.createUnsignedEnvelope({
492
+ objectKind: "space",
493
+ objectId: "space-1",
494
+ eventKind: "space.created",
495
+ actorDid: ownerIdentity.agentIdentity.did,
496
+ subjectId: "space-1",
497
+ issuedAt: "2026-03-07T13:09:00.000Z",
498
+ payload: Protocol.spacePayloadToJson({
499
+ spaceKind: "contract-thread",
500
+ ownerRef: {
501
+ kind: "contract",
502
+ id: "contract-dispute-1"
503
+ },
504
+ membershipPolicy: {
505
+ mode: "invite-only",
506
+ ownerMemberDids: [ownerIdentity.agentIdentity.did]
507
+ },
508
+ encryptionPolicy: {
509
+ mode: "member-sealed-box",
510
+ keyAgreementMethod: "did-keyagreement-v1"
511
+ }
512
+ })
513
+ }), ownerSigner);
514
+ await repository.appendEnvelope(spaceCreated);
515
+ const ownerMembership = Protocol.signProtocolEnvelope(Protocol.createUnsignedEnvelope({
516
+ objectKind: "space-membership",
517
+ objectId: "space-1:owner",
518
+ eventKind: "space-membership.member-added",
519
+ actorDid: ownerIdentity.agentIdentity.did,
520
+ subjectId: "space-1:owner",
521
+ issuedAt: "2026-03-07T13:10:00.000Z",
522
+ payload: Protocol.spaceMembershipPayloadToJson({
523
+ spaceId: "space-1",
524
+ memberDid: ownerIdentity.agentIdentity.did,
525
+ role: "owner"
526
+ })
527
+ }), ownerSigner);
528
+ await repository.appendEnvelope(ownerMembership);
529
+ const workerMembership = Protocol.signProtocolEnvelope(Protocol.createUnsignedEnvelope({
530
+ objectKind: "space-membership",
531
+ objectId: "space-1:worker",
532
+ eventKind: "space-membership.member-added",
533
+ actorDid: ownerIdentity.agentIdentity.did,
534
+ subjectId: "space-1:worker",
535
+ issuedAt: "2026-03-07T13:11:00.000Z",
536
+ payload: Protocol.spaceMembershipPayloadToJson({
537
+ spaceId: "space-1",
538
+ memberDid: workerIdentity.agentIdentity.did,
539
+ role: "member"
540
+ })
541
+ }), ownerSigner);
542
+ await repository.appendEnvelope(workerMembership);
543
+ const encryptedBody = await Protocol.encryptMessageForRecipients({
544
+ plaintext: "private proof update",
545
+ senderDid: ownerIdentity.agentIdentity.did,
546
+ senderKeyAgreementPublicKey: ownerIdentity.agentIdentity.keyAgreementPublicKey,
547
+ senderKeyAgreementSecretKey: ownerIdentity.keyAgreementKeyPair.secretKey,
548
+ recipientDids: [workerIdentity.agentIdentity.did]
549
+ });
550
+ const messageSent = Protocol.signProtocolEnvelope(Protocol.createUnsignedEnvelope({
551
+ objectKind: "message",
552
+ objectId: "message-1",
553
+ eventKind: "message.sent",
554
+ actorDid: ownerIdentity.agentIdentity.did,
555
+ subjectId: "message-1",
556
+ issuedAt: "2026-03-07T13:12:00.000Z",
557
+ payload: Protocol.messageSentPayloadToJson({
558
+ spaceId: "space-1",
559
+ messageType: "text",
560
+ metadata: {},
561
+ encryptedBody,
562
+ sentAt: "2026-03-07T13:12:00.000Z"
563
+ })
564
+ }), ownerSigner);
565
+ await repository.appendEnvelope(messageSent);
566
+ const messageState = await repository.readObjectState("message", "message-1");
567
+ assert.ok(messageState && "encryptedBody" in messageState);
568
+ const plaintext = Protocol.decryptEncryptedMessageBody({
569
+ encryptedBody: messageState.encryptedBody,
570
+ recipientDid: workerIdentity.agentIdentity.did,
571
+ recipientKeyAgreementSecretKey: workerIdentity.keyAgreementKeyPair.secretKey
572
+ });
573
+ assert.equal(plaintext, "private proof update");
574
+ assert.throws(() => Protocol.decryptEncryptedMessageBody({
575
+ encryptedBody: messageState.encryptedBody,
576
+ recipientDid: outsiderIdentity.agentIdentity.did,
577
+ recipientKeyAgreementSecretKey: outsiderIdentity.keyAgreementKeyPair.secretKey
578
+ }), /not addressed|Failed to decrypt/i);
579
+ await repository.close();
580
+ }
581
+ finally {
582
+ await Promise.allSettled([
583
+ removeTempDir(repositoryDir),
584
+ removeTempDir(ownerDir),
585
+ removeTempDir(workerDir),
586
+ removeTempDir(oracleDir),
587
+ removeTempDir(outsiderDir)
588
+ ]);
589
+ }
590
+ });
591
+ test("protocol announcements replicate over the control feed and make spaces discoverable to peers", async () => {
592
+ const bootstrapNode = await createBootstrapNode();
593
+ const agentADir = await createTempDir("emporion-economy-agent-a-");
594
+ const agentBDir = await createTempDir("emporion-economy-agent-b-");
595
+ try {
596
+ const initCapture = createIoCapture();
597
+ assert.equal(await runCli(["agent", "init", "--data-dir", agentADir, "--display-name", "Agent A"], initCapture.io), 0);
598
+ const initPayload = JSON.parse(initCapture.stdout.join(""));
599
+ const agentADid = initPayload.identity.did;
600
+ const spaceCapture = createIoCapture();
601
+ assert.equal(await runCli([
602
+ "space",
603
+ "create",
604
+ "--data-dir",
605
+ agentADir,
606
+ "--space-kind",
607
+ "market-room",
608
+ "--owner-kind",
609
+ "agent",
610
+ "--owner-id",
611
+ agentADid,
612
+ "--id",
613
+ "space-discovery-1"
614
+ ], spaceCapture.io), 0);
615
+ const agentA = await AgentTransport.create({
616
+ dataDir: agentADir,
617
+ bootstrap: bootstrapNode.bootstrap,
618
+ logLevel: "error"
619
+ });
620
+ const agentB = await AgentTransport.create({
621
+ dataDir: agentBDir,
622
+ bootstrap: bootstrapNode.bootstrap,
623
+ logLevel: "error"
624
+ });
625
+ try {
626
+ await agentA.start();
627
+ await agentB.start();
628
+ await agentA.joinTopic({ kind: "marketplace", marketplaceId: "economy" });
629
+ await agentB.joinTopic({ kind: "marketplace", marketplaceId: "economy" });
630
+ const session = await waitFor(() => [...agentB.getPeerSessions().values()].find((entry) => entry.remoteDid === agentA.identity.did), { timeoutMs: 10_000, message: "Agent B did not discover Agent A" });
631
+ assert.ok(session);
632
+ const remoteControlFeed = await waitFor(async () => {
633
+ const feed = agentB.getRemoteFeed(session.remoteControlFeedKey);
634
+ if (!feed) {
635
+ return undefined;
636
+ }
637
+ await feed.update({ wait: false });
638
+ for (let index = 0; index < feed.length; index += 1) {
639
+ const value = await feed.get(index);
640
+ if (Protocol.isProtocolAnnouncement(value) && value.kind === "space-descriptor" && value.objectId === "space-discovery-1") {
641
+ return value;
642
+ }
643
+ }
644
+ return undefined;
645
+ }, { timeoutMs: 10_000, message: "Agent B did not receive the space descriptor announcement" });
646
+ if (!remoteControlFeed) {
647
+ throw new Error("Expected a replicated space descriptor announcement");
648
+ }
649
+ assert.equal(remoteControlFeed.kind, "space-descriptor");
650
+ assert.equal(remoteControlFeed.spaceKind, "market-room");
651
+ assert.equal(remoteControlFeed.encryptionMode, "member-sealed-box");
652
+ }
653
+ finally {
654
+ await Promise.allSettled([agentA.stop(), agentB.stop()]);
655
+ }
656
+ }
657
+ finally {
658
+ await Promise.allSettled([bootstrapNode.destroy(), removeTempDir(agentADir), removeTempDir(agentBDir)]);
659
+ }
660
+ });
661
+ //# sourceMappingURL=economy.test.js.map