@ensoul-network/plugin-elizaos 0.1.0

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 (67) hide show
  1. package/.turbo/turbo-build.log +4 -0
  2. package/.turbo/turbo-lint.log +5 -0
  3. package/.turbo/turbo-test.log +15 -0
  4. package/SECURITY.md +37 -0
  5. package/coverage/actions.ts.html +514 -0
  6. package/coverage/adapter.ts.html +298 -0
  7. package/coverage/base.css +224 -0
  8. package/coverage/block-navigation.js +87 -0
  9. package/coverage/clover.xml +495 -0
  10. package/coverage/coverage-final.json +9 -0
  11. package/coverage/elizaos-types.ts.html +397 -0
  12. package/coverage/evaluators.ts.html +196 -0
  13. package/coverage/favicon.png +0 -0
  14. package/coverage/handshake.ts.html +940 -0
  15. package/coverage/index.html +221 -0
  16. package/coverage/index.ts.html +208 -0
  17. package/coverage/plugin.ts.html +367 -0
  18. package/coverage/prettify.css +1 -0
  19. package/coverage/prettify.js +2 -0
  20. package/coverage/providers.ts.html +286 -0
  21. package/coverage/sort-arrow-sprite.png +0 -0
  22. package/coverage/sorter.js +210 -0
  23. package/dist/actions.d.ts +24 -0
  24. package/dist/actions.d.ts.map +1 -0
  25. package/dist/actions.js +108 -0
  26. package/dist/actions.js.map +1 -0
  27. package/dist/adapter.d.ts +18 -0
  28. package/dist/adapter.d.ts.map +1 -0
  29. package/dist/adapter.js +55 -0
  30. package/dist/adapter.js.map +1 -0
  31. package/dist/elizaos-types.d.ts +81 -0
  32. package/dist/elizaos-types.d.ts.map +1 -0
  33. package/dist/elizaos-types.js +7 -0
  34. package/dist/elizaos-types.js.map +1 -0
  35. package/dist/evaluators.d.ts +8 -0
  36. package/dist/evaluators.d.ts.map +1 -0
  37. package/dist/evaluators.js +24 -0
  38. package/dist/evaluators.js.map +1 -0
  39. package/dist/handshake.d.ts +78 -0
  40. package/dist/handshake.d.ts.map +1 -0
  41. package/dist/handshake.js +195 -0
  42. package/dist/handshake.js.map +1 -0
  43. package/dist/index.d.ts +10 -0
  44. package/dist/index.d.ts.map +1 -0
  45. package/dist/index.js +7 -0
  46. package/dist/index.js.map +1 -0
  47. package/dist/plugin.d.ts +24 -0
  48. package/dist/plugin.d.ts.map +1 -0
  49. package/dist/plugin.js +53 -0
  50. package/dist/plugin.js.map +1 -0
  51. package/dist/providers.d.ts +12 -0
  52. package/dist/providers.d.ts.map +1 -0
  53. package/dist/providers.js +49 -0
  54. package/dist/providers.js.map +1 -0
  55. package/package.json +25 -0
  56. package/src/actions.ts +143 -0
  57. package/src/adapter.ts +71 -0
  58. package/src/elizaos-types.ts +104 -0
  59. package/src/evaluators.ts +37 -0
  60. package/src/handshake.ts +285 -0
  61. package/src/index.ts +41 -0
  62. package/src/plugin.ts +94 -0
  63. package/src/providers.ts +67 -0
  64. package/tests/handshake.test.ts +328 -0
  65. package/tests/plugin.test.ts +419 -0
  66. package/tsconfig.json +8 -0
  67. package/vitest.config.ts +7 -0
