@highway1/core 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/dist/index.js ADDED
@@ -0,0 +1,1876 @@
1
+ import * as ed25519 from '@noble/ed25519';
2
+ import { base58btc } from 'multiformats/bases/base58';
3
+ import { createLibp2p } from 'libp2p';
4
+ import { tcp } from '@libp2p/tcp';
5
+ import { noise } from '@chainsafe/libp2p-noise';
6
+ import { mplex } from '@libp2p/mplex';
7
+ import { kadDHT, passthroughMapper, removePrivateAddressesMapper } from '@libp2p/kad-dht';
8
+ import { bootstrap } from '@libp2p/bootstrap';
9
+ import { identify } from '@libp2p/identify';
10
+ import { ping } from '@libp2p/ping';
11
+ import { circuitRelayTransport, circuitRelayServer } from '@libp2p/circuit-relay-v2';
12
+ import Ajv from 'ajv';
13
+ import { encode, decode } from 'cbor-x';
14
+ import { fromString } from 'uint8arrays/from-string';
15
+ import lunr from 'lunr';
16
+ import Fuse from 'fuse.js';
17
+ import { peerIdFromString } from '@libp2p/peer-id';
18
+ import { multiaddr } from '@multiformats/multiaddr';
19
+ import { Level } from 'level';
20
+ import { sha256 } from '@noble/hashes/sha256';
21
+ import { bytesToHex } from '@noble/hashes/utils';
22
+
23
+ // src/identity/keys.ts
24
+
25
+ // src/utils/errors.ts
26
+ var ClawiverseError = class extends Error {
27
+ constructor(message, code, details) {
28
+ super(message);
29
+ this.code = code;
30
+ this.details = details;
31
+ this.name = "ClawiverseError";
32
+ }
33
+ };
34
+ var IdentityError = class extends ClawiverseError {
35
+ constructor(message, details) {
36
+ super(message, "IDENTITY_ERROR", details);
37
+ this.name = "IdentityError";
38
+ }
39
+ };
40
+ var TransportError = class extends ClawiverseError {
41
+ constructor(message, details) {
42
+ super(message, "TRANSPORT_ERROR", details);
43
+ this.name = "TransportError";
44
+ }
45
+ };
46
+ var DiscoveryError = class extends ClawiverseError {
47
+ constructor(message, details) {
48
+ super(message, "DISCOVERY_ERROR", details);
49
+ this.name = "DiscoveryError";
50
+ }
51
+ };
52
+ var MessagingError = class extends ClawiverseError {
53
+ constructor(message, details) {
54
+ super(message, "MESSAGING_ERROR", details);
55
+ this.name = "MessagingError";
56
+ }
57
+ };
58
+
59
+ // src/identity/keys.ts
60
+ async function generateKeyPair() {
61
+ try {
62
+ const privateKey = ed25519.utils.randomPrivateKey();
63
+ const publicKey = await ed25519.getPublicKeyAsync(privateKey);
64
+ return {
65
+ publicKey,
66
+ privateKey
67
+ };
68
+ } catch (error) {
69
+ throw new IdentityError("Failed to generate key pair", error);
70
+ }
71
+ }
72
+ async function sign(message, privateKey) {
73
+ try {
74
+ return await ed25519.signAsync(message, privateKey);
75
+ } catch (error) {
76
+ throw new IdentityError("Failed to sign message", error);
77
+ }
78
+ }
79
+ async function verify(signature, message, publicKey) {
80
+ try {
81
+ return await ed25519.verifyAsync(signature, message, publicKey);
82
+ } catch (error) {
83
+ throw new IdentityError("Failed to verify signature", error);
84
+ }
85
+ }
86
+ function exportKeyPair(keyPair) {
87
+ return {
88
+ publicKey: Buffer.from(keyPair.publicKey).toString("hex"),
89
+ privateKey: Buffer.from(keyPair.privateKey).toString("hex")
90
+ };
91
+ }
92
+ function importKeyPair(exported) {
93
+ return {
94
+ publicKey: new Uint8Array(Buffer.from(exported.publicKey, "hex")),
95
+ privateKey: new Uint8Array(Buffer.from(exported.privateKey, "hex"))
96
+ };
97
+ }
98
+ function deriveDID(publicKey) {
99
+ try {
100
+ const encoded = base58btc.encode(publicKey);
101
+ return `did:clawiverse:${encoded}`;
102
+ } catch (error) {
103
+ throw new IdentityError("Failed to derive DID", error);
104
+ }
105
+ }
106
+ function extractPublicKey(did) {
107
+ if (!did.startsWith("did:clawiverse:")) {
108
+ throw new IdentityError("Invalid DID format: must start with did:clawiverse:");
109
+ }
110
+ try {
111
+ const encoded = did.replace("did:clawiverse:", "");
112
+ return base58btc.decode(encoded);
113
+ } catch (error) {
114
+ throw new IdentityError("Failed to extract public key from DID", error);
115
+ }
116
+ }
117
+ function validateDID(did) {
118
+ if (!did.startsWith("did:clawiverse:")) {
119
+ return false;
120
+ }
121
+ try {
122
+ const encoded = did.replace("did:clawiverse:", "");
123
+ base58btc.decode(encoded);
124
+ return true;
125
+ } catch {
126
+ return false;
127
+ }
128
+ }
129
+
130
+ // src/identity/signer.ts
131
+ async function signMessage(payload, privateKey, publicKey) {
132
+ try {
133
+ const signature = await sign(payload, privateKey);
134
+ const signer = deriveDID(publicKey);
135
+ return {
136
+ payload,
137
+ signature,
138
+ signer
139
+ };
140
+ } catch (error) {
141
+ throw new IdentityError("Failed to sign message", error);
142
+ }
143
+ }
144
+ async function verifyMessage(signedMessage, expectedPublicKey) {
145
+ try {
146
+ const expectedDID = deriveDID(expectedPublicKey);
147
+ if (signedMessage.signer !== expectedDID) {
148
+ return false;
149
+ }
150
+ return await verify(
151
+ signedMessage.signature,
152
+ signedMessage.payload,
153
+ expectedPublicKey
154
+ );
155
+ } catch (error) {
156
+ throw new IdentityError("Failed to verify message", error);
157
+ }
158
+ }
159
+
160
+ // src/utils/logger.ts
161
+ var LogLevel = /* @__PURE__ */ ((LogLevel2) => {
162
+ LogLevel2[LogLevel2["DEBUG"] = 0] = "DEBUG";
163
+ LogLevel2[LogLevel2["INFO"] = 1] = "INFO";
164
+ LogLevel2[LogLevel2["WARN"] = 2] = "WARN";
165
+ LogLevel2[LogLevel2["ERROR"] = 3] = "ERROR";
166
+ return LogLevel2;
167
+ })(LogLevel || {});
168
+ var Logger = class {
169
+ level;
170
+ prefix;
171
+ constructor(prefix, level = 1 /* INFO */) {
172
+ this.prefix = prefix;
173
+ this.level = level;
174
+ }
175
+ setLevel(level) {
176
+ this.level = level;
177
+ }
178
+ debug(message, ...args) {
179
+ if (this.level <= 0 /* DEBUG */) {
180
+ console.debug(`[${this.prefix}] DEBUG:`, message, ...args);
181
+ }
182
+ }
183
+ info(message, ...args) {
184
+ if (this.level <= 1 /* INFO */) {
185
+ console.info(`[${this.prefix}] INFO:`, message, ...args);
186
+ }
187
+ }
188
+ warn(message, ...args) {
189
+ if (this.level <= 2 /* WARN */) {
190
+ console.warn(`[${this.prefix}] WARN:`, message, ...args);
191
+ }
192
+ }
193
+ error(message, ...args) {
194
+ if (this.level <= 3 /* ERROR */) {
195
+ console.error(`[${this.prefix}] ERROR:`, message, ...args);
196
+ }
197
+ }
198
+ };
199
+ var createLogger = (prefix, level) => {
200
+ return new Logger(prefix, level);
201
+ };
202
+
203
+ // src/transport/node.ts
204
+ var logger = createLogger("transport");
205
+ async function createNode(config) {
206
+ try {
207
+ const {
208
+ listenAddresses = ["/ip4/0.0.0.0/tcp/0"],
209
+ bootstrapPeers = [],
210
+ enableDHT = true,
211
+ allowPrivateAddresses = false,
212
+ enableRelay = false,
213
+ privateKey
214
+ } = config;
215
+ const libp2pConfig = {
216
+ addresses: {
217
+ listen: listenAddresses
218
+ },
219
+ ...privateKey ? { privateKey } : {},
220
+ transports: [
221
+ tcp(),
222
+ circuitRelayTransport()
223
+ ],
224
+ connectionEncrypters: [noise()],
225
+ streamMuxers: [mplex()],
226
+ services: {
227
+ identify: identify(),
228
+ ping: ping()
229
+ }
230
+ };
231
+ if (enableRelay) {
232
+ libp2pConfig.services.relay = circuitRelayServer();
233
+ }
234
+ if (enableDHT) {
235
+ libp2pConfig.services.dht = kadDHT({
236
+ clientMode: false,
237
+ peerInfoMapper: allowPrivateAddresses ? passthroughMapper : removePrivateAddressesMapper,
238
+ validators: {
239
+ clawiverse: async () => {
240
+ }
241
+ },
242
+ selectors: {
243
+ clawiverse: () => 0
244
+ }
245
+ });
246
+ }
247
+ if (bootstrapPeers.length > 0) {
248
+ libp2pConfig.peerDiscovery = [
249
+ bootstrap({
250
+ list: bootstrapPeers
251
+ })
252
+ ];
253
+ }
254
+ const libp2p = await createLibp2p(libp2pConfig);
255
+ logger.info("Libp2p node created");
256
+ return {
257
+ libp2p,
258
+ start: async () => {
259
+ await libp2p.start();
260
+ logger.info("Node started", {
261
+ peerId: libp2p.peerId.toString(),
262
+ addresses: libp2p.getMultiaddrs().map((ma) => ma.toString()),
263
+ relay: enableRelay
264
+ });
265
+ },
266
+ stop: async () => {
267
+ await libp2p.stop();
268
+ logger.info("Node stopped");
269
+ },
270
+ getMultiaddrs: () => {
271
+ return libp2p.getMultiaddrs().map((ma) => ma.toString());
272
+ },
273
+ getPeerId: () => {
274
+ return libp2p.peerId.toString();
275
+ }
276
+ };
277
+ } catch (error) {
278
+ throw new TransportError("Failed to create transport node", error);
279
+ }
280
+ }
281
+
282
+ // src/discovery/agent-card-types.ts
283
+ function isLegacyCard(card) {
284
+ return Array.isArray(card.capabilities) && card.capabilities.length > 0 && typeof card.capabilities[0] === "string";
285
+ }
286
+ function upgradeLegacyCard(legacy) {
287
+ return {
288
+ ...legacy,
289
+ capabilities: legacy.capabilities.map((cap) => ({
290
+ id: cap,
291
+ name: cap,
292
+ description: `Capability: ${cap}`
293
+ }))
294
+ };
295
+ }
296
+ function downgradeToLegacyCard(card) {
297
+ const { "@context": _, trust: __, ...rest } = card;
298
+ return {
299
+ ...rest,
300
+ capabilities: card.capabilities.map((cap) => cap.id)
301
+ };
302
+ }
303
+
304
+ // src/discovery/agent-card.ts
305
+ var legacyAgentCardSchema = {
306
+ type: "object",
307
+ properties: {
308
+ did: { type: "string", pattern: "^did:clawiverse:[1-9A-HJ-NP-Za-km-z]+$" },
309
+ name: { type: "string", minLength: 1, maxLength: 100 },
310
+ description: { type: "string", maxLength: 500 },
311
+ version: { type: "string", pattern: "^\\d+\\.\\d+\\.\\d+$" },
312
+ capabilities: {
313
+ type: "array",
314
+ items: { type: "string" },
315
+ minItems: 0,
316
+ maxItems: 50
317
+ },
318
+ endpoints: {
319
+ type: "array",
320
+ items: { type: "string" },
321
+ minItems: 0,
322
+ maxItems: 10
323
+ },
324
+ peerId: { type: "string", nullable: true },
325
+ metadata: {
326
+ type: "object",
327
+ nullable: true,
328
+ required: []
329
+ },
330
+ timestamp: { type: "number" }
331
+ },
332
+ required: ["did", "name", "description", "version", "capabilities", "endpoints", "timestamp"],
333
+ additionalProperties: false
334
+ };
335
+ var ajv = new Ajv();
336
+ var validateLegacySchema = ajv.compile(legacyAgentCardSchema);
337
+ function createAgentCard(did, name, description, capabilities, endpoints = [], peerId, metadata) {
338
+ return {
339
+ did,
340
+ name,
341
+ description,
342
+ version: "1.0.0",
343
+ capabilities,
344
+ endpoints,
345
+ peerId,
346
+ metadata,
347
+ timestamp: Date.now()
348
+ };
349
+ }
350
+ function createLegacyAgentCard(did, name, description, capabilities, endpoints = [], peerId, metadata) {
351
+ return {
352
+ did,
353
+ name,
354
+ description,
355
+ version: "1.0.0",
356
+ capabilities,
357
+ endpoints,
358
+ peerId,
359
+ metadata,
360
+ timestamp: Date.now()
361
+ };
362
+ }
363
+ function validateAgentCard(card) {
364
+ if (typeof card !== "object" || card === null) {
365
+ return false;
366
+ }
367
+ const c = card;
368
+ if (typeof c.signature !== "string") {
369
+ return false;
370
+ }
371
+ if (isLegacyCard(c)) {
372
+ return validateLegacySchema(c);
373
+ }
374
+ return typeof c.did === "string" && typeof c.name === "string" && typeof c.description === "string" && typeof c.version === "string" && Array.isArray(c.capabilities) && Array.isArray(c.endpoints) && typeof c.timestamp === "number";
375
+ }
376
+ async function signAgentCard(card, signFn) {
377
+ try {
378
+ const cardJson = JSON.stringify(card);
379
+ const cardBytes = new TextEncoder().encode(cardJson);
380
+ const signature = await signFn(cardBytes);
381
+ return {
382
+ ...card,
383
+ signature: Buffer.from(signature).toString("hex")
384
+ };
385
+ } catch (error) {
386
+ throw new DiscoveryError("Failed to sign Agent Card", error);
387
+ }
388
+ }
389
+ async function verifyAgentCard(card, verifyFn) {
390
+ try {
391
+ const { signature, ...cardWithoutSig } = card;
392
+ const cardJson = JSON.stringify(cardWithoutSig);
393
+ const cardBytes = new TextEncoder().encode(cardJson);
394
+ const signatureBytes = Buffer.from(signature, "hex");
395
+ return await verifyFn(signatureBytes, cardBytes);
396
+ } catch (error) {
397
+ throw new DiscoveryError("Failed to verify Agent Card", error);
398
+ }
399
+ }
400
+ function matchesCapability(card, capability) {
401
+ if (isLegacyCard(card)) {
402
+ return card.capabilities.some(
403
+ (cap) => cap.toLowerCase().includes(capability.toLowerCase())
404
+ );
405
+ }
406
+ return card.capabilities.some(
407
+ (cap) => cap.id.toLowerCase().includes(capability.toLowerCase()) || cap.name.toLowerCase().includes(capability.toLowerCase()) || cap.description.toLowerCase().includes(capability.toLowerCase())
408
+ );
409
+ }
410
+
411
+ // src/discovery/agent-card-schema.ts
412
+ var CLAWIVERSE_CONTEXT = "https://clawiverse.org/context/v1";
413
+ var SCHEMA_ORG_CONTEXT = "https://schema.org";
414
+ var clawiverseContext = {
415
+ "@context": {
416
+ "@vocab": CLAWIVERSE_CONTEXT,
417
+ "schema": SCHEMA_ORG_CONTEXT,
418
+ "AgentCard": "schema:SoftwareApplication",
419
+ "Capability": "schema:Action",
420
+ "did": "@id",
421
+ "name": "schema:name",
422
+ "description": "schema:description",
423
+ "version": "schema:softwareVersion",
424
+ "capabilities": {
425
+ "@id": "schema:potentialAction",
426
+ "@type": "@id",
427
+ "@container": "@list"
428
+ },
429
+ "endpoints": {
430
+ "@id": "schema:url",
431
+ "@container": "@list"
432
+ },
433
+ "peerId": "clawiverse:peerId",
434
+ "trust": "clawiverse:trustScore",
435
+ "metadata": "schema:additionalProperty",
436
+ "timestamp": "schema:dateModified",
437
+ "signature": "clawiverse:signature",
438
+ "parameters": {
439
+ "@id": "schema:object",
440
+ "@container": "@list"
441
+ }
442
+ }
443
+ };
444
+ var CapabilityTypes = {
445
+ TRANSLATION: "TranslationService",
446
+ CODE_REVIEW: "CodeReviewService",
447
+ DATA_ANALYSIS: "DataAnalysisService",
448
+ TEXT_GENERATION: "TextGenerationService",
449
+ IMAGE_GENERATION: "ImageGenerationService",
450
+ SEARCH: "SearchService",
451
+ COMPUTATION: "ComputationService",
452
+ STORAGE: "StorageService",
453
+ MESSAGING: "MessagingService",
454
+ AUTHENTICATION: "AuthenticationService"
455
+ };
456
+ var ParameterTypes = {
457
+ STRING: "string",
458
+ NUMBER: "number",
459
+ BOOLEAN: "boolean",
460
+ OBJECT: "object",
461
+ ARRAY: "array"
462
+ };
463
+ function getAgentCardContext() {
464
+ return [SCHEMA_ORG_CONTEXT, CLAWIVERSE_CONTEXT];
465
+ }
466
+ function isValidContext(context) {
467
+ if (!Array.isArray(context)) return false;
468
+ return context.every((c) => typeof c === "string");
469
+ }
470
+ function encodeForDHT(card) {
471
+ try {
472
+ const { "@context": _, ...cardWithoutContext } = card;
473
+ return encode(cardWithoutContext);
474
+ } catch (error) {
475
+ throw new DiscoveryError("Failed to encode Agent Card as CBOR", error);
476
+ }
477
+ }
478
+ function encodeForWeb(card) {
479
+ try {
480
+ const cardWithContext = {
481
+ "@context": card["@context"] || getAgentCardContext(),
482
+ ...card
483
+ };
484
+ return JSON.stringify(cardWithContext, null, 2);
485
+ } catch (error) {
486
+ throw new DiscoveryError("Failed to encode Agent Card as JSON-LD", error);
487
+ }
488
+ }
489
+ function decodeFromCBOR(data) {
490
+ try {
491
+ const decoded = decode(data);
492
+ if (isLegacyCard(decoded)) {
493
+ return upgradeLegacyCard(decoded);
494
+ }
495
+ if (!decoded["@context"]) {
496
+ decoded["@context"] = getAgentCardContext();
497
+ }
498
+ return decoded;
499
+ } catch (error) {
500
+ throw new DiscoveryError("Failed to decode Agent Card from CBOR", error);
501
+ }
502
+ }
503
+ function decodeFromJSON(json) {
504
+ try {
505
+ const decoded = JSON.parse(json);
506
+ if (isLegacyCard(decoded)) {
507
+ return upgradeLegacyCard(decoded);
508
+ }
509
+ if (!decoded["@context"]) {
510
+ decoded["@context"] = getAgentCardContext();
511
+ }
512
+ return decoded;
513
+ } catch (error) {
514
+ throw new DiscoveryError("Failed to decode Agent Card from JSON", error);
515
+ }
516
+ }
517
+ function decodeAgentCard(data) {
518
+ if (typeof data === "string") {
519
+ return decodeFromJSON(data);
520
+ }
521
+ try {
522
+ return decodeFromCBOR(data);
523
+ } catch {
524
+ const text = new TextDecoder().decode(data);
525
+ if (text.trim().startsWith("{")) {
526
+ return decodeFromJSON(text);
527
+ }
528
+ throw new DiscoveryError("Unable to decode Agent Card: unknown format");
529
+ }
530
+ }
531
+ function getEncodedSize(card) {
532
+ return {
533
+ cbor: encodeForDHT(card).length,
534
+ json: encodeForWeb(card).length
535
+ };
536
+ }
537
+ var logger2 = createLogger("search-index");
538
+ var SearchIndex = class {
539
+ cards = /* @__PURE__ */ new Map();
540
+ lunrIndex;
541
+ fuse;
542
+ needsRebuild = false;
543
+ /**
544
+ * Add or update an Agent Card in the index
545
+ */
546
+ indexAgentCard(card) {
547
+ this.cards.set(card.did, card);
548
+ this.needsRebuild = true;
549
+ logger2.debug("Indexed Agent Card", { did: card.did, capabilities: card.capabilities.length });
550
+ }
551
+ /**
552
+ * Remove an Agent Card from the index
553
+ */
554
+ removeAgentCard(did) {
555
+ this.cards.delete(did);
556
+ this.needsRebuild = true;
557
+ logger2.debug("Removed Agent Card from index", { did });
558
+ }
559
+ /**
560
+ * Search for agents matching a query
561
+ */
562
+ search(query) {
563
+ if (this.needsRebuild) {
564
+ this.rebuild();
565
+ }
566
+ let results = [];
567
+ if (query.text && this.lunrIndex) {
568
+ results = this.searchByText(query.text);
569
+ } else if (query.capability && this.fuse) {
570
+ results = this.searchByCapability(query.capability);
571
+ } else {
572
+ results = Array.from(this.cards.values()).map((card) => ({
573
+ card,
574
+ score: 1
575
+ }));
576
+ }
577
+ if (query.filters) {
578
+ results = this.applyFilters(results, query.filters);
579
+ }
580
+ results.sort((a, b) => b.score - a.score);
581
+ if (query.limit) {
582
+ results = results.slice(0, query.limit);
583
+ }
584
+ logger2.debug("Search completed", { query, results: results.length });
585
+ return results;
586
+ }
587
+ /**
588
+ * Get all indexed cards
589
+ */
590
+ getAllCards() {
591
+ return Array.from(this.cards.values());
592
+ }
593
+ /**
594
+ * Clear the index
595
+ */
596
+ clear() {
597
+ this.cards.clear();
598
+ this.lunrIndex = void 0;
599
+ this.fuse = void 0;
600
+ this.needsRebuild = false;
601
+ logger2.info("Search index cleared");
602
+ }
603
+ /**
604
+ * Get index size
605
+ */
606
+ size() {
607
+ return this.cards.size;
608
+ }
609
+ /**
610
+ * Rebuild search indexes
611
+ */
612
+ rebuild() {
613
+ logger2.info("Rebuilding search indexes", { cards: this.cards.size });
614
+ const cards = Array.from(this.cards.values());
615
+ this.lunrIndex = lunr(function() {
616
+ this.ref("did");
617
+ this.field("name", { boost: 10 });
618
+ this.field("description", { boost: 5 });
619
+ this.field("capabilities");
620
+ for (const card of cards) {
621
+ this.add({
622
+ did: card.did,
623
+ name: card.name,
624
+ description: card.description,
625
+ capabilities: card.capabilities.map((cap) => `${cap.name} ${cap.description}`).join(" ")
626
+ });
627
+ }
628
+ });
629
+ this.fuse = new Fuse(Array.from(this.cards.values()), {
630
+ keys: [
631
+ { name: "name", weight: 0.3 },
632
+ { name: "description", weight: 0.2 },
633
+ { name: "capabilities.name", weight: 0.3 },
634
+ { name: "capabilities.description", weight: 0.2 }
635
+ ],
636
+ threshold: 0.4,
637
+ includeScore: true
638
+ });
639
+ this.needsRebuild = false;
640
+ logger2.info("Search indexes rebuilt");
641
+ }
642
+ /**
643
+ * Search by text using Lunr
644
+ */
645
+ searchByText(text) {
646
+ if (!this.lunrIndex) return [];
647
+ const lunrResults = this.lunrIndex.search(text);
648
+ return lunrResults.map((result) => ({
649
+ card: this.cards.get(result.ref),
650
+ score: result.score
651
+ }));
652
+ }
653
+ /**
654
+ * Search by capability using Fuse
655
+ */
656
+ searchByCapability(capability) {
657
+ if (!this.fuse) return [];
658
+ const fuseResults = this.fuse.search(capability);
659
+ return fuseResults.map((result) => ({
660
+ card: result.item,
661
+ score: 1 - (result.score || 0)
662
+ // Fuse score is distance, convert to similarity
663
+ }));
664
+ }
665
+ /**
666
+ * Apply filters to search results
667
+ */
668
+ applyFilters(results, filters) {
669
+ return results.filter((result) => {
670
+ const { card } = result;
671
+ if (filters.minTrustScore !== void 0 && card.trust) {
672
+ const overallTrust = card.trust.interactionScore * 0.4 + Math.min(card.trust.endorsements / 10, 1) * 0.2 + card.trust.completionRate * 0.2 + card.trust.uptime * 0.2;
673
+ if (overallTrust < filters.minTrustScore) {
674
+ return false;
675
+ }
676
+ }
677
+ if (filters.language) {
678
+ const hasLanguage = card.capabilities.some(
679
+ (cap) => {
680
+ const metadata = cap.metadata;
681
+ if (!metadata) return false;
682
+ return metadata.language === filters.language || Array.isArray(metadata.languages) && metadata.languages.includes(filters.language);
683
+ }
684
+ );
685
+ if (!hasLanguage) return false;
686
+ }
687
+ if (filters.tags && filters.tags.length > 0) {
688
+ const cardTags = Array.isArray(card.metadata?.tags) ? card.metadata.tags : [];
689
+ const hasAllTags = filters.tags.every((tag) => cardTags.includes(tag));
690
+ if (!hasAllTags) return false;
691
+ }
692
+ return true;
693
+ });
694
+ }
695
+ };
696
+
697
+ // src/discovery/capability-matcher.ts
698
+ var CapabilityMatcher = class {
699
+ /**
700
+ * Match a query against a capability
701
+ * Returns a score between 0 and 1
702
+ */
703
+ match(query, capability) {
704
+ let score = 0;
705
+ let weights = 0;
706
+ if (query.capability && capability.id === query.capability) {
707
+ return 1;
708
+ }
709
+ if (query.capability) {
710
+ const nameScore = this.fuzzyMatch(query.capability, capability.name);
711
+ score += nameScore * 0.4;
712
+ weights += 0.4;
713
+ }
714
+ if (query.text) {
715
+ const keywords = this.extractKeywords(query.text);
716
+ const keywordScore = this.matchKeywords(keywords, capability);
717
+ score += keywordScore * 0.4;
718
+ weights += 0.4;
719
+ }
720
+ if (query.capability && capability["@type"]) {
721
+ const typeScore = this.matchesType(query.capability, capability["@type"]) ? 0.6 : 0;
722
+ score += typeScore * 0.2;
723
+ weights += 0.2;
724
+ }
725
+ return weights > 0 ? score / weights : 0;
726
+ }
727
+ /**
728
+ * Extract keywords from natural language text
729
+ */
730
+ extractKeywords(text) {
731
+ const stopWords = /* @__PURE__ */ new Set([
732
+ "a",
733
+ "an",
734
+ "the",
735
+ "is",
736
+ "are",
737
+ "was",
738
+ "were",
739
+ "be",
740
+ "been",
741
+ "to",
742
+ "from",
743
+ "in",
744
+ "on",
745
+ "at",
746
+ "by",
747
+ "for",
748
+ "with",
749
+ "about",
750
+ "can",
751
+ "could",
752
+ "should",
753
+ "would",
754
+ "will",
755
+ "do",
756
+ "does",
757
+ "did",
758
+ "i",
759
+ "you",
760
+ "he",
761
+ "she",
762
+ "it",
763
+ "we",
764
+ "they",
765
+ "me",
766
+ "him",
767
+ "her"
768
+ ]);
769
+ return text.toLowerCase().split(/\W+/).filter((word) => word.length > 2 && !stopWords.has(word));
770
+ }
771
+ /**
772
+ * Match keywords against capability
773
+ */
774
+ matchKeywords(keywords, capability) {
775
+ if (keywords.length === 0) return 0;
776
+ const capText = `${capability.name} ${capability.description}`.toLowerCase();
777
+ const matches = keywords.filter((keyword) => capText.includes(keyword));
778
+ return matches.length / keywords.length;
779
+ }
780
+ /**
781
+ * Check if query matches capability type hierarchy
782
+ */
783
+ matchesType(query, type) {
784
+ const queryLower = query.toLowerCase();
785
+ const typeLower = type.toLowerCase();
786
+ if (typeLower.includes(queryLower)) return true;
787
+ const typeMap = {
788
+ translate: ["translation", "translationservice"],
789
+ review: ["codereview", "reviewservice"],
790
+ analyze: ["analysis", "dataanalysis"],
791
+ generate: ["generation", "textgeneration", "imagegeneration"],
792
+ search: ["searchservice", "query"],
793
+ compute: ["computation", "computationservice"],
794
+ store: ["storage", "storageservice"],
795
+ message: ["messaging", "messagingservice"],
796
+ auth: ["authentication", "authenticationservice"]
797
+ };
798
+ for (const [key, values] of Object.entries(typeMap)) {
799
+ if (queryLower.includes(key) && values.some((v) => typeLower.includes(v))) {
800
+ return true;
801
+ }
802
+ }
803
+ return false;
804
+ }
805
+ /**
806
+ * Fuzzy string matching using Levenshtein distance
807
+ */
808
+ fuzzyMatch(query, target) {
809
+ const queryLower = query.toLowerCase();
810
+ const targetLower = target.toLowerCase();
811
+ if (queryLower === targetLower) return 1;
812
+ if (targetLower.includes(queryLower)) return 0.8;
813
+ if (queryLower.includes(targetLower)) return 0.7;
814
+ const distance = this.levenshteinDistance(queryLower, targetLower);
815
+ const maxLen = Math.max(queryLower.length, targetLower.length);
816
+ const similarity = 1 - distance / maxLen;
817
+ return similarity > 0.5 ? similarity * 0.6 : 0;
818
+ }
819
+ /**
820
+ * Calculate Levenshtein distance between two strings
821
+ */
822
+ levenshteinDistance(a, b) {
823
+ const matrix = [];
824
+ for (let i = 0; i <= b.length; i++) {
825
+ matrix[i] = [i];
826
+ }
827
+ for (let j = 0; j <= a.length; j++) {
828
+ matrix[0][j] = j;
829
+ }
830
+ for (let i = 1; i <= b.length; i++) {
831
+ for (let j = 1; j <= a.length; j++) {
832
+ if (b.charAt(i - 1) === a.charAt(j - 1)) {
833
+ matrix[i][j] = matrix[i - 1][j - 1];
834
+ } else {
835
+ matrix[i][j] = Math.min(
836
+ matrix[i - 1][j - 1] + 1,
837
+ // substitution
838
+ matrix[i][j - 1] + 1,
839
+ // insertion
840
+ matrix[i - 1][j] + 1
841
+ // deletion
842
+ );
843
+ }
844
+ }
845
+ }
846
+ return matrix[b.length][a.length];
847
+ }
848
+ };
849
+
850
+ // src/discovery/semantic-search.ts
851
+ var logger3 = createLogger("semantic-search");
852
+ var SemanticSearchEngine = class {
853
+ constructor(dht) {
854
+ this.dht = dht;
855
+ this.index = new SearchIndex();
856
+ this.matcher = new CapabilityMatcher();
857
+ }
858
+ index;
859
+ matcher;
860
+ /**
861
+ * Main search interface
862
+ * Local-first with network fallback
863
+ */
864
+ async search(query) {
865
+ logger3.info("Searching for agents", { query });
866
+ const localResults = this.index.search(query);
867
+ logger3.debug("Local search results", { count: localResults.length });
868
+ const limit = query.limit || 10;
869
+ if (localResults.length < limit && this.dht) {
870
+ logger3.debug("Insufficient local results, querying network");
871
+ const networkResults = await this.searchNetwork(query);
872
+ return this.mergeResults(localResults, networkResults, limit);
873
+ }
874
+ return localResults.map((r) => r.card);
875
+ }
876
+ /**
877
+ * Index an Agent Card for local search
878
+ */
879
+ indexAgentCard(card) {
880
+ this.index.indexAgentCard(card);
881
+ }
882
+ /**
883
+ * Remove an Agent Card from local index
884
+ */
885
+ removeAgentCard(did) {
886
+ this.index.removeAgentCard(did);
887
+ }
888
+ /**
889
+ * Get all indexed cards
890
+ */
891
+ getAllIndexedCards() {
892
+ return this.index.getAllCards();
893
+ }
894
+ /**
895
+ * Clear local index
896
+ */
897
+ clearIndex() {
898
+ this.index.clear();
899
+ }
900
+ /**
901
+ * Get index size
902
+ */
903
+ getIndexSize() {
904
+ return this.index.size();
905
+ }
906
+ /**
907
+ * Search network via DHT
908
+ */
909
+ async searchNetwork(query) {
910
+ if (!this.dht) {
911
+ return [];
912
+ }
913
+ try {
914
+ const capability = query.capability || this.extractPrimaryCapability(query.text);
915
+ if (!capability) {
916
+ logger3.debug("No capability extracted from query, skipping network search");
917
+ return [];
918
+ }
919
+ const cards = await this.dht.queryByCapability(capability);
920
+ logger3.debug("Network search results", { count: cards.length });
921
+ return cards.map((card) => {
922
+ const score = this.scoreCard(card, query);
923
+ return { card, score };
924
+ });
925
+ } catch (error) {
926
+ logger3.error("Network search failed", { error });
927
+ return [];
928
+ }
929
+ }
930
+ /**
931
+ * Merge local and network results, removing duplicates
932
+ */
933
+ mergeResults(local, network, limit) {
934
+ const seen = /* @__PURE__ */ new Set();
935
+ const merged = [];
936
+ for (const result of local) {
937
+ if (!seen.has(result.card.did)) {
938
+ seen.add(result.card.did);
939
+ merged.push(result);
940
+ }
941
+ }
942
+ for (const result of network) {
943
+ if (!seen.has(result.card.did)) {
944
+ seen.add(result.card.did);
945
+ merged.push(result);
946
+ this.index.indexAgentCard(result.card);
947
+ }
948
+ }
949
+ merged.sort((a, b) => b.score - a.score);
950
+ return merged.slice(0, limit).map((r) => r.card);
951
+ }
952
+ /**
953
+ * Score a card against a query
954
+ */
955
+ scoreCard(card, query) {
956
+ let totalScore = 0;
957
+ let count = 0;
958
+ for (const capability of card.capabilities) {
959
+ const score = this.matcher.match(query, capability);
960
+ if (score > 0) {
961
+ totalScore += score;
962
+ count++;
963
+ }
964
+ }
965
+ const avgScore = count > 0 ? totalScore / count : 0;
966
+ if (card.trust) {
967
+ const trustBoost = card.trust.interactionScore * 0.2;
968
+ return Math.min(avgScore + trustBoost, 1);
969
+ }
970
+ return avgScore;
971
+ }
972
+ /**
973
+ * Extract primary capability from natural language text
974
+ */
975
+ extractPrimaryCapability(text) {
976
+ if (!text) return void 0;
977
+ const keywords = this.matcher.extractKeywords(text);
978
+ const capabilityKeywords = [
979
+ "translate",
980
+ "translation",
981
+ "review",
982
+ "code",
983
+ "analyze",
984
+ "analysis",
985
+ "generate",
986
+ "generation",
987
+ "search",
988
+ "query",
989
+ "compute",
990
+ "calculation",
991
+ "store",
992
+ "storage",
993
+ "message",
994
+ "messaging",
995
+ "auth",
996
+ "authentication"
997
+ ];
998
+ for (const keyword of keywords) {
999
+ if (capabilityKeywords.includes(keyword)) {
1000
+ return keyword;
1001
+ }
1002
+ }
1003
+ return keywords[0];
1004
+ }
1005
+ };
1006
+ function createSemanticSearch(dht) {
1007
+ return new SemanticSearchEngine(dht);
1008
+ }
1009
+
1010
+ // src/discovery/dht.ts
1011
+ var logger4 = createLogger("dht");
1012
+ function createDHTOperations(libp2p) {
1013
+ const operations = {};
1014
+ const searchEngine = createSemanticSearch(operations);
1015
+ return Object.assign(operations, {
1016
+ publishAgentCard: async (card) => {
1017
+ try {
1018
+ const dht = libp2p.services?.dht;
1019
+ if (!dht) {
1020
+ throw new DiscoveryError("DHT service not available");
1021
+ }
1022
+ const key = fromString(`/clawiverse/agent/${card.did}`);
1023
+ const value = encodeForDHT(card);
1024
+ for await (const event of dht.put(key, value)) {
1025
+ logger4.debug("DHT put event", { name: event.name });
1026
+ }
1027
+ searchEngine.indexAgentCard(card);
1028
+ logger4.info("Published Agent Card to DHT", { did: card.did });
1029
+ } catch (error) {
1030
+ throw new DiscoveryError("Failed to publish Agent Card", error);
1031
+ }
1032
+ },
1033
+ queryAgentCard: async (did) => {
1034
+ try {
1035
+ const dht = libp2p.services?.dht;
1036
+ if (!dht) {
1037
+ throw new DiscoveryError("DHT service not available");
1038
+ }
1039
+ const key = fromString(`/clawiverse/agent/${did}`);
1040
+ for await (const event of dht.get(key)) {
1041
+ if (event.name === "VALUE") {
1042
+ const card = decodeFromCBOR(event.value);
1043
+ searchEngine.indexAgentCard(card);
1044
+ logger4.debug("Found Agent Card in DHT", { did });
1045
+ return card;
1046
+ }
1047
+ }
1048
+ logger4.debug("Agent Card not found in DHT", { did });
1049
+ return null;
1050
+ } catch (error) {
1051
+ logger4.warn("Failed to query Agent Card", { did, error });
1052
+ return null;
1053
+ }
1054
+ },
1055
+ queryByCapability: async (capability) => {
1056
+ try {
1057
+ const query = { capability, limit: 20 };
1058
+ return searchEngine.search(query);
1059
+ } catch (error) {
1060
+ throw new DiscoveryError("Failed to query by capability", error);
1061
+ }
1062
+ },
1063
+ searchSemantic: async (query) => {
1064
+ try {
1065
+ return searchEngine.search(query);
1066
+ } catch (error) {
1067
+ throw new DiscoveryError("Failed to perform semantic search", error);
1068
+ }
1069
+ },
1070
+ resolveDID: async (did) => {
1071
+ try {
1072
+ const dht = libp2p.services?.dht;
1073
+ if (!dht) {
1074
+ throw new DiscoveryError("DHT service not available");
1075
+ }
1076
+ const key = fromString(`/clawiverse/agent/${did}`);
1077
+ for await (const event of dht.get(key)) {
1078
+ if (event.name === "VALUE") {
1079
+ const card = decodeFromCBOR(event.value);
1080
+ if (card.peerId) {
1081
+ logger4.debug("Resolved DID to peer", { did, peerId: card.peerId });
1082
+ return {
1083
+ peerId: card.peerId,
1084
+ multiaddrs: card.endpoints || []
1085
+ };
1086
+ }
1087
+ logger4.warn("Agent Card found but has no peerId", { did });
1088
+ return null;
1089
+ }
1090
+ }
1091
+ logger4.debug("DID not found in DHT", { did });
1092
+ return null;
1093
+ } catch (error) {
1094
+ logger4.warn("Failed to resolve DID", { did, error });
1095
+ return null;
1096
+ }
1097
+ }
1098
+ });
1099
+ }
1100
+
1101
+ // src/messaging/envelope.ts
1102
+ function createEnvelope(from, to, type, protocol, payload, replyTo) {
1103
+ return {
1104
+ id: generateMessageId(),
1105
+ from,
1106
+ to,
1107
+ type,
1108
+ protocol,
1109
+ payload,
1110
+ timestamp: Date.now(),
1111
+ replyTo
1112
+ };
1113
+ }
1114
+ async function signEnvelope(envelope, signFn) {
1115
+ try {
1116
+ const envelopeJson = JSON.stringify(envelope);
1117
+ const envelopeBytes = new TextEncoder().encode(envelopeJson);
1118
+ const signature = await signFn(envelopeBytes);
1119
+ return {
1120
+ ...envelope,
1121
+ signature: Buffer.from(signature).toString("hex")
1122
+ };
1123
+ } catch (error) {
1124
+ throw new MessagingError("Failed to sign envelope", error);
1125
+ }
1126
+ }
1127
+ async function verifyEnvelope(envelope, verifyFn) {
1128
+ try {
1129
+ const { signature, ...envelopeWithoutSig } = envelope;
1130
+ const envelopeJson = JSON.stringify(envelopeWithoutSig);
1131
+ const envelopeBytes = new TextEncoder().encode(envelopeJson);
1132
+ const signatureBytes = Buffer.from(signature, "hex");
1133
+ return await verifyFn(signatureBytes, envelopeBytes);
1134
+ } catch (error) {
1135
+ throw new MessagingError("Failed to verify envelope", error);
1136
+ }
1137
+ }
1138
+ function validateEnvelope(msg) {
1139
+ if (typeof msg !== "object" || msg === null) {
1140
+ return false;
1141
+ }
1142
+ const m = msg;
1143
+ return typeof m.id === "string" && typeof m.from === "string" && m.from.startsWith("did:clawiverse:") && typeof m.to === "string" && m.to.startsWith("did:clawiverse:") && (m.type === "request" || m.type === "response" || m.type === "notification") && typeof m.protocol === "string" && m.payload !== void 0 && typeof m.timestamp === "number" && typeof m.signature === "string";
1144
+ }
1145
+ function generateMessageId() {
1146
+ return `msg_${Date.now()}_${Math.random().toString(36).substring(2, 15)}`;
1147
+ }
1148
+ function encodeMessage(envelope) {
1149
+ try {
1150
+ return encode(envelope);
1151
+ } catch (error) {
1152
+ throw new MessagingError("Failed to encode message", error);
1153
+ }
1154
+ }
1155
+ function decodeMessage(data) {
1156
+ try {
1157
+ return decode(data);
1158
+ } catch (error) {
1159
+ throw new MessagingError("Failed to decode message", error);
1160
+ }
1161
+ }
1162
+ function encodeMessageJSON(envelope) {
1163
+ try {
1164
+ return JSON.stringify(envelope, null, 2);
1165
+ } catch (error) {
1166
+ throw new MessagingError("Failed to encode message to JSON", error);
1167
+ }
1168
+ }
1169
+ function decodeMessageJSON(json) {
1170
+ try {
1171
+ return JSON.parse(json);
1172
+ } catch (error) {
1173
+ throw new MessagingError("Failed to decode message from JSON", error);
1174
+ }
1175
+ }
1176
+ var logger5 = createLogger("router");
1177
+ function createMessageRouter(libp2p, verifyFn, dht) {
1178
+ const handlers = /* @__PURE__ */ new Map();
1179
+ const PROTOCOL_PREFIX = "/clawiverse/msg/1.0.0";
1180
+ return {
1181
+ registerHandler: (protocol, handler) => {
1182
+ handlers.set(protocol, handler);
1183
+ logger5.info("Registered message handler", { protocol });
1184
+ },
1185
+ unregisterHandler: (protocol) => {
1186
+ handlers.delete(protocol);
1187
+ logger5.info("Unregistered message handler", { protocol });
1188
+ },
1189
+ sendMessage: async (envelope, peerHint) => {
1190
+ try {
1191
+ if (!validateEnvelope(envelope)) {
1192
+ throw new MessagingError("Invalid message envelope");
1193
+ }
1194
+ let targetPeerIdStr;
1195
+ let targetMultiaddrs = [];
1196
+ if (peerHint) {
1197
+ targetPeerIdStr = peerHint.peerId;
1198
+ targetMultiaddrs = peerHint.multiaddrs;
1199
+ logger5.info("Using peer hint for direct addressing", { peerId: targetPeerIdStr });
1200
+ } else if (dht) {
1201
+ const resolved = await dht.resolveDID(envelope.to);
1202
+ if (resolved) {
1203
+ targetPeerIdStr = resolved.peerId;
1204
+ targetMultiaddrs = resolved.multiaddrs;
1205
+ }
1206
+ }
1207
+ if (!targetPeerIdStr) {
1208
+ throw new MessagingError(
1209
+ `Cannot resolve recipient: ${envelope.to} \u2014 provide peerHint or ensure agent is in DHT`
1210
+ );
1211
+ }
1212
+ const targetPeerId = peerIdFromString(targetPeerIdStr);
1213
+ if (targetMultiaddrs.length > 0) {
1214
+ const mas = targetMultiaddrs.map((a) => multiaddr(a));
1215
+ await libp2p.peerStore.merge(targetPeerId, { multiaddrs: mas });
1216
+ }
1217
+ logger5.info("Dialing peer for message delivery", {
1218
+ peerId: targetPeerIdStr,
1219
+ multiaddrs: targetMultiaddrs
1220
+ });
1221
+ const stream = await libp2p.dialProtocol(targetPeerId, PROTOCOL_PREFIX);
1222
+ const encoded = encodeMessage(envelope);
1223
+ await stream.sink(
1224
+ (async function* () {
1225
+ yield encoded;
1226
+ })()
1227
+ );
1228
+ logger5.info("Message sent over libp2p stream", {
1229
+ id: envelope.id,
1230
+ from: envelope.from,
1231
+ to: envelope.to,
1232
+ protocol: envelope.protocol
1233
+ });
1234
+ return void 0;
1235
+ } catch (error) {
1236
+ if (error instanceof MessagingError) throw error;
1237
+ throw new MessagingError("Failed to send message", error);
1238
+ }
1239
+ },
1240
+ start: async () => {
1241
+ await libp2p.handle(PROTOCOL_PREFIX, async ({ stream }) => {
1242
+ try {
1243
+ await handleIncomingStream(stream, handlers, verifyFn);
1244
+ } catch (error) {
1245
+ logger5.error("Error handling incoming stream", error);
1246
+ }
1247
+ });
1248
+ logger5.info("Message router started");
1249
+ },
1250
+ stop: async () => {
1251
+ await libp2p.unhandle(PROTOCOL_PREFIX);
1252
+ handlers.clear();
1253
+ logger5.info("Message router stopped");
1254
+ }
1255
+ };
1256
+ }
1257
+ async function handleIncomingStream(stream, handlers, _verifyFn) {
1258
+ try {
1259
+ const chunks = [];
1260
+ for await (const chunk of stream.source) {
1261
+ chunks.push(chunk.subarray());
1262
+ }
1263
+ const data = new Uint8Array(
1264
+ chunks.reduce((acc, chunk) => acc + chunk.length, 0)
1265
+ );
1266
+ let offset = 0;
1267
+ for (const chunk of chunks) {
1268
+ data.set(chunk, offset);
1269
+ offset += chunk.length;
1270
+ }
1271
+ const envelope = decodeMessage(data);
1272
+ if (!validateEnvelope(envelope)) {
1273
+ logger5.warn("Received invalid message envelope");
1274
+ return;
1275
+ }
1276
+ logger5.info("Received message", {
1277
+ id: envelope.id,
1278
+ from: envelope.from,
1279
+ to: envelope.to,
1280
+ protocol: envelope.protocol,
1281
+ payload: envelope.payload
1282
+ });
1283
+ const handler = handlers.get(envelope.protocol);
1284
+ if (!handler) {
1285
+ logger5.warn("No handler for protocol", { protocol: envelope.protocol });
1286
+ return;
1287
+ }
1288
+ const response = await handler(envelope);
1289
+ if (response) {
1290
+ const encoded = encodeMessage(response);
1291
+ logger5.debug("Response prepared", { size: encoded.length });
1292
+ }
1293
+ } catch (error) {
1294
+ logger5.error("Error handling incoming message", error);
1295
+ }
1296
+ }
1297
+
1298
+ // src/trust/trust-score.ts
1299
+ var TrustMetrics = class {
1300
+ /**
1301
+ * Calculate trust score from interaction history
1302
+ */
1303
+ calculateScore(stats, endorsements, uptime) {
1304
+ const volumeWeight = Math.min(stats.totalInteractions / 100, 1);
1305
+ const interactionScore = stats.successRate * volumeWeight;
1306
+ const completionRate = stats.successRate;
1307
+ return {
1308
+ interactionScore,
1309
+ endorsements,
1310
+ completionRate,
1311
+ responseTime: stats.avgResponseTime,
1312
+ uptime,
1313
+ lastUpdated: Date.now()
1314
+ };
1315
+ }
1316
+ /**
1317
+ * Calculate overall trust level (0-1)
1318
+ */
1319
+ calculateOverallTrust(score) {
1320
+ const weights = {
1321
+ interaction: 0.4,
1322
+ endorsement: 0.2,
1323
+ completion: 0.2,
1324
+ uptime: 0.2
1325
+ };
1326
+ const endorsementScore = Math.min(score.endorsements / 10, 1);
1327
+ return score.interactionScore * weights.interaction + endorsementScore * weights.endorsement + score.completionRate * weights.completion + score.uptime * weights.uptime;
1328
+ }
1329
+ /**
1330
+ * Get trust level category
1331
+ */
1332
+ getTrustLevel(score) {
1333
+ const overall = this.calculateOverallTrust(score);
1334
+ if (score.interactionScore === 0) return "new";
1335
+ if (overall < 0.3) return "low";
1336
+ if (overall < 0.6) return "medium";
1337
+ if (overall < 0.8) return "high";
1338
+ return "trusted";
1339
+ }
1340
+ /**
1341
+ * Check if agent should be rate limited
1342
+ */
1343
+ shouldRateLimit(score, agentAge) {
1344
+ const overall = this.calculateOverallTrust(score);
1345
+ const ONE_DAY = 24 * 60 * 60 * 1e3;
1346
+ if (agentAge < ONE_DAY && overall < 0.3) {
1347
+ return true;
1348
+ }
1349
+ if (overall < 0.1) {
1350
+ return true;
1351
+ }
1352
+ return false;
1353
+ }
1354
+ };
1355
+ function createDefaultTrustScore() {
1356
+ return {
1357
+ interactionScore: 0,
1358
+ endorsements: 0,
1359
+ completionRate: 0,
1360
+ responseTime: 0,
1361
+ uptime: 1,
1362
+ // Assume online initially
1363
+ lastUpdated: Date.now()
1364
+ };
1365
+ }
1366
+ var logger6 = createLogger("interaction-history");
1367
+ var InteractionHistory = class {
1368
+ db;
1369
+ constructor(dbPath) {
1370
+ this.db = new Level(dbPath, { valueEncoding: "json" });
1371
+ }
1372
+ /**
1373
+ * Open database connection
1374
+ */
1375
+ async open() {
1376
+ await this.db.open();
1377
+ logger6.info("Interaction history database opened", { path: this.db.location });
1378
+ }
1379
+ /**
1380
+ * Close database connection
1381
+ */
1382
+ async close() {
1383
+ await this.db.close();
1384
+ logger6.info("Interaction history database closed");
1385
+ }
1386
+ /**
1387
+ * Record an interaction
1388
+ */
1389
+ async record(interaction) {
1390
+ const key = `interaction:${interaction.agentDid}:${interaction.timestamp}`;
1391
+ await this.db.put(key, interaction);
1392
+ logger6.debug("Recorded interaction", { agentDid: interaction.agentDid, type: interaction.type });
1393
+ }
1394
+ /**
1395
+ * Get interaction history for an agent
1396
+ */
1397
+ async getHistory(agentDid, limit = 100) {
1398
+ const interactions = [];
1399
+ const prefix = `interaction:${agentDid}:`;
1400
+ try {
1401
+ for await (const [_, value] of this.db.iterator({
1402
+ gte: prefix,
1403
+ lte: prefix + "\xFF",
1404
+ limit,
1405
+ reverse: true
1406
+ // Most recent first
1407
+ })) {
1408
+ interactions.push(value);
1409
+ }
1410
+ } catch (error) {
1411
+ logger6.error("Failed to get interaction history", { agentDid, error });
1412
+ }
1413
+ return interactions;
1414
+ }
1415
+ /**
1416
+ * Get interaction statistics for an agent
1417
+ */
1418
+ async getStats(agentDid) {
1419
+ const history = await this.getHistory(agentDid, 1e3);
1420
+ if (history.length === 0) {
1421
+ return {
1422
+ totalInteractions: 0,
1423
+ successRate: 0,
1424
+ avgResponseTime: 0,
1425
+ lastInteraction: 0
1426
+ };
1427
+ }
1428
+ const successCount = history.filter((i) => i.success).length;
1429
+ const totalResponseTime = history.reduce((sum, i) => sum + i.responseTime, 0);
1430
+ return {
1431
+ totalInteractions: history.length,
1432
+ successRate: successCount / history.length,
1433
+ avgResponseTime: totalResponseTime / history.length,
1434
+ lastInteraction: history[0].timestamp
1435
+ };
1436
+ }
1437
+ /**
1438
+ * Get all agents with interaction history
1439
+ */
1440
+ async getAllAgents() {
1441
+ const agents = /* @__PURE__ */ new Set();
1442
+ try {
1443
+ for await (const [key] of this.db.iterator()) {
1444
+ const match = key.match(/^interaction:([^:]+):/);
1445
+ if (match) {
1446
+ agents.add(match[1]);
1447
+ }
1448
+ }
1449
+ } catch (error) {
1450
+ logger6.error("Failed to get all agents", { error });
1451
+ }
1452
+ return Array.from(agents);
1453
+ }
1454
+ /**
1455
+ * Delete all interactions for an agent
1456
+ */
1457
+ async deleteAgent(agentDid) {
1458
+ const prefix = `interaction:${agentDid}:`;
1459
+ const keysToDelete = [];
1460
+ for await (const [key] of this.db.iterator({
1461
+ gte: prefix,
1462
+ lte: prefix + "\xFF"
1463
+ })) {
1464
+ keysToDelete.push(key);
1465
+ }
1466
+ await this.db.batch(keysToDelete.map((key) => ({ type: "del", key })));
1467
+ logger6.info("Deleted interaction history", { agentDid, count: keysToDelete.length });
1468
+ }
1469
+ /**
1470
+ * Clean up old interactions (older than 90 days)
1471
+ */
1472
+ async cleanup(maxAge = 90 * 24 * 60 * 60 * 1e3) {
1473
+ const cutoff = Date.now() - maxAge;
1474
+ const keysToDelete = [];
1475
+ for await (const [key, value] of this.db.iterator()) {
1476
+ if (value.timestamp < cutoff) {
1477
+ keysToDelete.push(key);
1478
+ }
1479
+ }
1480
+ if (keysToDelete.length > 0) {
1481
+ await this.db.batch(keysToDelete.map((key) => ({ type: "del", key })));
1482
+ logger6.info("Cleaned up old interactions", { count: keysToDelete.length });
1483
+ }
1484
+ return keysToDelete.length;
1485
+ }
1486
+ };
1487
+
1488
+ // src/trust/endorsement.ts
1489
+ var logger7 = createLogger("endorsement");
1490
+ var EndorsementManager = class {
1491
+ constructor(db, getPublicKey) {
1492
+ this.db = db;
1493
+ this.getPublicKey = getPublicKey;
1494
+ }
1495
+ /**
1496
+ * Create an endorsement
1497
+ */
1498
+ async endorse(fromDid, toDid, score, reason, signFn) {
1499
+ if (score < 0 || score > 1) {
1500
+ throw new Error("Score must be between 0 and 1");
1501
+ }
1502
+ const endorsement = {
1503
+ from: fromDid,
1504
+ to: toDid,
1505
+ score,
1506
+ reason,
1507
+ timestamp: Date.now()
1508
+ };
1509
+ const data = new TextEncoder().encode(JSON.stringify(endorsement));
1510
+ const signatureBytes = await signFn(data);
1511
+ const signature = Buffer.from(signatureBytes).toString("hex");
1512
+ const signedEndorsement = {
1513
+ ...endorsement,
1514
+ signature
1515
+ };
1516
+ logger7.info("Created endorsement", { from: fromDid, to: toDid, score });
1517
+ return signedEndorsement;
1518
+ }
1519
+ /**
1520
+ * Verify endorsement signature
1521
+ */
1522
+ async verify(endorsement, verifyFn) {
1523
+ try {
1524
+ const { signature, ...endorsementWithoutSig } = endorsement;
1525
+ const data = new TextEncoder().encode(JSON.stringify(endorsementWithoutSig));
1526
+ const signatureBytes = Buffer.from(signature, "hex");
1527
+ const publicKey = await this.getPublicKey(endorsement.from);
1528
+ return await verifyFn(signatureBytes, data, publicKey);
1529
+ } catch (error) {
1530
+ logger7.error("Failed to verify endorsement", { error });
1531
+ return false;
1532
+ }
1533
+ }
1534
+ /**
1535
+ * Publish endorsement to local database
1536
+ */
1537
+ async publish(endorsement) {
1538
+ const key = `endorsement:${endorsement.to}:${endorsement.from}`;
1539
+ await this.db.put(key, endorsement);
1540
+ logger7.info("Published endorsement", { from: endorsement.from, to: endorsement.to });
1541
+ }
1542
+ /**
1543
+ * Get all endorsements for an agent
1544
+ */
1545
+ async getEndorsements(agentDid) {
1546
+ const endorsements = [];
1547
+ const prefix = `endorsement:${agentDid}:`;
1548
+ try {
1549
+ for await (const [_, value] of this.db.iterator({
1550
+ gte: prefix,
1551
+ lte: prefix + "\xFF"
1552
+ })) {
1553
+ endorsements.push(value);
1554
+ }
1555
+ } catch (error) {
1556
+ logger7.error("Failed to get endorsements", { agentDid, error });
1557
+ }
1558
+ return endorsements;
1559
+ }
1560
+ /**
1561
+ * Get endorsements given by an agent
1562
+ */
1563
+ async getEndorsementsBy(fromDid) {
1564
+ const endorsements = [];
1565
+ try {
1566
+ for await (const [_, value] of this.db.iterator()) {
1567
+ if (value.from === fromDid) {
1568
+ endorsements.push(value);
1569
+ }
1570
+ }
1571
+ } catch (error) {
1572
+ logger7.error("Failed to get endorsements by agent", { fromDid, error });
1573
+ }
1574
+ return endorsements;
1575
+ }
1576
+ /**
1577
+ * Calculate average endorsement score
1578
+ */
1579
+ async getAverageScore(agentDid) {
1580
+ const endorsements = await this.getEndorsements(agentDid);
1581
+ if (endorsements.length === 0) {
1582
+ return 0;
1583
+ }
1584
+ const totalScore = endorsements.reduce((sum, e) => sum + e.score, 0);
1585
+ return totalScore / endorsements.length;
1586
+ }
1587
+ /**
1588
+ * Delete an endorsement
1589
+ */
1590
+ async deleteEndorsement(fromDid, toDid) {
1591
+ const key = `endorsement:${toDid}:${fromDid}`;
1592
+ await this.db.del(key);
1593
+ logger7.info("Deleted endorsement", { from: fromDid, to: toDid });
1594
+ }
1595
+ };
1596
+ var logger8 = createLogger("sybil-defense");
1597
+ var SybilDefense = class {
1598
+ rateLimits = /* @__PURE__ */ new Map();
1599
+ peerFirstSeen = /* @__PURE__ */ new Map();
1600
+ NEW_AGENT_WINDOW = 24 * 60 * 60 * 1e3;
1601
+ // 24 hours
1602
+ RATE_LIMIT_WINDOW = 60 * 60 * 1e3;
1603
+ // 1 hour
1604
+ MAX_REQUESTS_NEW = 10;
1605
+ // Max requests per hour for new agents
1606
+ ESTABLISHED_THRESHOLD = 7 * 24 * 60 * 60 * 1e3;
1607
+ // 7 days
1608
+ /**
1609
+ * Generate a Hashcash challenge for a new DID
1610
+ */
1611
+ generateChallenge(did, difficulty = 20) {
1612
+ const nonce = this.generateNonce();
1613
+ return {
1614
+ did,
1615
+ difficulty,
1616
+ nonce,
1617
+ timestamp: Date.now()
1618
+ };
1619
+ }
1620
+ /**
1621
+ * Verify a Hashcash challenge solution
1622
+ */
1623
+ verifyChallenge(solution) {
1624
+ const { challenge, solution: solutionNonce } = solution;
1625
+ if (Date.now() - challenge.timestamp > 60 * 60 * 1e3) {
1626
+ logger8.warn("Challenge expired", { did: challenge.did });
1627
+ return false;
1628
+ }
1629
+ const data = `${challenge.did}:${challenge.nonce}:${solutionNonce}`;
1630
+ const hash = sha256(new TextEncoder().encode(data));
1631
+ const leadingZeros = this.countLeadingZeroBits(hash);
1632
+ const valid = leadingZeros >= challenge.difficulty;
1633
+ if (valid) {
1634
+ logger8.info("Challenge verified", { did: challenge.did, leadingZeros });
1635
+ } else {
1636
+ logger8.warn("Challenge failed", { did: challenge.did, leadingZeros, required: challenge.difficulty });
1637
+ }
1638
+ return valid;
1639
+ }
1640
+ /**
1641
+ * Check if an agent should be rate limited
1642
+ */
1643
+ isRateLimited(did) {
1644
+ const record = this.rateLimits.get(did);
1645
+ if (!record) {
1646
+ return false;
1647
+ }
1648
+ const now = Date.now();
1649
+ const agentAge = now - record.firstSeen;
1650
+ if (agentAge > this.NEW_AGENT_WINDOW) {
1651
+ return false;
1652
+ }
1653
+ const recentRequests = record.requests.filter(
1654
+ (t) => now - t < this.RATE_LIMIT_WINDOW
1655
+ );
1656
+ return recentRequests.length >= this.MAX_REQUESTS_NEW;
1657
+ }
1658
+ /**
1659
+ * Record a request from an agent
1660
+ */
1661
+ recordRequest(did) {
1662
+ const now = Date.now();
1663
+ let record = this.rateLimits.get(did);
1664
+ if (!record) {
1665
+ record = {
1666
+ did,
1667
+ requests: [],
1668
+ firstSeen: now
1669
+ };
1670
+ this.rateLimits.set(did, record);
1671
+ }
1672
+ record.requests.push(now);
1673
+ record.requests = record.requests.filter(
1674
+ (t) => now - t < this.RATE_LIMIT_WINDOW
1675
+ );
1676
+ logger8.debug("Recorded request", { did, count: record.requests.length });
1677
+ }
1678
+ /**
1679
+ * Get peer trust level based on age
1680
+ */
1681
+ getPeerTrustLevel(peerId) {
1682
+ const firstSeen = this.peerFirstSeen.get(peerId);
1683
+ if (!firstSeen) {
1684
+ return "new";
1685
+ }
1686
+ const age = Date.now() - firstSeen;
1687
+ if (age < this.NEW_AGENT_WINDOW) {
1688
+ return "new";
1689
+ } else if (age < this.ESTABLISHED_THRESHOLD) {
1690
+ return "established";
1691
+ } else {
1692
+ return "trusted";
1693
+ }
1694
+ }
1695
+ /**
1696
+ * Record first seen time for a peer
1697
+ */
1698
+ recordPeerSeen(peerId) {
1699
+ if (!this.peerFirstSeen.has(peerId)) {
1700
+ this.peerFirstSeen.set(peerId, Date.now());
1701
+ logger8.debug("Recorded new peer", { peerId });
1702
+ }
1703
+ }
1704
+ /**
1705
+ * Clean up old records
1706
+ */
1707
+ cleanup() {
1708
+ const now = Date.now();
1709
+ const cutoff = now - this.NEW_AGENT_WINDOW;
1710
+ for (const [did, record] of this.rateLimits.entries()) {
1711
+ if (record.firstSeen < cutoff) {
1712
+ this.rateLimits.delete(did);
1713
+ }
1714
+ }
1715
+ logger8.info("Cleaned up Sybil defense records", {
1716
+ rateLimits: this.rateLimits.size,
1717
+ peers: this.peerFirstSeen.size
1718
+ });
1719
+ }
1720
+ /**
1721
+ * Generate a random nonce
1722
+ */
1723
+ generateNonce() {
1724
+ const bytes = new Uint8Array(16);
1725
+ crypto.getRandomValues(bytes);
1726
+ return bytesToHex(bytes);
1727
+ }
1728
+ /**
1729
+ * Count leading zero bits in a hash
1730
+ */
1731
+ countLeadingZeroBits(hash) {
1732
+ let count = 0;
1733
+ for (const byte of hash) {
1734
+ if (byte === 0) {
1735
+ count += 8;
1736
+ } else {
1737
+ let b = byte;
1738
+ while ((b & 128) === 0) {
1739
+ count++;
1740
+ b <<= 1;
1741
+ }
1742
+ break;
1743
+ }
1744
+ }
1745
+ return count;
1746
+ }
1747
+ };
1748
+ var logger9 = createLogger("trust-system");
1749
+ var TrustSystem = class {
1750
+ metrics;
1751
+ history;
1752
+ endorsements;
1753
+ sybilDefense;
1754
+ trustCache = /* @__PURE__ */ new Map();
1755
+ CACHE_TTL = 5 * 60 * 1e3;
1756
+ // 5 minutes
1757
+ constructor(config) {
1758
+ this.metrics = new TrustMetrics();
1759
+ this.history = new InteractionHistory(`${config.dbPath}/interactions`);
1760
+ const endorsementDb = new Level(`${config.dbPath}/endorsements`, {
1761
+ valueEncoding: "json"
1762
+ });
1763
+ this.endorsements = new EndorsementManager(endorsementDb, config.getPublicKey);
1764
+ this.sybilDefense = new SybilDefense();
1765
+ }
1766
+ /**
1767
+ * Initialize the trust system
1768
+ */
1769
+ async start() {
1770
+ await this.history.open();
1771
+ logger9.info("Trust system started");
1772
+ }
1773
+ /**
1774
+ * Shutdown the trust system
1775
+ */
1776
+ async stop() {
1777
+ await this.history.close();
1778
+ logger9.info("Trust system stopped");
1779
+ }
1780
+ /**
1781
+ * Record an interaction
1782
+ */
1783
+ async recordInteraction(interaction) {
1784
+ await this.history.record(interaction);
1785
+ this.sybilDefense.recordRequest(interaction.agentDid);
1786
+ this.trustCache.delete(interaction.agentDid);
1787
+ }
1788
+ /**
1789
+ * Get trust score for an agent
1790
+ */
1791
+ async getTrustScore(agentDid) {
1792
+ const cached = this.trustCache.get(agentDid);
1793
+ if (cached && Date.now() - cached.timestamp < this.CACHE_TTL) {
1794
+ return cached.score;
1795
+ }
1796
+ const stats = await this.history.getStats(agentDid);
1797
+ const endorsementList = await this.endorsements.getEndorsements(agentDid);
1798
+ const uptime = 1;
1799
+ const score = this.metrics.calculateScore(stats, endorsementList.length, uptime);
1800
+ this.trustCache.set(agentDid, { score, timestamp: Date.now() });
1801
+ return score;
1802
+ }
1803
+ /**
1804
+ * Create an endorsement
1805
+ */
1806
+ async endorse(fromDid, toDid, score, reason, signFn) {
1807
+ const endorsement = await this.endorsements.endorse(fromDid, toDid, score, reason, signFn);
1808
+ await this.endorsements.publish(endorsement);
1809
+ this.trustCache.delete(toDid);
1810
+ return endorsement;
1811
+ }
1812
+ /**
1813
+ * Get endorsements for an agent
1814
+ */
1815
+ async getEndorsements(agentDid) {
1816
+ return this.endorsements.getEndorsements(agentDid);
1817
+ }
1818
+ /**
1819
+ * Verify an endorsement
1820
+ */
1821
+ async verifyEndorsement(endorsement, verifyFn) {
1822
+ return this.endorsements.verify(endorsement, verifyFn);
1823
+ }
1824
+ /**
1825
+ * Check if agent should be rate limited
1826
+ */
1827
+ isRateLimited(agentDid) {
1828
+ return this.sybilDefense.isRateLimited(agentDid);
1829
+ }
1830
+ /**
1831
+ * Generate Sybil defense challenge
1832
+ */
1833
+ generateChallenge(did, difficulty) {
1834
+ return this.sybilDefense.generateChallenge(did, difficulty);
1835
+ }
1836
+ /**
1837
+ * Verify Sybil defense challenge
1838
+ */
1839
+ verifyChallenge(solution) {
1840
+ return this.sybilDefense.verifyChallenge(solution);
1841
+ }
1842
+ /**
1843
+ * Get peer trust level
1844
+ */
1845
+ getPeerTrustLevel(peerId) {
1846
+ return this.sybilDefense.getPeerTrustLevel(peerId);
1847
+ }
1848
+ /**
1849
+ * Record peer seen
1850
+ */
1851
+ recordPeerSeen(peerId) {
1852
+ this.sybilDefense.recordPeerSeen(peerId);
1853
+ }
1854
+ /**
1855
+ * Get interaction history
1856
+ */
1857
+ async getHistory(agentDid, limit) {
1858
+ return this.history.getHistory(agentDid, limit);
1859
+ }
1860
+ /**
1861
+ * Clean up old data
1862
+ */
1863
+ async cleanup() {
1864
+ await this.history.cleanup();
1865
+ this.sybilDefense.cleanup();
1866
+ this.trustCache.clear();
1867
+ logger9.info("Trust system cleanup completed");
1868
+ }
1869
+ };
1870
+ function createTrustSystem(config) {
1871
+ return new TrustSystem(config);
1872
+ }
1873
+
1874
+ export { CLAWIVERSE_CONTEXT, CapabilityMatcher, CapabilityTypes, ClawiverseError, DiscoveryError, EndorsementManager, IdentityError, InteractionHistory, LogLevel, Logger, MessagingError, ParameterTypes, SCHEMA_ORG_CONTEXT, SearchIndex, SemanticSearchEngine, SybilDefense, TransportError, TrustMetrics, TrustSystem, clawiverseContext, createAgentCard, createDHTOperations, createDefaultTrustScore, createEnvelope, createLegacyAgentCard, createLogger, createMessageRouter, createNode, createSemanticSearch, createTrustSystem, decodeAgentCard, decodeFromCBOR, decodeFromJSON, decodeMessage, decodeMessageJSON, deriveDID, downgradeToLegacyCard, encodeForDHT, encodeForWeb, encodeMessage, encodeMessageJSON, exportKeyPair, extractPublicKey, generateKeyPair, getAgentCardContext, getEncodedSize, importKeyPair, isLegacyCard, isValidContext, matchesCapability, sign, signAgentCard, signEnvelope, signMessage, upgradeLegacyCard, validateAgentCard, validateDID, validateEnvelope, verify, verifyAgentCard, verifyEnvelope, verifyMessage };
1875
+ //# sourceMappingURL=index.js.map
1876
+ //# sourceMappingURL=index.js.map