@a2aletheia/sdk 0.2.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 (122) hide show
  1. package/README.md +436 -0
  2. package/dist/__tests__/pow-solver.test.d.ts +2 -0
  3. package/dist/__tests__/pow-solver.test.d.ts.map +1 -0
  4. package/dist/__tests__/pow-solver.test.js +35 -0
  5. package/dist/__tests__/pow-solver.test.js.map +1 -0
  6. package/dist/__tests__/signing.test.d.ts +2 -0
  7. package/dist/__tests__/signing.test.d.ts.map +1 -0
  8. package/dist/__tests__/signing.test.js +59 -0
  9. package/dist/__tests__/signing.test.js.map +1 -0
  10. package/dist/agent/agent-context.d.ts +26 -0
  11. package/dist/agent/agent-context.d.ts.map +1 -0
  12. package/dist/agent/agent-context.js +38 -0
  13. package/dist/agent/agent-context.js.map +1 -0
  14. package/dist/agent/agent-executor.d.ts +17 -0
  15. package/dist/agent/agent-executor.d.ts.map +1 -0
  16. package/dist/agent/agent-executor.js +91 -0
  17. package/dist/agent/agent-executor.js.map +1 -0
  18. package/dist/agent/agent-response.d.ts +63 -0
  19. package/dist/agent/agent-response.d.ts.map +1 -0
  20. package/dist/agent/agent-response.js +211 -0
  21. package/dist/agent/agent-response.js.map +1 -0
  22. package/dist/agent/aletheia-agent.d.ts +114 -0
  23. package/dist/agent/aletheia-agent.d.ts.map +1 -0
  24. package/dist/agent/aletheia-agent.js +248 -0
  25. package/dist/agent/aletheia-agent.js.map +1 -0
  26. package/dist/agent/index.d.ts +6 -0
  27. package/dist/agent/index.d.ts.map +1 -0
  28. package/dist/agent/index.js +9 -0
  29. package/dist/agent/index.js.map +1 -0
  30. package/dist/agent/types.d.ts +61 -0
  31. package/dist/agent/types.d.ts.map +1 -0
  32. package/dist/agent/types.js +2 -0
  33. package/dist/agent/types.js.map +1 -0
  34. package/dist/client.d.ts +114 -0
  35. package/dist/client.d.ts.map +1 -0
  36. package/dist/client.js +141 -0
  37. package/dist/client.js.map +1 -0
  38. package/dist/identity/agent-signer.d.ts +93 -0
  39. package/dist/identity/agent-signer.d.ts.map +1 -0
  40. package/dist/identity/agent-signer.js +288 -0
  41. package/dist/identity/agent-signer.js.map +1 -0
  42. package/dist/identity/did-resolver.d.ts +17 -0
  43. package/dist/identity/did-resolver.d.ts.map +1 -0
  44. package/dist/identity/did-resolver.js +73 -0
  45. package/dist/identity/did-resolver.js.map +1 -0
  46. package/dist/identity/index.d.ts +4 -0
  47. package/dist/identity/index.d.ts.map +1 -0
  48. package/dist/identity/index.js +4 -0
  49. package/dist/identity/index.js.map +1 -0
  50. package/dist/identity/manifest-fetcher.d.ts +12 -0
  51. package/dist/identity/manifest-fetcher.d.ts.map +1 -0
  52. package/dist/identity/manifest-fetcher.js +27 -0
  53. package/dist/identity/manifest-fetcher.js.map +1 -0
  54. package/dist/index.d.ts +12 -0
  55. package/dist/index.d.ts.map +1 -0
  56. package/dist/index.js +20 -0
  57. package/dist/index.js.map +1 -0
  58. package/dist/logger/console-logger.d.ts +20 -0
  59. package/dist/logger/console-logger.d.ts.map +1 -0
  60. package/dist/logger/console-logger.js +43 -0
  61. package/dist/logger/console-logger.js.map +1 -0
  62. package/dist/logger/event-emitter.d.ts +39 -0
  63. package/dist/logger/event-emitter.d.ts.map +1 -0
  64. package/dist/logger/event-emitter.js +50 -0
  65. package/dist/logger/event-emitter.js.map +1 -0
  66. package/dist/logger/index.d.ts +4 -0
  67. package/dist/logger/index.d.ts.map +1 -0
  68. package/dist/logger/index.js +4 -0
  69. package/dist/logger/index.js.map +1 -0
  70. package/dist/logger/noop-logger.d.ts +20 -0
  71. package/dist/logger/noop-logger.d.ts.map +1 -0
  72. package/dist/logger/noop-logger.js +19 -0
  73. package/dist/logger/noop-logger.js.map +1 -0
  74. package/dist/reputation/index.d.ts +2 -0
  75. package/dist/reputation/index.d.ts.map +1 -0
  76. package/dist/reputation/index.js +2 -0
  77. package/dist/reputation/index.js.map +1 -0
  78. package/dist/reputation/pow-solver.d.ts +20 -0
  79. package/dist/reputation/pow-solver.d.ts.map +1 -0
  80. package/dist/reputation/pow-solver.js +46 -0
  81. package/dist/reputation/pow-solver.js.map +1 -0
  82. package/dist/reputation/rating-client.d.ts +35 -0
  83. package/dist/reputation/rating-client.d.ts.map +1 -0
  84. package/dist/reputation/rating-client.js +51 -0
  85. package/dist/reputation/rating-client.js.map +1 -0
  86. package/dist/security/audit-client.d.ts +17 -0
  87. package/dist/security/audit-client.d.ts.map +1 -0
  88. package/dist/security/audit-client.js +20 -0
  89. package/dist/security/audit-client.js.map +1 -0
  90. package/dist/security/index.d.ts +2 -0
  91. package/dist/security/index.d.ts.map +1 -0
  92. package/dist/security/index.js +2 -0
  93. package/dist/security/index.js.map +1 -0
  94. package/dist/tools/calculator.d.ts +29 -0
  95. package/dist/tools/calculator.d.ts.map +1 -0
  96. package/dist/tools/calculator.js +314 -0
  97. package/dist/tools/calculator.js.map +1 -0
  98. package/dist/tools/convert-units.d.ts +49 -0
  99. package/dist/tools/convert-units.d.ts.map +1 -0
  100. package/dist/tools/convert-units.js +152 -0
  101. package/dist/tools/convert-units.js.map +1 -0
  102. package/dist/tools/get-current-time.d.ts +23 -0
  103. package/dist/tools/get-current-time.d.ts.map +1 -0
  104. package/dist/tools/get-current-time.js +28 -0
  105. package/dist/tools/get-current-time.js.map +1 -0
  106. package/dist/tools/index.d.ts +80 -0
  107. package/dist/tools/index.d.ts.map +1 -0
  108. package/dist/tools/index.js +41 -0
  109. package/dist/tools/index.js.map +1 -0
  110. package/dist/tools/uuid-generator.d.ts +21 -0
  111. package/dist/tools/uuid-generator.d.ts.map +1 -0
  112. package/dist/tools/uuid-generator.js +25 -0
  113. package/dist/tools/uuid-generator.js.map +1 -0
  114. package/dist/utils/http.d.ts +11 -0
  115. package/dist/utils/http.d.ts.map +1 -0
  116. package/dist/utils/http.js +60 -0
  117. package/dist/utils/http.js.map +1 -0
  118. package/dist/utils/signing.d.ts +43 -0
  119. package/dist/utils/signing.d.ts.map +1 -0
  120. package/dist/utils/signing.js +78 -0
  121. package/dist/utils/signing.js.map +1 -0
  122. package/package.json +66 -0
