@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.
- package/.turbo/turbo-build.log +4 -0
- package/.turbo/turbo-lint.log +5 -0
- package/.turbo/turbo-test.log +15 -0
- package/SECURITY.md +37 -0
- package/coverage/actions.ts.html +514 -0
- package/coverage/adapter.ts.html +298 -0
- package/coverage/base.css +224 -0
- package/coverage/block-navigation.js +87 -0
- package/coverage/clover.xml +495 -0
- package/coverage/coverage-final.json +9 -0
- package/coverage/elizaos-types.ts.html +397 -0
- package/coverage/evaluators.ts.html +196 -0
- package/coverage/favicon.png +0 -0
- package/coverage/handshake.ts.html +940 -0
- package/coverage/index.html +221 -0
- package/coverage/index.ts.html +208 -0
- package/coverage/plugin.ts.html +367 -0
- package/coverage/prettify.css +1 -0
- package/coverage/prettify.js +2 -0
- package/coverage/providers.ts.html +286 -0
- package/coverage/sort-arrow-sprite.png +0 -0
- package/coverage/sorter.js +210 -0
- package/dist/actions.d.ts +24 -0
- package/dist/actions.d.ts.map +1 -0
- package/dist/actions.js +108 -0
- package/dist/actions.js.map +1 -0
- package/dist/adapter.d.ts +18 -0
- package/dist/adapter.d.ts.map +1 -0
- package/dist/adapter.js +55 -0
- package/dist/adapter.js.map +1 -0
- package/dist/elizaos-types.d.ts +81 -0
- package/dist/elizaos-types.d.ts.map +1 -0
- package/dist/elizaos-types.js +7 -0
- package/dist/elizaos-types.js.map +1 -0
- package/dist/evaluators.d.ts +8 -0
- package/dist/evaluators.d.ts.map +1 -0
- package/dist/evaluators.js +24 -0
- package/dist/evaluators.js.map +1 -0
- package/dist/handshake.d.ts +78 -0
- package/dist/handshake.d.ts.map +1 -0
- package/dist/handshake.js +195 -0
- package/dist/handshake.js.map +1 -0
- package/dist/index.d.ts +10 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +7 -0
- package/dist/index.js.map +1 -0
- package/dist/plugin.d.ts +24 -0
- package/dist/plugin.d.ts.map +1 -0
- package/dist/plugin.js +53 -0
- package/dist/plugin.js.map +1 -0
- package/dist/providers.d.ts +12 -0
- package/dist/providers.d.ts.map +1 -0
- package/dist/providers.js +49 -0
- package/dist/providers.js.map +1 -0
- package/package.json +25 -0
- package/src/actions.ts +143 -0
- package/src/adapter.ts +71 -0
- package/src/elizaos-types.ts +104 -0
- package/src/evaluators.ts +37 -0
- package/src/handshake.ts +285 -0
- package/src/index.ts +41 -0
- package/src/plugin.ts +94 -0
- package/src/providers.ts +67 -0
- package/tests/handshake.test.ts +328 -0
- package/tests/plugin.test.ts +419 -0
- package/tsconfig.json +8 -0
- 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
|
+
});
|