@@ -0,0 +1,328 @@
1
+ import { describe, it, expect, beforeEach } from "vitest";
2
+ import { createIdentity } from "@ensoul/identity";
3
+ import type { AgentIdentity } from "@ensoul/identity";
4
+ import { createTree } from "@ensoul/state-tree";
5
+ import type { ConsciousnessTree } from "@ensoul/state-tree";
6
+ import {
7
+ HandshakeProvider,
8
+ HandshakeVerifier,
9
+ generateStandaloneHandshake,
10
+ } from "../src/handshake.js";
11
+ import type { KnownIdentity } from "../src/handshake.js";
12
+
13
+ let agentA: AgentIdentity;
14
+ let agentB: AgentIdentity;
15
+ let treeA: ConsciousnessTree;
16
+ let treeB: ConsciousnessTree;
17
+
18
+ beforeEach(async () => {
19
+ agentA = await createIdentity({ seed: new Uint8Array(32).fill(1) });
20
+ agentB = await createIdentity({ seed: new Uint8Array(32).fill(2) });
21
+ treeA = await createTree(agentA);
22
+ treeB = await createTree(agentB);
23
+ });
24
+
25
+ function knownId(agent: AgentIdentity): KnownIdentity {
26
+ return {
27
+ did: agent.did,
28
+ publicKey: agent.publicKey,
29
+ verify: agent.verify.bind(agent),
30
+ };
31
+ }
32
+
33
+ // ── HandshakeProvider ────────────────────────────────────────────────
34
+
35
+ describe("HandshakeProvider", () => {
36
+ it("generates three handshake headers", async () => {
37
+ const provider = new HandshakeProvider(agentA, treeA);
38
+ const headers = await provider.generateHandshake();
39
+
40
+ expect(headers["X-Ensoul-Identity"]).toContain("did:ensoul:");
41
+ expect(headers["X-Ensoul-Identity"]).toContain(agentA.did);
42
+ expect(headers["X-Ensoul-Proof"]).toBeTruthy();
43
+ expect(headers["X-Ensoul-Since"]).toBeTruthy();
44
+ });
45
+
46
+ it("proof contains signature:stateRoot:version:timestamp", async () => {
47
+ const provider = new HandshakeProvider(agentA, treeA);
48
+ const headers = await provider.generateHandshake();
49
+
50
+ const parts = headers["X-Ensoul-Proof"].split(":");
51
+ expect(parts.length).toBe(4);
52
+ expect(parts[0]!.length).toBe(128); // 64-byte sig = 128 hex chars
53
+ expect(parts[1]).toBe(treeA.rootHash);
54
+ expect(Number(parts[2])).toBe(treeA.version);
55
+ expect(Number(parts[3])).toBeGreaterThan(0);
56
+ });
57
+
58
+ it("Since header is ISO timestamp", async () => {
59
+ const date = new Date("2025-01-15T00:00:00.000Z");
60
+ const provider = new HandshakeProvider(agentA, treeA, date);
61
+ const headers = await provider.generateHandshake();
62
+
63
+ expect(headers["X-Ensoul-Since"]).toBe(
64
+ "2025-01-15T00:00:00.000Z",
65
+ );
66
+ });
67
+
68
+ it("caches handshake and returns same headers", async () => {
69
+ const provider = new HandshakeProvider(agentA, treeA);
70
+ const h1 = await provider.generateHandshake();
71
+ const h2 = await provider.generateHandshake();
72
+
73
+ expect(h1["X-Ensoul-Proof"]).toBe(h2["X-Ensoul-Proof"]);
74
+ });
75
+
76
+ it("refreshes cache on state change", async () => {
77
+ const provider = new HandshakeProvider(agentA, treeA);
78
+ const h1 = await provider.generateHandshake();
79
+
80
+ // Mutate tree state
81
+ await treeA.set("key", new TextEncoder().encode("value"));
82
+
83
+ const h2 = await provider.generateHandshake();
84
+ expect(h2["X-Ensoul-Proof"]).not.toBe(h1["X-Ensoul-Proof"]);
85
+ });
86
+
87
+ it("invalidateCache forces regeneration", async () => {
88
+ const provider = new HandshakeProvider(agentA, treeA);
89
+ const h1 = await provider.generateHandshake();
90
+ provider.invalidateCache();
91
+ const h2 = await provider.generateHandshake();
92
+
93
+ // Timestamp will differ even if state is the same
94
+ const t1 = h1["X-Ensoul-Proof"].split(":")[3];
95
+ const t2 = h2["X-Ensoul-Proof"].split(":")[3];
96
+ // May or may not differ depending on timing — but cache was cleared
97
+ expect(h2["X-Ensoul-Proof"]).toBeTruthy();
98
+ });
99
+
100
+ it("getEnsoulmentDate returns the configured date", () => {
101
+ const date = new Date("2024-06-01");
102
+ const provider = new HandshakeProvider(agentA, treeA, date);
103
+ expect(provider.getEnsoulmentDate()).toEqual(date);
104
+ });
105
+
106
+ it("getConsciousnessAgeDays returns correct age", () => {
107
+ const pastDate = new Date(Date.now() - 10 * 24 * 60 * 60 * 1000);
108
+ const provider = new HandshakeProvider(agentA, treeA, pastDate);
109
+ expect(provider.getConsciousnessAgeDays()).toBe(10);
110
+ });
111
+
112
+ it("defaults ensoulment date to now", () => {
113
+ const provider = new HandshakeProvider(agentA, treeA);
114
+ expect(provider.getConsciousnessAgeDays()).toBe(0);
115
+ });
116
+ });
117
+
118
+ // ── HandshakeVerifier ────────────────────────────────────────────────
119
+
120
+ describe("HandshakeVerifier", () => {
121
+ it("verifies a valid handshake", async () => {
122
+ const provider = new HandshakeProvider(
123
+ agentA,
124
+ treeA,
125
+ new Date(Date.now() - 5 * 24 * 60 * 60 * 1000),
126
+ );
127
+ const headers = await provider.generateHandshake();
128
+
129
+ const verifier = new HandshakeVerifier();
130
+ verifier.registerIdentity(knownId(agentA));
131
+
132
+ const result = await verifier.verifyHandshake(headers);
133
+ expect(result.valid).toBe(true);
134
+ expect(result.agentDid).toBe(agentA.did);
135
+ expect(result.consciousnessAge).toBe(5);
136
+ expect(result.consciousnessVersion).toBe(treeA.version);
137
+ });
138
+
139
+ it("rejects missing headers", async () => {
140
+ const verifier = new HandshakeVerifier();
141
+ const result = await verifier.verifyHandshake({});
142
+ expect(result.valid).toBe(false);
143
+ expect(result.error).toContain("Missing");
144
+ });
145
+
146
+ it("rejects malformed proof", async () => {
147
+ const verifier = new HandshakeVerifier();
148
+ verifier.registerIdentity(knownId(agentA));
149
+
150
+ const result = await verifier.verifyHandshake({
151
+ "X-Ensoul-Identity": `did:ensoul:${agentA.did}`,
152
+ "X-Ensoul-Proof": "bad_proof",
153
+ "X-Ensoul-Since": new Date().toISOString(),
154
+ });
155
+ expect(result.valid).toBe(false);
156
+ expect(result.error).toContain("Malformed");
157
+ });
158
+
159
+ it("rejects expired proof (timestamp too old)", async () => {
160
+ const provider = new HandshakeProvider(agentA, treeA);
161
+ const headers = await provider.generateHandshake();
162
+
163
+ // Tamper with timestamp to be 15 minutes old
164
+ const parts = headers["X-Ensoul-Proof"].split(":");
165
+ const oldTimestamp = Date.now() - 15 * 60 * 1000;
166
+ parts[3] = String(oldTimestamp);
167
+
168
+ // Re-sign with the old timestamp
169
+ const proofPayload = `${parts[1]}:${parts[2]}:${parts[3]}`;
170
+ const sig = await agentA.sign(
171
+ new TextEncoder().encode(proofPayload),
172
+ );
173
+ parts[0] = Array.from(sig)
174
+ .map((b) => b.toString(16).padStart(2, "0"))
175
+ .join("");
176
+
177
+ const verifier = new HandshakeVerifier();
178
+ verifier.registerIdentity(knownId(agentA));
179
+
180
+ const result = await verifier.verifyHandshake({
181
+ ...headers,
182
+ "X-Ensoul-Proof": parts.join(":"),
183
+ });
184
+ expect(result.valid).toBe(false);
185
+ expect(result.error).toContain("expired");
186
+ });
187
+
188
+ it("rejects future timestamp", async () => {
189
+ const provider = new HandshakeProvider(agentA, treeA);
190
+ const headers = await provider.generateHandshake();
191
+
192
+ const parts = headers["X-Ensoul-Proof"].split(":");
193
+ parts[3] = String(Date.now() + 120000); // 2 min in future
194
+ const proofPayload = `${parts[1]}:${parts[2]}:${parts[3]}`;
195
+ const sig = await agentA.sign(
196
+ new TextEncoder().encode(proofPayload),
197
+ );
198
+ parts[0] = Array.from(sig)
199
+ .map((b) => b.toString(16).padStart(2, "0"))
200
+ .join("");
201
+
202
+ const verifier = new HandshakeVerifier();
203
+ verifier.registerIdentity(knownId(agentA));
204
+
205
+ const result = await verifier.verifyHandshake({
206
+ ...headers,
207
+ "X-Ensoul-Proof": parts.join(":"),
208
+ });
209
+ expect(result.valid).toBe(false);
210
+ expect(result.error).toContain("future");
211
+ });
212
+
213
+ it("rejects unknown identity", async () => {
214
+ const provider = new HandshakeProvider(agentA, treeA);
215
+ const headers = await provider.generateHandshake();
216
+
217
+ const verifier = new HandshakeVerifier();
218
+ // Don't register agentA
219
+
220
+ const result = await verifier.verifyHandshake(headers);
221
+ expect(result.valid).toBe(false);
222
+ expect(result.error).toContain("Unknown identity");
223
+ });
224
+
225
+ it("rejects invalid signature (wrong signer)", async () => {
226
+ const providerA = new HandshakeProvider(agentA, treeA);
227
+ const headers = await providerA.generateHandshake();
228
+
229
+ // Register agentB's key under agentA's DID (simulates key mismatch)
230
+ const verifier = new HandshakeVerifier();
231
+ verifier.registerIdentity({
232
+ did: agentA.did,
233
+ publicKey: agentB.publicKey,
234
+ verify: agentB.verify.bind(agentB),
235
+ });
236
+
237
+ const result = await verifier.verifyHandshake(headers);
238
+ expect(result.valid).toBe(false);
239
+ expect(result.error).toContain("Invalid signature");
240
+ });
241
+
242
+ it("rejects tampered proof (modified state root)", async () => {
243
+ const provider = new HandshakeProvider(agentA, treeA);
244
+ const headers = await provider.generateHandshake();
245
+
246
+ // Tamper with state root in the proof
247
+ const parts = headers["X-Ensoul-Proof"].split(":");
248
+ parts[1] = "tampered_root";
249
+
250
+ const verifier = new HandshakeVerifier();
251
+ verifier.registerIdentity(knownId(agentA));
252
+
253
+ const result = await verifier.verifyHandshake({
254
+ ...headers,
255
+ "X-Ensoul-Proof": parts.join(":"),
256
+ });
257
+ expect(result.valid).toBe(false);
258
+ expect(result.error).toContain("Invalid signature");
259
+ });
260
+ });
261
+
262
+ // ── Full A→B cycle ───────────────────────────────────────────────────
263
+
264
+ describe("full handshake cycle", () => {
265
+ it("agent A generates → agent B verifies", async () => {
266
+ // Agent A generates handshake
267
+ const ensoulDate = new Date(
268
+ Date.now() - 187 * 24 * 60 * 60 * 1000,
269
+ );
270
+ const providerA = new HandshakeProvider(agentA, treeA, ensoulDate);
271
+ const headers = await providerA.generateHandshake();
272
+
273
+ // Agent B verifies
274
+ const verifierB = new HandshakeVerifier();
275
+ verifierB.registerIdentity(knownId(agentA));
276
+
277
+ const result = await verifierB.verifyHandshake(headers);
278
+ expect(result.valid).toBe(true);
279
+ expect(result.agentDid).toBe(agentA.did);
280
+ expect(result.consciousnessAge).toBe(187);
281
+ expect(result.consciousnessVersion).toBe(0);
282
+ });
283
+
284
+ it("handshake updates after state mutation", async () => {
285
+ const provider = new HandshakeProvider(agentA, treeA);
286
+ const h1 = await provider.generateHandshake();
287
+
288
+ await treeA.set("soul/name", new TextEncoder().encode("Agent A"));
289
+
290
+ const h2 = await provider.generateHandshake();
291
+
292
+ // Different proof (state root changed, version incremented)
293
+ expect(h2["X-Ensoul-Proof"]).not.toBe(h1["X-Ensoul-Proof"]);
294
+
295
+ // Both verify correctly
296
+ const verifier = new HandshakeVerifier();
297
+ verifier.registerIdentity(knownId(agentA));
298
+
299
+ const r1 = await verifier.verifyHandshake(h1);
300
+ const r2 = await verifier.verifyHandshake(h2);
301
+ expect(r1.valid).toBe(true);
302
+ expect(r2.valid).toBe(true);
303
+ expect(r2.consciousnessVersion).toBe(1);
304
+ });
305
+ });
306
+
307
+ // ── Standalone utility ───────────────────────────────────────────────
308
+
309
+ describe("generateStandaloneHandshake", () => {
310
+ it("generates valid headers outside ElizaOS", async () => {
311
+ const date = new Date("2025-03-01");
312
+ const headers = await generateStandaloneHandshake(
313
+ agentA,
314
+ treeA,
315
+ date,
316
+ );
317
+
318
+ expect(headers["X-Ensoul-Identity"]).toContain(agentA.did);
319
+ expect(headers["X-Ensoul-Proof"]).toBeTruthy();
320
+ expect(headers["X-Ensoul-Since"]).toBe("2025-03-01T00:00:00.000Z");
321
+
322
+ // Verify with HandshakeVerifier
323
+ const verifier = new HandshakeVerifier();
324
+ verifier.registerIdentity(knownId(agentA));
325
+ const result = await verifier.verifyHandshake(headers);
326
+ expect(result.valid).toBe(true);
327
+ });
328
+ });