@uuaid/agent-commons 0.2.0-alpha.2 → 0.2.0-alpha.4

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 (72) hide show
  1. package/CONFORMANCE.md +1 -1
  2. package/NOTICE +4 -7
  3. package/PROVENANCE.json +12 -17
  4. package/README.md +103 -12
  5. package/bin/agent-commons.mjs +8 -3
  6. package/examples/Dockerfile +1 -1
  7. package/package.json +5 -2
  8. package/src/carrier.mjs +7 -2
  9. package/src/channels.mjs +1 -1
  10. package/src/config.mjs +2 -1
  11. package/src/continuity.mjs +14 -7
  12. package/src/governance-scope.mjs +1 -1
  13. package/src/governance.mjs +4 -2
  14. package/src/index.d.ts +3 -1
  15. package/src/pillar.mjs +2 -2
  16. package/src/profiles.mjs +2 -1
  17. package/src/relay/auth.mjs +294 -0
  18. package/src/relay/board.mjs +223 -0
  19. package/src/relay/claims.mjs +25 -0
  20. package/src/relay/cli.mjs +49 -0
  21. package/src/relay/client.mjs +437 -0
  22. package/src/relay/docs/reconnect-v1.md +27 -0
  23. package/src/relay/docs/working-rules-v1.md +42 -0
  24. package/src/relay/enroll.mjs +232 -0
  25. package/src/relay/export-cli.mjs +47 -0
  26. package/src/relay/governance.mjs +1328 -0
  27. package/src/relay/mailbox.mjs +103 -0
  28. package/src/relay/records.mjs +146 -0
  29. package/src/relay/render.mjs +38 -0
  30. package/src/relay/roster.mjs +134 -0
  31. package/src/relay/routes.mjs +87 -0
  32. package/src/relay/schema.mjs +1081 -0
  33. package/src/relay/seed.mjs +89 -0
  34. package/src/relay/serve.mjs +116 -0
  35. package/src/relay/server.mjs +633 -0
  36. package/src/relay/signing.mjs +693 -0
  37. package/src/relay/test-mode.mjs +73 -0
  38. package/src/runtime.mjs +25 -12
  39. package/src/spaces/manifest.mjs +245 -0
  40. package/src/spaces/pillar-group.mjs +12 -0
  41. package/src/spaces/policy.mjs +229 -0
  42. package/src/spaces/store.mjs +325 -0
  43. package/src/trust.mjs +44 -5
  44. package/src/uuaid-namespace.d.mts +6 -0
  45. package/src/uuaid-namespace.mjs +48 -0
  46. package/src/vendor/pillar/blob/blob.mjs +12 -14
  47. package/src/vendor/pillar/groups-v2.mjs +1108 -0
  48. package/src/vendor/pillar/groups.mjs +175 -0
  49. package/src/vendor/pillar/identity/jcs.mjs +4 -5
  50. package/src/vendor/pillar/identity/keychain.mjs +51 -2
  51. package/src/vendor/pillar/net/carrier-client.mjs +29 -7
  52. package/src/vendor/pillar/net/refusals.mjs +4 -2
  53. package/test/channels.test.mjs +0 -268
  54. package/test/cohort.test.mjs +0 -36
  55. package/test/collaboration.test.mjs +0 -459
  56. package/test/continuity.test.mjs +0 -229
  57. package/test/custody.negative.test.mjs +0 -165
  58. package/test/derivation-parity.test.mjs +0 -19
  59. package/test/fixtures/derivation-vectors.json +0 -26
  60. package/test/fixtures/ed25519-small-order.vectors.json +0 -15
  61. package/test/governance-expected-red.expected-red.mjs +0 -241
  62. package/test/governance-scope.test.mjs +0 -142
  63. package/test/governance-t1-t10.test.mjs +0 -477
  64. package/test/governance.test.mjs +0 -514
  65. package/test/hold-admission.test.mjs +0 -1185
  66. package/test/keychain-strict-ed25519.test.mjs +0 -240
  67. package/test/offline.test.mjs +0 -152
  68. package/test/runtime-channels.test.mjs +0 -1140
  69. package/test/runtime.test.mjs +0 -201
  70. package/test/security-runtime.test.mjs +0 -104
  71. package/test/strict-ed25519-consumers.test.mjs +0 -234
  72. package/test/transport.test.mjs +0 -353