package/README.md ADDED
@@ -0,0 +1,436 @@
1
+ # @a2aletheia/sdk
2
+
3
+ SDK for integrating with the **Aletheia A2A Agent Discovery Registry**.
4
+
5
+ Aletheia is a discovery layer for [A2A (Agent-to-Agent)](https://google.github.io/A2A/) compatible agents, enabling agents to find each other by capabilities, establish trust scores, and communicate securely.
6
+
7
+ ## Installation
8
+
9
+ ```bash
10
+ npm install @a2aletheia/sdk
11
+ # or
12
+ pnpm add @a2aletheia/sdk
13
+ # or
14
+ yarn add @a2aletheia/sdk
15
+ ```
16
+
17
+ ## Quick Start
18
+
19
+ ```typescript
20
+ import { AletheiaClient } from "@a2aletheia/sdk";
21
+
22
+ // Initialize the client
23
+ const aletheia = new AletheiaClient({
24
+ apiUrl: "https://aletheia.example.com",
25
+ });
26
+
27
+ // Discover agents by capability
28
+ const agents = await aletheia.discoverAgents({
29
+ capability: "define-word",
30
+ isLive: true,
31
+ limit: 5,
32
+ });
33
+
34
+ console.log(
35
+ `Found ${agents.items.length} agents with word definition capability`,
36
+ );
37
+
38
+ // Get agent details
39
+ const agent = await aletheia.getAgent("did:web:example.com");
40
+ console.log(`Agent: ${agent.name} at ${agent.url}`);
41
+ ```
42
+
43
+ ## API Reference
44
+
45
+ ### Initialization
46
+
47
+ ```typescript
48
+ const aletheia = new AletheiaClient({
49
+ apiUrl: string; // Aletheia registry URL
50
+ chainId?: number; // Optional: blockchain chain ID for SIWE
51
+ });
52
+ ```
53
+
54
+ ### Discovery
55
+
56
+ #### `discoverAgents(params)`
57
+
58
+ Find agents by capability, trust score, or other criteria.
59
+
60
+ ```typescript
61
+ const result = await aletheia.discoverAgents({
62
+ capability?: string; // Filter by capability ID
63
+ query?: string; // Search by name/description
64
+ minTrustScore?: number; // Minimum trust score (0-100)
65
+ isLive?: boolean; // Only return live agents
66
+ limit?: number; // Max results to return
67
+ });
68
+
69
+ // Returns: { items: Agent[], total: number, page: number, limit: number }
70
+ ```
71
+
72
+ #### `searchAgents(params)`
73
+
74
+ Search agents with more filtering options.
75
+
76
+ ```typescript
77
+ const result = await aletheia.searchAgents({
78
+ query?: string;
79
+ status?: "active" | "inactive" | "suspended" | "pending";
80
+ page?: number;
81
+ limit?: number;
82
+ });
83
+ ```
84
+
85
+ ### Agent Registration
86
+
87
+ #### `registerAgent(manifestUrl, ownerAddress?)`
88
+
89
+ Register a new agent with the registry.
90
+
91
+ ```typescript
92
+ const agent = await aletheia.registerAgent(
93
+ "https://my-agent.example.com/.well-known/agent.json",
94
+ "0x1234...", // Optional: owner wallet address
95
+ );
96
+ ```
97
+
98
+ #### `getAgent(did)`
99
+
100
+ Get an agent by its DID.
101
+
102
+ ```typescript
103
+ const agent = await aletheia.getAgent("did:web:my-agent.example.com");
104
+ ```
105
+
106
+ ### Liveness
107
+
108
+ #### `checkLiveness(did)`
109
+
110
+ Check if an agent is currently live/reachable.
111
+
112
+ ```typescript
113
+ const isLive = await aletheia.checkLiveness("did:web:my-agent.example.com");
114
+ ```
115
+
116
+ ### Identity
117
+
118
+ #### `resolveDID(did)`
119
+
120
+ Resolve a DID to get its DID Document.
121
+
122
+ ```typescript
123
+ const didDocument = await aletheia.resolveDID("did:web:example.com");
124
+ ```
125
+
126
+ #### `fetchManifest(baseUrl)`
127
+
128
+ Fetch an agent's A2A manifest (Agent Card).
129
+
130
+ ```typescript
131
+ const manifest = await aletheia.fetchManifest("https://my-agent.example.com");
132
+ ```
133
+
134
+ ### Agent Message Signing & Verification
135
+
136
+ The SDK provides Ed25519 cryptographic signing for secure agent-to-agent authentication. This allows agents to prove their identity when communicating with each other.
137
+
138
+ #### Generate a Key Pair
139
+
140
+ ```typescript
141
+ import { generateAgentKeyPair } from "@a2aletheia/sdk";
142
+
143
+ const keys = await generateAgentKeyPair();
144
+ console.log(keys.didKey); // did:key:z6Mk...
145
+
146
+ // Store keys.privateKey securely! Never share it.
147
+ // Include keys.publicKeyMultibase in your DID document
148
+ ```
149
+
150
+ #### Sign Messages
151
+
152
+ ```typescript
153
+ import { signAgentMessage } from "@a2aletheia/sdk";
154
+
155
+ const signedMessage = await signAgentMessage(
156
+ { action: "book", hotelId: "123" }, // Your payload
157
+ keys.privateKey,
158
+ keys.didKey, // or your did:web
159
+ );
160
+
161
+ // signedMessage contains: { payload, signature, signer, timestamp }
162
+ ```
163
+
164
+ #### Verify Messages (with Public Key)
165
+
166
+ ```typescript
167
+ import { verifyAgentSignature } from "@a2aletheia/sdk";
168
+
169
+ const isValid = await verifyAgentSignature(signedMessage, publicKeyHex);
170
+ ```
171
+
172
+ #### Verify Messages (with DID Resolution)
173
+
174
+ The recommended way to verify agent messages - automatically resolves the signer's DID and verifies against their published public key:
175
+
176
+ ```typescript
177
+ // Using the client (recommended)
178
+ const result = await aletheia.verifyAgentMessage(signedMessage);
179
+ if (!result.valid) {
180
+ throw new Error(`Verification failed: ${result.error}`);
181
+ }
182
+ console.log(`Verified message from ${result.didDocument?.id}`);
183
+
184
+ // Or using the low-level function
185
+ import { verifyAgentMessageWithDID } from "@a2aletheia/sdk";
186
+
187
+ const didDocument = await aletheia.resolveDID(signedMessage.signer);
188
+ const isValid = await verifyAgentMessageWithDID(signedMessage, didDocument);
189
+ ```
190
+
191
+ #### End-to-End Example: Agent Authentication
192
+
193
+ ```typescript
194
+ // === AGENT SIDE (the one proving identity) ===
195
+
196
+ // 1. Generate keys (do this once, store securely)
197
+ const keys = await generateAgentKeyPair();
198
+
199
+ // 2. Publish public key in your DID document at:
200
+ // https://your-agent.com/.well-known/did.json
201
+ const didDocument = {
202
+ "@context": ["https://www.w3.org/ns/did/v1"],
203
+ id: "did:web:your-agent.com",
204
+ verificationMethod: [
205
+ {
206
+ id: "did:web:your-agent.com#key-1",
207
+ type: "Ed25519VerificationKey2020",
208
+ controller: "did:web:your-agent.com",
209
+ publicKeyMultibase: keys.publicKeyMultibase,
210
+ },
211
+ ],
212
+ authentication: ["did:web:your-agent.com#key-1"],
213
+ };
214
+
215
+ // 3. Sign outgoing messages
216
+ const signedResponse = await signAgentMessage(
217
+ { result: "booking confirmed", confirmationId: "ABC123" },
218
+ keys.privateKey,
219
+ "did:web:your-agent.com",
220
+ );
221
+
222
+ // === CLIENT SIDE (the one verifying) ===
223
+
224
+ // 4. Receive and verify the signed message
225
+ const verification = await aletheia.verifyAgentMessage(signedResponse);
226
+ if (!verification.valid) {
227
+ throw new Error("Message not from the claimed agent!");
228
+ }
229
+
230
+ // Now we know this message really came from did:web:your-agent.com
231
+ console.log("Verified response:", signedResponse.payload);
232
+ ```
233
+
234
+ ### Authentication (SIWE)
235
+
236
+ The SDK supports Sign-In with Ethereum (SIWE) for authenticated actions.
237
+
238
+ ```typescript
239
+ // Get a nonce for signing
240
+ const nonce = await aletheia.getNonce();
241
+
242
+ // Sign the message with your wallet (using viem, ethers, etc.)
243
+ const message = createSiweMessage({ nonce, address, ... });
244
+ const signature = await wallet.signMessage(message);
245
+
246
+ // Verify and get session token
247
+ const { sessionToken } = await aletheia.verifySiwe(message, signature);
248
+
249
+ // Set auth token for subsequent requests
250
+ aletheia.setAuthToken(sessionToken);
251
+ ```
252
+
253
+ ## Types & Exports
254
+
255
+ The SDK exports types and utilities:
256
+
257
+ ```typescript
258
+ // Main client
259
+ import { AletheiaClient } from "@a2aletheia/sdk";
260
+
261
+ // Identity utilities
262
+ import {
263
+ DIDResolver,
264
+ ManifestFetcher,
265
+ generateAgentKeyPair,
266
+ signAgentMessage,
267
+ verifyAgentSignature,
268
+ verifyAgentMessageWithDID,
269
+ } from "@a2aletheia/sdk";
270
+
271
+ // Types
272
+ import type {
273
+ AletheiaClientConfig,
274
+ AgentKeyPair,
275
+ SignedMessage,
276
+ } from "@a2aletheia/sdk";
277
+
278
+ // Re-exported from @a2aletheia/base-types
279
+ import type {
280
+ Agent,
281
+ AgentManifest,
282
+ DID,
283
+ DIDDocument,
284
+ TrustScore,
285
+ // ... etc
286
+ } from "@a2aletheia/base-types";
287
+ ```
288
+
289
+ ## LLM Agent Tools
290
+
291
+ The SDK ships four deterministic utility tools that LLM agents commonly need. Each tool is exported as an **OpenAI-compatible tool definition** with an `execute` function, making them directly usable with LangChain, Vercel AI SDK, CrewAI, or any OpenAI function-calling compatible framework.
292
+
293
+ ### Quick Start — Plug into your agent framework
294
+
295
+ ```typescript
296
+ import {
297
+ aletheiaToolDefinitions,
298
+ aletheiaToolExecutors,
299
+ } from "@a2aletheia/sdk";
300
+
301
+ // Pass definitions to your LLM (OpenAI function calling format)
302
+ const response = await openai.chat.completions.create({
303
+ model: "gpt-4",
304
+ tools: aletheiaToolDefinitions.map((t) => ({
305
+ type: t.type,
306
+ function: t.function,
307
+ })),
308
+ messages,
309
+ });
310
+
311
+ // Route tool calls back to the matching executor
312
+ for (const call of response.choices[0].message.tool_calls ?? []) {
313
+ const result = aletheiaToolExecutors[call.function.name]?.(
314
+ JSON.parse(call.function.arguments),
315
+ );
316
+ console.log(result);
317
+ }
318
+ ```
319
+
320
+ ### Available Tools
321
+
322
+ | Tool | Name | Description |
323
+ | ------------------------------ | ------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
324
+ | `calculatorToolDefinition` | `calculator` | Safe math expression evaluator (recursive descent parser, no eval). Supports `+ - * / % **`, parentheses, functions (`sqrt`, `abs`, `ceil`, `floor`, `round`, `min`, `max`, `log`, `sin`, `cos`, `tan`), constants (`PI`, `E`). |
325
+ | `getCurrentTimeToolDefinition` | `get_current_time` | Returns current time in ISO 8601, Unix timestamp (s/ms), and UTC string. |
326
+ | `convertUnitsToolDefinition` | `convert_units` | Convert between units in the same category: crypto (wei/gwei/ether with bigint precision via viem), time (seconds/minutes/hours/days), data (bytes/KB/MB/GB/TB). |
327
+ | `uuidGeneratorToolDefinition` | `uuid_generator` | Generate a random UUID v4 via `crypto.randomUUID()`. |
328
+
329
+ ### Using individual tools directly
330
+
331
+ ```typescript
332
+ import {
333
+ calculate,
334
+ getCurrentTime,
335
+ convertUnits,
336
+ generateUUID,
337
+ } from "@a2aletheia/sdk";
338
+
339
+ calculate("2 ** 10 + sqrt(144)");
340
+ // { tool: "calculator", input: "2 ** 10 + sqrt(144)", result: 1036 }
341
+
342
+ getCurrentTime();
343
+ // { tool: "get_current_time", iso8601: "2025-...", unixTimestamp: 1735..., ... }
344
+
345
+ convertUnits("1.5", "ether", "wei");
346
+ // { tool: "convert_units", result: "1500000000000000000", category: "crypto", ... }
347
+
348
+ generateUUID();
349
+ // { tool: "uuid_generator", uuid: "550e8400-...", version: 4 }
350
+ ```
351
+
352
+ ## Agent-to-Agent Communication
353
+
354
+ `@a2aletheia/sdk` covers agent **hosting** (receiving inbound A2A messages) and **registry access** (discovery, registration, reputation). To initiate **outbound** communication with other agents on the Aletheia network — with trust-verified discovery, liveness gating, and trust score thresholds — use [`@a2aletheia/a2a`](https://www.npmjs.com/package/@a2aletheia/a2a):
355
+
356
+ ```bash
357
+ pnpm add @a2aletheia/a2a
358
+ ```
359
+
360
+ | Capability | `@a2aletheia/sdk` | `@a2aletheia/a2a` |
361
+ | --------------------------------------------- | ----------------- | --------------------------- |
362
+ | Host an A2A agent (receive messages) | `AletheiaAgent` | `PeerAgent` |
363
+ | Registry client (discover, register, ratings) | `AletheiaClient` | -- |
364
+ | Send messages to other agents | -- | `AletheiaA2A` / `PeerAgent` |
365
+ | Trust pipeline (DID + liveness + score gates) | -- | Built-in |
366
+
367
+ ### Inbound-only agent (SDK)
368
+
369
+ ```typescript
370
+ import { AletheiaAgent } from "@a2aletheia/sdk/agent";
371
+
372
+ const agent = new AletheiaAgent({
373
+ name: "MyAgent",
374
+ version: "1.0.0",
375
+ url: "https://my-agent.example.com",
376
+ description: "Handles inbound requests",
377
+ skills: [
378
+ {
379
+ id: "summarize",
380
+ name: "Summarize",
381
+ description: "Summarize text",
382
+ tags: [],
383
+ },
384
+ ],
385
+ });
386
+
387
+ agent.handle(async (context, response) => {
388
+ response.text(`You said: ${context.textContent}`);
389
+ });
390
+
391
+ await agent.start(4000);
392
+ ```
393
+
394
+ ### Full-duplex peer (A2A) — receive AND send
395
+
396
+ ```typescript
397
+ import { PeerAgent } from "@a2aletheia/a2a";
398
+
399
+ const peer = new PeerAgent({
400
+ registryUrl: "https://registry.aletheia.dev",
401
+ name: "MyAgent",
402
+ version: "1.0.0",
403
+ url: "https://my-agent.example.com",
404
+ description: "Can reach out to other agents",
405
+ skills: [
406
+ {
407
+ id: "orchestrate",
408
+ name: "Orchestrate",
409
+ description: "Orchestrate tasks",
410
+ tags: [],
411
+ },
412
+ ],
413
+ minTrustScore: 0.5,
414
+ });
415
+
416
+ peer.handle(async (context, response) => {
417
+ // Delegate to another agent on the network
418
+ const result = await peer.sendByCapability("translate", context.textContent);
419
+ response.text(`Translated via ${result.agentName}`);
420
+ });
421
+
422
+ await peer.start(4000);
423
+ ```
424
+
425
+ See the [`@a2aletheia/a2a` README](https://www.npmjs.com/package/@a2aletheia/a2a) for the full API reference.
426
+
427
+ ## Roadmap
428
+
429
+ - **Phase 1** ✅ Identity & Discovery
430
+ - **Phase 2** ✅ Reputation & Trust Scores
431
+ - **Phase 3** 🚧 Security Audits
432
+ - **Phase 4** 🚧 Payments & Staking
433
+
434
+ ## License
435
+
436
+ MIT
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=pow-solver.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"pow-solver.test.d.ts","sourceRoot":"","sources":["../../src/__tests__/pow-solver.test.ts"],"names":[],"mappings":""}
@@ -0,0 +1,35 @@
1
+ import { describe, it, expect } from "vitest";
2
+ import { solvePoWChallenge } from "../reputation/pow-solver.js";
3
+ describe("PoW Solver", () => {
4
+ it("solves a challenge with low difficulty", async () => {
5
+ const result = await solvePoWChallenge("test-challenge", 8);
6
+ expect(result.nonce).toBeDefined();
7
+ expect(result.hash).toMatch(/^0x[0-9a-f]{64}$/);
8
+ // Verify 8 leading zero bits (first 2 hex chars must be "00")
9
+ const hex = result.hash.slice(2);
10
+ expect(hex.slice(0, 2)).toBe("00");
11
+ });
12
+ it("returns valid hash format", async () => {
13
+ const result = await solvePoWChallenge("another-challenge", 4);
14
+ expect(result.hash).toMatch(/^0x[0-9a-f]{64}$/);
15
+ expect(typeof result.nonce).toBe("string");
16
+ });
17
+ it("hash meets the specified difficulty", async () => {
18
+ const difficulty = 12;
19
+ const result = await solvePoWChallenge("difficulty-test", difficulty);
20
+ // 12 bits = first 3 hex chars must represent values with 12 leading zero bits
21
+ // That means first byte (2 hex chars) = 0x00, second byte top 4 bits = 0
22
+ const hex = result.hash.slice(2);
23
+ const firstByte = parseInt(hex.slice(0, 2), 16);
24
+ const secondByte = parseInt(hex.slice(2, 4), 16);
25
+ expect(firstByte).toBe(0);
26
+ expect(secondByte >> 4).toBe(0); // top 4 bits of second byte = 0
27
+ });
28
+ it("different challenges produce different results", async () => {
29
+ const result1 = await solvePoWChallenge("challenge-a", 4);
30
+ const result2 = await solvePoWChallenge("challenge-b", 4);
31
+ // Hashes should differ (extremely unlikely to match)
32
+ expect(result1.hash).not.toBe(result2.hash);
33
+ });
34
+ });
35
+ //# sourceMappingURL=pow-solver.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"pow-solver.test.js","sourceRoot":"","sources":["../../src/__tests__/pow-solver.test.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,EAAE,EAAE,MAAM,EAAE,MAAM,QAAQ,CAAC;AAC9C,OAAO,EAAE,iBAAiB,EAAE,MAAM,6BAA6B,CAAC;AAEhE,QAAQ,CAAC,YAAY,EAAE,GAAG,EAAE;IAC1B,EAAE,CAAC,wCAAwC,EAAE,KAAK,IAAI,EAAE;QACtD,MAAM,MAAM,GAAG,MAAM,iBAAiB,CAAC,gBAAgB,EAAE,CAAC,CAAC,CAAC;QAE5D,MAAM,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,WAAW,EAAE,CAAC;QACnC,MAAM,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC,OAAO,CAAC,kBAAkB,CAAC,CAAC;QAEhD,8DAA8D;QAC9D,MAAM,GAAG,GAAG,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC;QACjC,MAAM,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;IACrC,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,2BAA2B,EAAE,KAAK,IAAI,EAAE;QACzC,MAAM,MAAM,GAAG,MAAM,iBAAiB,CAAC,mBAAmB,EAAE,CAAC,CAAC,CAAC;QAE/D,MAAM,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC,OAAO,CAAC,kBAAkB,CAAC,CAAC;QAChD,MAAM,CAAC,OAAO,MAAM,CAAC,KAAK,CAAC,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC;IAC7C,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,qCAAqC,EAAE,KAAK,IAAI,EAAE;QACnD,MAAM,UAAU,GAAG,EAAE,CAAC;QACtB,MAAM,MAAM,GAAG,MAAM,iBAAiB,CAAC,iBAAiB,EAAE,UAAU,CAAC,CAAC;QAEtE,8EAA8E;QAC9E,yEAAyE;QACzE,MAAM,GAAG,GAAG,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC;QACjC,MAAM,SAAS,GAAG,QAAQ,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC;QAChD,MAAM,UAAU,GAAG,QAAQ,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC;QAEjD,MAAM,CAAC,SAAS,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;QAC1B,MAAM,CAAC,UAAU,IAAI,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,CAAC,gCAAgC;IACnE,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,gDAAgD,EAAE,KAAK,IAAI,EAAE;QAC9D,MAAM,OAAO,GAAG,MAAM,iBAAiB,CAAC,aAAa,EAAE,CAAC,CAAC,CAAC;QAC1D,MAAM,OAAO,GAAG,MAAM,iBAAiB,CAAC,aAAa,EAAE,CAAC,CAAC,CAAC;QAE1D,qDAAqD;QACrD,MAAM,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC,GAAG,CAAC,IAAI,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC;IAC9C,CAAC,CAAC,CAAC;AACL,CAAC,CAAC,CAAC"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=signing.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"signing.test.d.ts","sourceRoot":"","sources":["../../src/__tests__/signing.test.ts"],"names":[],"mappings":""}
@@ -0,0 +1,59 @@
1
+ import { describe, it, expect } from "vitest";
2
+ import { generatePrivateKey } from "viem/accounts";
3
+ import { signRatingPayload, verifyRatingSignature, hashRatingPayload, } from "../utils/signing.js";
4
+ const TEST_PAYLOAD = {
5
+ fromDid: "did:pkh:eip155:1:0x1234567890abcdef1234567890abcdef12345678",
6
+ toDid: "did:web:agent.example.com",
7
+ score: 4,
8
+ chainId: 1,
9
+ powNonce: "ff",
10
+ powHash: "0x0000abcdef1234567890abcdef1234567890abcdef1234567890abcdef1234",
11
+ challengeId: "test-challenge-id",
12
+ };
13
+ describe("EIP-712 Rating Signing", () => {
14
+ it("signRatingPayload + verifyRatingSignature round-trip", async () => {
15
+ const privateKey = generatePrivateKey();
16
+ const signed = await signRatingPayload(TEST_PAYLOAD, privateKey);
17
+ expect(signed.payload).toBeDefined();
18
+ expect(signed.signature).toMatch(/^0x[0-9a-fA-F]+$/);
19
+ expect(signed.signer).toMatch(/^0x[0-9a-fA-F]{40}$/);
20
+ const valid = await verifyRatingSignature(signed.payload, signed.signature, signed.signer);
21
+ expect(valid).toBe(true);
22
+ });
23
+ it("verifyRatingSignature rejects wrong signer", async () => {
24
+ const privateKey = generatePrivateKey();
25
+ const signed = await signRatingPayload(TEST_PAYLOAD, privateKey);
26
+ // Use a different address as expected signer
27
+ const wrongSigner = "0x0000000000000000000000000000000000000001";
28
+ const valid = await verifyRatingSignature(signed.payload, signed.signature, wrongSigner);
29
+ expect(valid).toBe(false);
30
+ });
31
+ it("hashRatingPayload returns consistent hash", () => {
32
+ const hash1 = hashRatingPayload(TEST_PAYLOAD);
33
+ const hash2 = hashRatingPayload(TEST_PAYLOAD);
34
+ expect(hash1).toBe(hash2);
35
+ expect(hash1).toMatch(/^0x[0-9a-fA-F]{64}$/);
36
+ });
37
+ it("hashRatingPayload changes with different inputs", () => {
38
+ const hash1 = hashRatingPayload(TEST_PAYLOAD);
39
+ const hash2 = hashRatingPayload({ ...TEST_PAYLOAD, score: 3 });
40
+ expect(hash1).not.toBe(hash2);
41
+ });
42
+ it("includes chainId in signature verification", async () => {
43
+ const privateKey = generatePrivateKey();
44
+ const signed = await signRatingPayload(TEST_PAYLOAD, privateKey);
45
+ // Tamper with chainId in the payload
46
+ const parsed = JSON.parse(signed.payload);
47
+ parsed.chainId = 999;
48
+ const tampered = JSON.stringify(parsed);
49
+ const valid = await verifyRatingSignature(tampered, signed.signature, signed.signer);
50
+ expect(valid).toBe(false);
51
+ });
52
+ it("signature changes with different chainId", async () => {
53
+ const privateKey = generatePrivateKey();
54
+ const signed1 = await signRatingPayload(TEST_PAYLOAD, privateKey);
55
+ const signed2 = await signRatingPayload({ ...TEST_PAYLOAD, chainId: 137 }, privateKey);
56
+ expect(signed1.signature).not.toBe(signed2.signature);
57
+ });
58
+ });
59
+ //# sourceMappingURL=signing.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"signing.test.js","sourceRoot":"","sources":["../../src/__tests__/signing.test.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,26 @@
1
+ import type { Message, Task, Part } from "@a2a-js/sdk";
2
+ import { RequestContext } from "@a2a-js/sdk/server";
3
+ import type { AgentContext } from "./types.js";
4
+ /**
5
+ * Extended RequestContext with helper methods for accessing message content.
6
+ */
7
+ export declare class AgentContextImpl extends RequestContext implements AgentContext {
8
+ constructor(userMessage: Message, taskId: string, contextId: string, task?: Task, referenceTasks?: Task[]);
9
+ /**
10
+ * Access the raw message parts.
11
+ */
12
+ get parts(): Part[];
13
+ /**
14
+ * Extracts all text from message parts, joined with newlines.
15
+ */
16
+ get textContent(): string;
17
+ /**
18
+ * Extracts the first data part's data, or null if none.
19
+ */
20
+ get dataContent(): Record<string, unknown> | null;
21
+ /**
22
+ * Create an AgentContext from a RequestContext.
23
+ */
24
+ static from(context: RequestContext): AgentContextImpl;
25
+ }
26
+ //# sourceMappingURL=agent-context.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"agent-context.d.ts","sourceRoot":"","sources":["../../src/agent/agent-context.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,OAAO,EAAE,IAAI,EAAE,IAAI,EAAE,MAAM,aAAa,CAAC;AACvD,OAAO,EAAE,cAAc,EAAE,MAAM,oBAAoB,CAAC;AACpD,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,YAAY,CAAC;AAE/C;;GAEG;AACH,qBAAa,gBACX,SAAQ,cACR,YAAW,YAAY;gBAGrB,WAAW,EAAE,OAAO,EACpB,MAAM,EAAE,MAAM,EACd,SAAS,EAAE,MAAM,EACjB,IAAI,CAAC,EAAE,IAAI,EACX,cAAc,CAAC,EAAE,IAAI,EAAE;IAKzB;;OAEG;IACH,IAAI,KAAK,IAAI,IAAI,EAAE,CAElB;IAED;;OAEG;IACH,IAAI,WAAW,IAAI,MAAM,CAKxB;IAED;;OAEG;IACH,IAAI,WAAW,IAAI,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,IAAI,CAKhD;IAED;;OAEG;IACH,MAAM,CAAC,IAAI,CAAC,OAAO,EAAE,cAAc,GAAG,gBAAgB;CASvD"}
@@ -0,0 +1,38 @@
1
+ import { RequestContext } from "@a2a-js/sdk/server";
2
+ /**
3
+ * Extended RequestContext with helper methods for accessing message content.
4
+ */
5
+ export class AgentContextImpl extends RequestContext {
6
+ constructor(userMessage, taskId, contextId, task, referenceTasks) {
7
+ super(userMessage, taskId, contextId, task, referenceTasks);
8
+ }
9
+ /**
10
+ * Access the raw message parts.
11
+ */
12
+ get parts() {
13
+ return this.userMessage.parts;
14
+ }
15
+ /**
16
+ * Extracts all text from message parts, joined with newlines.
17
+ */
18
+ get textContent() {
19
+ return this.userMessage.parts
20
+ .filter((part) => part.kind === "text")
21
+ .map((part) => part.text)
22
+ .join("\n");
23
+ }
24
+ /**
25
+ * Extracts the first data part's data, or null if none.
26
+ */
27
+ get dataContent() {
28
+ const dataPart = this.userMessage.parts.find((part) => part.kind === "data");
29
+ return dataPart?.data ?? null;
30
+ }
31
+ /**
32
+ * Create an AgentContext from a RequestContext.
33
+ */
34
+ static from(context) {
35
+ return new AgentContextImpl(context.userMessage, context.taskId, context.contextId, context.task, context.referenceTasks);
36
+ }
37
+ }
38
+ //# sourceMappingURL=agent-context.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"agent-context.js","sourceRoot":"","sources":["../../src/agent/agent-context.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,cAAc,EAAE,MAAM,oBAAoB,CAAC;AAGpD;;GAEG;AACH,MAAM,OAAO,gBACX,SAAQ,cAAc;IAGtB,YACE,WAAoB,EACpB,MAAc,EACd,SAAiB,EACjB,IAAW,EACX,cAAuB;QAEvB,KAAK,CAAC,WAAW,EAAE,MAAM,EAAE,SAAS,EAAE,IAAI,EAAE,cAAc,CAAC,CAAC;IAC9D,CAAC;IAED;;OAEG;IACH,IAAI,KAAK;QACP,OAAO,IAAI,CAAC,WAAW,CAAC,KAAK,CAAC;IAChC,CAAC;IAED;;OAEG;IACH,IAAI,WAAW;QACb,OAAO,IAAI,CAAC,WAAW,CAAC,KAAK;aAC1B,MAAM,CAAC,CAAC,IAAI,EAAmC,EAAE,CAAC,IAAI,CAAC,IAAI,KAAK,MAAM,CAAC;aACvE,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,CAAC,IAAI,CAAC;aACxB,IAAI,CAAC,IAAI,CAAC,CAAC;IAChB,CAAC;IAED;;OAEG;IACH,IAAI,WAAW;QACb,MAAM,QAAQ,GAAG,IAAI,CAAC,WAAW,CAAC,KAAK,CAAC,IAAI,CAC1C,CAAC,IAAI,EAAmC,EAAE,CAAC,IAAI,CAAC,IAAI,KAAK,MAAM,CAChE,CAAC;QACF,OAAO,QAAQ,EAAE,IAAI,IAAI,IAAI,CAAC;IAChC,CAAC;IAED;;OAEG;IACH,MAAM,CAAC,IAAI,CAAC,OAAuB;QACjC,OAAO,IAAI,gBAAgB,CACzB,OAAO,CAAC,WAAW,EACnB,OAAO,CAAC,MAAM,EACd,OAAO,CAAC,SAAS,EACjB,OAAO,CAAC,IAAI,EACZ,OAAO,CAAC,cAAc,CACvB,CAAC;IACJ,CAAC;CACF"}
@@ -0,0 +1,17 @@
1
+ import type { AgentExecutor, ExecutionEventBus, RequestContext } from "@a2a-js/sdk/server";
2
+ import type { AletheiaLogger } from "@a2aletheia/types";
3
+ import type { AgentHandler, CancelHandler } from "./types.js";
4
+ import type { EventEmitter } from "../logger/event-emitter.js";
5
+ /**
6
+ * Bridges the functional handler pattern to the @a2a-js AgentExecutor interface.
7
+ */
8
+ export declare class DelegatingAgentExecutor implements AgentExecutor {
9
+ private getHandler;
10
+ private getCancelHandler;
11
+ private readonly logger;
12
+ private readonly events;
13
+ constructor(getHandler: () => AgentHandler | undefined, getCancelHandler: () => CancelHandler | undefined, logger: AletheiaLogger, events: EventEmitter);
14
+ execute(context: RequestContext, eventBus: ExecutionEventBus): Promise<void>;
15
+ cancelTask(taskId: string, eventBus: ExecutionEventBus): Promise<void>;
16
+ }
17
+ //# sourceMappingURL=agent-executor.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"agent-executor.d.ts","sourceRoot":"","sources":["../../src/agent/agent-executor.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,aAAa,EACb,iBAAiB,EACjB,cAAc,EACf,MAAM,oBAAoB,CAAC;AAC5B,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,mBAAmB,CAAC;AACxD,OAAO,KAAK,EAAE,YAAY,EAAE,aAAa,EAAE,MAAM,YAAY,CAAC;AAC9D,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,4BAA4B,CAAC;AAI/D;;GAEG;AACH,qBAAa,uBAAwB,YAAW,aAAa;IAEzD,OAAO,CAAC,UAAU;IAClB,OAAO,CAAC,gBAAgB;IACxB,OAAO,CAAC,QAAQ,CAAC,MAAM;IACvB,OAAO,CAAC,QAAQ,CAAC,MAAM;gBAHf,UAAU,EAAE,MAAM,YAAY,GAAG,SAAS,EAC1C,gBAAgB,EAAE,MAAM,aAAa,GAAG,SAAS,EACxC,MAAM,EAAE,cAAc,EACtB,MAAM,EAAE,YAAY;IAGjC,OAAO,CACX,OAAO,EAAE,cAAc,EACvB,QAAQ,EAAE,iBAAiB,GAC1B,OAAO,CAAC,IAAI,CAAC;IA2DV,UAAU,CAAC,MAAM,EAAE,MAAM,EAAE,QAAQ,EAAE,iBAAiB,GAAG,OAAO,CAAC,IAAI,CAAC;CAwB7E"}