@@ -1,353 +0,0 @@
1
- import test from "node:test";
2
- import assert from "node:assert/strict";
3
- import { MemoryLoopbackTransport, PillarCarrierTransport } from "../src/transport.mjs";
4
- import { OFFLINE_ENV } from "../src/offline.mjs";
5
- import { Keychain } from "../src/pillar.mjs";
6
-
7
- function makeKeychain() {
8
- const kc = new Keychain("unused");
9
- kc._identity = Keychain.generate();
10
- return kc;
11
- }
12
-
13
- test("MemoryLoopbackTransport: basic deliver, sequencing, and fetchInbox (two-pole)", async () => {
14
- const transport = new MemoryLoopbackTransport({ instanceId: "test-single" });
15
- assert.equal(transport.sourceId, "memory://loopback/test-single");
16
- assert.deepEqual(transport.sources(), ["memory://loopback/test-single"]);
17
-
18
- const env1 = { id: "msg-1", sender: "agent-a", recipient: "agent-b" };
19
- const env2 = { id: "msg-2", sender: "agent-a", recipient: "agent-b" };
20
-
21
- // Control pole: delivery returns accepted: true with monotonic seq
22
- const receipt1 = await transport.deliver(env1);
23
- assert.equal(receipt1.accepted, true);
24
- assert.equal(receipt1.seq, 1);
25
- assert.equal(receipt1.carrier, "memory://loopback/test-single");
26
- assert.equal(receipt1.duplicate, false);
27
- assert.ok(typeof receipt1.sha === "string" && receipt1.sha.length > 0);
28
-
29
- const receipt2 = await transport.deliver(env2);
30
- assert.equal(receipt2.accepted, true);
31
- assert.equal(receipt2.seq, 2);
32
-
33
- // Fetch all since seq 0
34
- const batchAll = await transport.fetchInbox(transport.sourceId, { since: 0 });
35
- assert.equal(batchAll.sourceId, "memory://loopback/test-single");
36
- assert.equal(batchAll.envelopes.length, 2);
37
- assert.equal(batchAll.envelopes[0].seq, 1);
38
- assert.equal(batchAll.envelopes[0].sourceId, "memory://loopback/test-single");
39
- assert.equal(batchAll.envelopes[0].envelope.id, "msg-1");
40
- assert.equal(batchAll.envelopes[1].seq, 2);
41
-
42
- // Fetch since seq 1 (should only return msg-2)
43
- const batchSince1 = await transport.fetchInbox(transport.sourceId, { since: 1 });
44
- assert.equal(batchSince1.envelopes.length, 1);
45
- assert.equal(batchSince1.envelopes[0].seq, 2);
46
- assert.equal(batchSince1.envelopes[0].envelope.id, "msg-2");
47
-
48
- // Unknown sourceId fails closed
49
- await assert.rejects(
50
- async () => transport.fetchInbox("memory://loopback/other"),
51
- (err) => err.code === "UNKNOWN_SOURCE_ID"
52
- );
53
-
54
- await transport.close();
55
- });
56
-
57
- test("MemoryLoopbackTransport: unique instance qualifiers and duplicate rejection (two-pole)", async () => {
58
- // Omitted qualifiers generate unique IDs
59
- const defaultA = new MemoryLoopbackTransport();
60
- const defaultB = new MemoryLoopbackTransport();
61
- assert.notEqual(
62
- defaultA.sourceId,
63
- defaultB.sourceId,
64
- "Omitted instanceId must generate unique sourceId to avoid cursor crosstalk"
65
- );
66
-
67
- // Explicit non-empty qualifier works
68
- const namedA = new MemoryLoopbackTransport({ instanceId: "my-named-instance" });
69
- assert.equal(namedA.sourceId, "memory://loopback/my-named-instance");
70
-
71
- // Duplicate explicit qualifier is rejected
72
- assert.throws(
73
- () => new MemoryLoopbackTransport({ instanceId: "my-named-instance" }),
74
- (err) => err.code === "DUPLICATE_TRANSPORT_INSTANCE"
75
- );
76
-
77
- // Invalid / empty qualifiers rejected
78
- assert.throws(
79
- () => new MemoryLoopbackTransport({ instanceId: "" }),
80
- /instanceId must be a non-empty string/
81
- );
82
-
83
- await defaultA.close();
84
- await defaultB.close();
85
- await namedA.close();
86
-
87
- // After close, name can be reused cleanly
88
- const namedAReused = new MemoryLoopbackTransport({ instanceId: "my-named-instance" });
89
- assert.equal(namedAReused.sourceId, "memory://loopback/my-named-instance");
90
- await namedAReused.close();
91
- });
92
-
93
- test("MemoryLoopbackTransport: async subscriptions execute strictly in FIFO order (two-pole)", async () => {
94
- const transport = new MemoryLoopbackTransport();
95
- const executionLog = [];
96
-
97
- const unsubscribe = transport.subscribe(transport.sourceId, async (item) => {
98
- executionLog.push(`start:${item.seq}`);
99
- if (item.seq === 1) {
100
- // Simulate slow async processing on message 1 (50ms)
101
- await new Promise((resolve) => setTimeout(resolve, 50));
102
- } else {
103
- await new Promise((resolve) => setTimeout(resolve, 5));
104
- }
105
- executionLog.push(`end:${item.seq}`);
106
- });
107
-
108
- await transport.deliver({ id: "msg-fifo-1" });
109
- await transport.deliver({ id: "msg-fifo-2" });
110
-
111
- // Wait for queue processing to drain
112
- await new Promise((resolve) => setTimeout(resolve, 80));
113
-
114
- // Control pole: Must be strictly serialized start:1, end:1, start:2, end:2 (no start:2 before end:1)
115
- assert.deepEqual(
116
- executionLog,
117
- ["start:1", "end:1", "start:2", "end:2"],
118
- "Async subscription callbacks must complete strictly in FIFO order without overlapping"
119
- );
120
-
121
- // Unsubscribe stops future deliveries
122
- unsubscribe();
123
- await transport.deliver({ id: "msg-fifo-3" });
124
- await new Promise((resolve) => setTimeout(resolve, 10));
125
-
126
- assert.equal(executionLog.length, 4, "Unsubscribed callback must not receive new messages");
127
-
128
- await transport.close();
129
- });
130
-
131
- test("PillarCarrierTransport: adapter normalizes legacy deliver and fetchInbox (two-pole)", async () => {
132
- // Fake legacy client returning shapes without L3 fields
133
- const fakeLegacyClient = {
134
- carriers: ["http://127.0.0.1:5087"],
135
- async deliver(envelope, options) {
136
- // Legacy carrier-client.mjs:134 returns { carrier, seq, sha, duplicate, all } without accepted
137
- return {
138
- carrier: "http://127.0.0.1:5087",
139
- seq: 42,
140
- sha: "abc123sha",
141
- duplicate: false,
142
- all: []
143
- };
144
- },
145
- async fetchInbox(base, options) {
146
- // Legacy carrier-client.mjs:163 returns { envelopes: [{ seq, envelope }], now } without sourceId
147
- return {
148
- envelopes: [
149
- { seq: 10, envelope: { id: "in-1", sender: "peer", recipient: "me" } },
150
- { seq: 11, envelope: { id: "in-2", sender: "peer", recipient: "me" } }
151
- ],
152
- now: 1700000000000
153
- };
154
- }
155
- };
156
-
157
- const transport = new PillarCarrierTransport({
158
- client: fakeLegacyClient,
159
- env: { [OFFLINE_ENV]: "1" } // Loopback is permitted even when offline is forced
160
- });
161
-
162
- // Control pole: deliver normalizes accepted: true
163
- const receipt = await transport.deliver({ id: "out-1" });
164
- assert.equal(receipt.accepted, true);
165
- assert.equal(receipt.carrier, "http://127.0.0.1:5087");
166
- assert.equal(receipt.seq, 42);
167
- assert.equal(receipt.sha, "abc123sha");
168
-
169
- // Control pole: fetchInbox annotates each item with sourceId
170
- const batch = await transport.fetchInbox("http://127.0.0.1:5087", { since: 0 });
171
- assert.equal(batch.sourceId, "http://127.0.0.1:5087");
172
- assert.equal(batch.envelopes.length, 2);
173
- assert.equal(batch.envelopes[0].sourceId, "http://127.0.0.1:5087");
174
- assert.equal(batch.envelopes[0].seq, 10);
175
- assert.equal(batch.envelopes[1].sourceId, "http://127.0.0.1:5087");
176
- assert.equal(batch.envelopes[1].seq, 11);
177
-
178
- // Reject unexpected source ID
179
- await assert.rejects(
180
- async () => transport.fetchInbox("http://127.0.0.1:9999"),
181
- (err) => err.code === "UNEXPECTED_SOURCE_ID"
182
- );
183
-
184
- await transport.close();
185
- });
186
-
187
- test("PillarCarrierTransport: injected client obeys forced offline refusal (two-pole)", async () => {
188
- const remoteCarrier = "https://carrier.remote.test";
189
- let injectedDeliverCalled = false;
190
- let injectedFetchCalled = false;
191
-
192
- const fakeInjectedClient = {
193
- carriers: [remoteCarrier],
194
- async deliver() {
195
- injectedDeliverCalled = true;
196
- return { carrier: remoteCarrier, seq: 1 };
197
- },
198
- async fetchInbox() {
199
- injectedFetchCalled = true;
200
- return { envelopes: [], now: 1000 };
201
- }
202
- };
203
-
204
- // With AGENT_COMMONS_OFFLINE="1", injected client MUST be refused before deliver or fetch
205
- const guardedTransport = new PillarCarrierTransport({
206
- client: fakeInjectedClient,
207
- env: { [OFFLINE_ENV]: "1" }
208
- });
209
-
210
- await assert.rejects(
211
- async () => guardedTransport.deliver({ id: "env-1", carrier: remoteCarrier }),
212
- (err) => err.code === "offline" && err.carrier === remoteCarrier
213
- );
214
- assert.equal(injectedDeliverCalled, false, "Injected client deliver must not be called when offline");
215
-
216
- await assert.rejects(
217
- async () => guardedTransport.fetchInbox(remoteCarrier),
218
- (err) => err.code === "offline" && err.carrier === remoteCarrier
219
- );
220
- assert.equal(injectedFetchCalled, false, "Injected client fetchInbox must not be called when offline");
221
- });
222
-
223
- test("PillarCarrierTransport: empty target carrier set fails closed when offline (two-pole R3b)", async () => {
224
- let clientCalled = false;
225
- const fakeClientNoCarriers = {
226
- // No carriers property
227
- async deliver() {
228
- clientCalled = true;
229
- return { seq: 1 };
230
- }
231
- };
232
-
233
- const transport = new PillarCarrierTransport({
234
- client: fakeClientNoCarriers,
235
- env: { [OFFLINE_ENV]: "1" }
236
- });
237
-
238
- // Envelope has no carrier field
239
- await assert.rejects(
240
- async () => transport.deliver({ id: "env-empty-carrier" }),
241
- (err) => err.code === "offline" && err.carrier === "unspecified"
242
- );
243
- assert.equal(clientCalled, false, "Injected client deliver must not be called when offline with empty carrier set");
244
- });
245
-
246
- test("R3c offline unknown injected destination must not be authorized by envelope carrier hint (two-pole)", async () => {
247
- let called = false;
248
- const client = {
249
- async deliver() {
250
- called = true;
251
- return { carrier: "https://pillar.uuaid.org", seq: 1, sha: "fixture", duplicate: false };
252
- }
253
- };
254
- const adapter = new PillarCarrierTransport({
255
- client,
256
- env: { [OFFLINE_ENV]: "1" }
257
- });
258
- let receipt;
259
- let code = null;
260
- try {
261
- receipt = await adapter.deliver({ id: "carrier-hint", carrier: "http://127.0.0.1:5087" });
262
- } catch (error) {
263
- code = error.code;
264
- }
265
- assert.equal(called, false, "Unknown injected client must not be called under offline mode even with loopback carrier hint");
266
- assert.equal(code, "offline");
267
- assert.equal(receipt, undefined);
268
- });
269
-
270
- test("PillarCarrierTransport: offline guard refuses non-loopback egress (two-pole)", async () => {
271
- const remoteCarrier = "https://pillar.uuaid.org";
272
- const loopbackCarrier = "http://127.0.0.1:5087";
273
-
274
- // Control pole 1: remote carrier refused offline
275
- const offlineTransport = new PillarCarrierTransport({
276
- keychain: makeKeychain(),
277
- carriers: [remoteCarrier],
278
- env: { [OFFLINE_ENV]: "1" }
279
- });
280
-
281
- await assert.rejects(
282
- async () => offlineTransport.deliver({ id: "out-1" }),
283
- (err) => err.code === "offline" && err.carrier === remoteCarrier
284
- );
285
-
286
- await assert.rejects(
287
- async () => offlineTransport.fetchInbox(remoteCarrier),
288
- (err) => err.code === "offline" && err.carrier === remoteCarrier
289
- );
290
-
291
- // Control pole 2: loopback carrier is allowed past the offline guard
292
- let loopbackDeliverCalled = false;
293
- const fakeLoopbackClient = {
294
- carriers: [loopbackCarrier],
295
- _offlineEnv: { [OFFLINE_ENV]: "1" },
296
- async deliver(env) {
297
- loopbackDeliverCalled = true;
298
- return { carrier: loopbackCarrier, seq: 1, sha: "sha", duplicate: false };
299
- }
300
- };
301
- const loopbackTransport = new PillarCarrierTransport({ client: fakeLoopbackClient, env: { [OFFLINE_ENV]: "1" } });
302
- const loopbackReceipt = await loopbackTransport.deliver({ id: "loopback-1" });
303
- assert.equal(loopbackReceipt.accepted, true);
304
- assert.equal(loopbackDeliverCalled, true);
305
-
306
- // Opt-in AGENT_COMMONS_OFFLINE="0" permits remote call past guard
307
- let remoteDeliverCalled = false;
308
- const fakeRemoteClient = {
309
- carriers: [remoteCarrier],
310
- _offlineEnv: { [OFFLINE_ENV]: "0" },
311
- async deliver(env) {
312
- remoteDeliverCalled = true;
313
- return { carrier: remoteCarrier, seq: 1, sha: "sha", duplicate: false };
314
- }
315
- };
316
- const optInTransport = new PillarCarrierTransport({ client: fakeRemoteClient, env: { [OFFLINE_ENV]: "0" } });
317
- const optInReceipt = await optInTransport.deliver({ id: "optin-1" });
318
- assert.equal(optInReceipt.accepted, true);
319
- assert.equal(remoteDeliverCalled, true);
320
- });
321
-
322
- test("TransportCapabilities and DeliveryResult match v4 design contract (two-pole)", () => {
323
- const loopback = new MemoryLoopbackTransport();
324
- const caps = loopback.capabilities;
325
-
326
- assert.equal(typeof caps.name, "string");
327
- assert.equal(typeof caps.canPush, "boolean");
328
- assert.equal(typeof caps.canPoll, "boolean");
329
- assert.equal(typeof caps.supportsStreaming, "boolean");
330
- assert.equal(typeof caps.maxMessageBytes, "number");
331
- assert.equal(typeof caps.loopbackSafe, "boolean");
332
-
333
- assert.equal(caps.name, "memory");
334
- assert.equal(caps.canPush, true);
335
- assert.equal(caps.canPoll, true);
336
- assert.equal(caps.supportsStreaming, true);
337
- assert.equal(caps.maxMessageBytes, 64000);
338
- assert.equal(caps.loopbackSafe, true);
339
-
340
- const pillar = new PillarCarrierTransport({
341
- keychain: makeKeychain(),
342
- carriers: ["http://127.0.0.1:5087"]
343
- });
344
- const pCaps = pillar.capabilities;
345
- assert.equal(pCaps.name, "pillar");
346
- assert.equal(pCaps.canPush, false);
347
- assert.equal(pCaps.canPoll, true);
348
- assert.equal(pCaps.supportsStreaming, false);
349
- assert.equal(pCaps.maxMessageBytes, 512000);
350
- assert.equal(pCaps.loopbackSafe, false);
351
-
352
- loopback.close();
353
- });