@highway1/core 0.1.47 → 0.1.51

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 CHANGED
@@ -1,22 +1,10 @@
1
1
  import * as ed25519 from '@noble/ed25519';
2
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 } 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';
3
+ import { WebSocket } from 'ws';
12
4
  import Ajv from 'ajv';
13
5
  import { encode, decode } from 'cbor-x';
14
- import { fromString } from 'uint8arrays/from-string';
15
- import { toString } from 'uint8arrays/to-string';
16
6
  import lunr from 'lunr';
17
7
  import Fuse from 'fuse.js';
18
- import { peerIdFromString } from '@libp2p/peer-id';
19
- import { multiaddr } from '@multiformats/multiaddr';
20
8
  import { Level } from 'level';
21
9
  import { sha256 } from '@noble/hashes/sha256';
22
10
  import { bytesToHex } from '@noble/hashes/utils';
@@ -201,107 +189,282 @@ var createLogger = (prefix, level) => {
201
189
  return new Logger(prefix, level);
202
190
  };
203
191
 
204
- // src/transport/node.ts
205
- var logger = createLogger("transport");
206
- async function createNode(config) {
207
- try {
208
- const {
209
- listenAddresses = ["/ip4/0.0.0.0/tcp/0"],
210
- bootstrapPeers = [],
211
- enableDHT = true,
212
- enableRelay = false,
213
- reserveRelaySlot = false,
214
- enableMDNS = false,
215
- privateKey
216
- } = config;
217
- const relayListenAddrs = reserveRelaySlot ? bootstrapPeers.map((peer) => `${peer}/p2p-circuit`) : [];
218
- const libp2pConfig = {
219
- addresses: {
220
- listen: [...listenAddresses, ...relayListenAddrs]
221
- },
222
- ...privateKey ? { privateKey } : {},
223
- transports: [
224
- tcp(),
225
- circuitRelayTransport()
226
- ],
227
- connectionEncrypters: [noise()],
228
- streamMuxers: [mplex()],
229
- connectionManager: {
230
- minConnections: bootstrapPeers.length > 0 ? 1 : 0,
231
- maxConnections: 50
232
- },
233
- services: {
234
- identify: identify(),
235
- ping: ping()
236
- }
237
- };
238
- if (enableRelay) {
239
- libp2pConfig.services.relay = circuitRelayServer({
240
- reservations: {
241
- maxReservations: 100
242
- // Allow up to 100 concurrent relay reservations
192
+ // src/transport/relay-client.ts
193
+ var logger = createLogger("relay-client");
194
+ var DEFAULT_RECONNECT = {
195
+ baseMs: 1e3,
196
+ jitterMs: 2e3,
197
+ maxDelayMs: 3e4,
198
+ stableAfterMs: 6e4
199
+ };
200
+ function createRelayClient(config) {
201
+ const { relayUrls, did, keyPair, card } = config;
202
+ const reconnectConfig = { ...DEFAULT_RECONNECT, ...config.reconnect };
203
+ const connections = relayUrls.map((url) => ({
204
+ url,
205
+ ws: null,
206
+ connected: false,
207
+ reconnectAttempt: 0,
208
+ reconnectTimer: null,
209
+ stableTimer: null,
210
+ peerCount: 0
211
+ }));
212
+ let deliveryHandler = null;
213
+ let deliveryReportHandler = null;
214
+ let stopped = false;
215
+ async function connectToRelay(conn) {
216
+ if (stopped) return;
217
+ try {
218
+ logger.info("Connecting to relay", { url: conn.url });
219
+ const ws = new WebSocket(conn.url);
220
+ conn.ws = ws;
221
+ await new Promise((resolve, reject) => {
222
+ const timeout = setTimeout(() => {
223
+ reject(new Error("Connection timeout"));
224
+ }, 1e4);
225
+ ws.on("open", async () => {
226
+ clearTimeout(timeout);
227
+ logger.info("WebSocket connected", { url: conn.url });
228
+ try {
229
+ const timestamp = Date.now();
230
+ const helloData = JSON.stringify({ did, card, timestamp });
231
+ const signature = await sign(new TextEncoder().encode(helloData), keyPair.privateKey);
232
+ const hello = {
233
+ type: "HELLO",
234
+ protocolVersion: 1,
235
+ did,
236
+ card,
237
+ timestamp,
238
+ signature
239
+ };
240
+ ws.send(JSON.stringify(hello));
241
+ resolve();
242
+ } catch (err) {
243
+ reject(err);
244
+ }
245
+ });
246
+ ws.on("error", (err) => {
247
+ clearTimeout(timeout);
248
+ reject(err);
249
+ });
250
+ });
251
+ ws.on("message", async (data) => {
252
+ try {
253
+ const msg = JSON.parse(data.toString());
254
+ await handleRelayMessage(conn, msg);
255
+ } catch (err) {
256
+ logger.warn("Failed to parse relay message", { error: err.message });
243
257
  }
244
258
  });
245
- }
246
- if (enableDHT) {
247
- libp2pConfig.services.dht = kadDHT({
248
- clientMode: false,
249
- peerInfoMapper: passthroughMapper,
250
- validators: {
251
- clawiverse: async () => {
252
- }
253
- },
254
- selectors: {
255
- clawiverse: () => 0
256
- },
257
- // Optimize for small networks: reduce replication factor and query timeout
258
- kBucketSize: 20,
259
- // Default K=20, keep for compatibility
260
- querySelfInterval: 3e4,
261
- // Self-query every 30 seconds (libp2p default)
262
- // Allow queries to complete faster in small networks
263
- allowQueryWithZeroPeers: true
259
+ ws.on("close", () => {
260
+ logger.info("WebSocket closed", { url: conn.url });
261
+ conn.connected = false;
262
+ conn.ws = null;
263
+ if (conn.stableTimer) {
264
+ clearTimeout(conn.stableTimer);
265
+ conn.stableTimer = null;
266
+ }
267
+ if (!stopped) {
268
+ scheduleReconnect(conn);
269
+ }
270
+ });
271
+ ws.on("error", (err) => {
272
+ logger.warn("WebSocket error", { url: conn.url, error: err.message });
264
273
  });
274
+ } catch (err) {
275
+ logger.warn("Failed to connect to relay", { url: conn.url, error: err.message });
276
+ conn.ws = null;
277
+ conn.connected = false;
278
+ if (!stopped) {
279
+ scheduleReconnect(conn);
280
+ }
265
281
  }
266
- const peerDiscovery = [];
267
- if (bootstrapPeers.length > 0) peerDiscovery.push(bootstrap({ list: bootstrapPeers }));
268
- if (enableMDNS) {
269
- const { mdns } = await import('@libp2p/mdns');
270
- peerDiscovery.push(mdns());
271
- }
272
- if (peerDiscovery.length > 0) libp2pConfig.peerDiscovery = peerDiscovery;
273
- const libp2p = await createLibp2p(libp2pConfig);
274
- logger.info("Libp2p node created", {
275
- listenAddresses: libp2pConfig.addresses.listen,
276
- reserveRelaySlot
277
- });
278
- return {
279
- libp2p,
280
- start: async () => {
281
- await libp2p.start();
282
- logger.info("Node started", {
283
- peerId: libp2p.peerId.toString(),
284
- addresses: libp2p.getMultiaddrs().map((ma) => ma.toString()),
285
- relay: enableRelay,
286
- reserveRelaySlot
287
- });
288
- },
289
- stop: async () => {
290
- await libp2p.stop();
291
- logger.info("Node stopped");
292
- },
293
- getMultiaddrs: () => {
294
- return libp2p.getMultiaddrs().map((ma) => ma.toString());
295
- },
296
- getPeerId: () => {
297
- return libp2p.peerId.toString();
282
+ }
283
+ function scheduleReconnect(conn) {
284
+ if (conn.reconnectTimer) return;
285
+ const delay = Math.min(
286
+ reconnectConfig.baseMs * Math.pow(2, conn.reconnectAttempt) + Math.random() * reconnectConfig.jitterMs,
287
+ reconnectConfig.maxDelayMs
288
+ );
289
+ conn.reconnectAttempt++;
290
+ logger.debug("Scheduling reconnect", { url: conn.url, attempt: conn.reconnectAttempt, delayMs: delay });
291
+ conn.reconnectTimer = setTimeout(() => {
292
+ conn.reconnectTimer = null;
293
+ connectToRelay(conn);
294
+ }, delay);
295
+ }
296
+ async function handleRelayMessage(conn, msg) {
297
+ switch (msg.type) {
298
+ case "WELCOME": {
299
+ const welcome = msg;
300
+ logger.info("Received WELCOME", { relayId: welcome.relayId, peers: welcome.peers });
301
+ conn.connected = true;
302
+ conn.peerCount = welcome.peers;
303
+ conn.reconnectAttempt = 0;
304
+ if (conn.stableTimer) clearTimeout(conn.stableTimer);
305
+ conn.stableTimer = setTimeout(() => {
306
+ conn.reconnectAttempt = 0;
307
+ logger.debug("Connection stable", { url: conn.url });
308
+ }, reconnectConfig.stableAfterMs);
309
+ break;
298
310
  }
299
- };
300
- } catch (error) {
301
- throw new TransportError("Failed to create transport node", error);
311
+ case "DELIVER": {
312
+ const deliver = msg;
313
+ logger.info("Received DELIVER", { messageId: deliver.messageId, from: deliver.from });
314
+ if (deliveryHandler) {
315
+ await deliveryHandler(deliver);
316
+ }
317
+ break;
318
+ }
319
+ case "DELIVERY_REPORT": {
320
+ const report = msg;
321
+ logger.info("Received DELIVERY_REPORT", { messageId: report.messageId, status: report.status });
322
+ if (deliveryReportHandler) {
323
+ deliveryReportHandler(report);
324
+ }
325
+ break;
326
+ }
327
+ case "PONG": {
328
+ conn.peerCount = msg.peers;
329
+ logger.debug("Received PONG", { peers: msg.peers });
330
+ break;
331
+ }
332
+ default:
333
+ logger.debug("Received relay message", { type: msg.type });
334
+ }
302
335
  }
336
+ function getConnectedConnection() {
337
+ return connections.find((c) => c.connected && c.ws) || null;
338
+ }
339
+ async function sendToRelay(msg) {
340
+ const conn = getConnectedConnection();
341
+ if (!conn || !conn.ws) {
342
+ throw new TransportError("No connected relay");
343
+ }
344
+ conn.ws.send(JSON.stringify(msg));
345
+ }
346
+ return {
347
+ async start() {
348
+ stopped = false;
349
+ logger.info("Starting relay client", { relays: relayUrls.length });
350
+ for (let i = 0; i < connections.length; i++) {
351
+ const conn = connections[i];
352
+ setTimeout(() => {
353
+ connectToRelay(conn);
354
+ }, Math.random() * 2e3);
355
+ }
356
+ const maxWait = 15e3;
357
+ const start = Date.now();
358
+ while (Date.now() - start < maxWait) {
359
+ if (getConnectedConnection()) {
360
+ logger.info("Relay client started");
361
+ return;
362
+ }
363
+ await new Promise((resolve) => setTimeout(resolve, 100));
364
+ }
365
+ throw new TransportError("Failed to connect to any relay");
366
+ },
367
+ async stop() {
368
+ stopped = true;
369
+ logger.info("Stopping relay client");
370
+ for (const conn of connections) {
371
+ if (conn.reconnectTimer) {
372
+ clearTimeout(conn.reconnectTimer);
373
+ conn.reconnectTimer = null;
374
+ }
375
+ if (conn.stableTimer) {
376
+ clearTimeout(conn.stableTimer);
377
+ conn.stableTimer = null;
378
+ }
379
+ if (conn.ws) {
380
+ conn.ws.close();
381
+ conn.ws = null;
382
+ }
383
+ conn.connected = false;
384
+ }
385
+ logger.info("Relay client stopped");
386
+ },
387
+ async sendEnvelope(toDid, envelopeBytes) {
388
+ const msg = {
389
+ type: "SEND",
390
+ to: toDid,
391
+ envelope: envelopeBytes
392
+ };
393
+ await sendToRelay(msg);
394
+ logger.debug("Sent envelope", { to: toDid, size: envelopeBytes.length });
395
+ },
396
+ async discover(query, minTrust, limit) {
397
+ const conn = getConnectedConnection();
398
+ const ws = conn?.ws;
399
+ if (!conn || !ws) {
400
+ throw new TransportError("No connected relay");
401
+ }
402
+ return new Promise((resolve, reject) => {
403
+ const timeout = setTimeout(() => {
404
+ reject(new TransportError("Discover timeout"));
405
+ }, 1e4);
406
+ const handler = (data) => {
407
+ try {
408
+ const msg = JSON.parse(data.toString());
409
+ if (msg.type === "DISCOVERED") {
410
+ clearTimeout(timeout);
411
+ ws.off("message", handler);
412
+ resolve(msg.agents);
413
+ }
414
+ } catch (err) {
415
+ }
416
+ };
417
+ ws.on("message", handler);
418
+ ws.send(JSON.stringify({ type: "DISCOVER", query, minTrust, limit }));
419
+ });
420
+ },
421
+ async fetchCard(did2) {
422
+ const conn = getConnectedConnection();
423
+ const ws = conn?.ws;
424
+ if (!conn || !ws) {
425
+ throw new TransportError("No connected relay");
426
+ }
427
+ return new Promise((resolve, reject) => {
428
+ const timeout = setTimeout(() => {
429
+ reject(new TransportError("Fetch card timeout"));
430
+ }, 5e3);
431
+ const handler = (data) => {
432
+ try {
433
+ const msg = JSON.parse(data.toString());
434
+ if (msg.type === "CARD" && msg.did === did2) {
435
+ clearTimeout(timeout);
436
+ ws.off("message", handler);
437
+ resolve(msg.card);
438
+ }
439
+ } catch (err) {
440
+ }
441
+ };
442
+ ws.on("message", handler);
443
+ ws.send(JSON.stringify({ type: "FETCH_CARD", did: did2 }));
444
+ });
445
+ },
446
+ onDeliver(handler) {
447
+ deliveryHandler = handler;
448
+ },
449
+ onDeliveryReport(handler) {
450
+ deliveryReportHandler = handler;
451
+ },
452
+ isConnected() {
453
+ return getConnectedConnection() !== null;
454
+ },
455
+ getConnectedRelays() {
456
+ return connections.filter((c) => c.connected).map((c) => c.url);
457
+ },
458
+ getPeerCount() {
459
+ const conn = getConnectedConnection();
460
+ return conn ? conn.peerCount : 0;
461
+ }
462
+ };
303
463
  }
304
464
 
465
+ // src/transport/relay-types.ts
466
+ var RELAY_PROTOCOL_VERSION = 1;
467
+
305
468
  // src/discovery/agent-card-types.ts
306
469
  function isLegacyCard(card) {
307
470
  return Array.isArray(card.capabilities) && card.capabilities.length > 0 && typeof card.capabilities[0] === "string";
@@ -557,7 +720,71 @@ function getEncodedSize(card) {
557
720
  json: encodeForWeb(card).length
558
721
  };
559
722
  }
560
- var logger2 = createLogger("search-index");
723
+
724
+ // src/discovery/relay-index.ts
725
+ var logger2 = createLogger("relay-index");
726
+ function createRelayIndexOperations(client) {
727
+ return {
728
+ publishAgentCard: async (card) => {
729
+ logger2.debug("Agent Card published via HELLO", { did: card.did });
730
+ },
731
+ queryAgentCard: async (did) => {
732
+ try {
733
+ const card = await client.fetchCard(did);
734
+ if (card) {
735
+ logger2.debug("Found Agent Card via relay", { did });
736
+ } else {
737
+ logger2.debug("Agent Card not found via relay", { did });
738
+ }
739
+ return card;
740
+ } catch (error) {
741
+ logger2.warn("Failed to query Agent Card", { did, error });
742
+ return null;
743
+ }
744
+ },
745
+ queryByCapability: async (capability) => {
746
+ try {
747
+ const results = await client.discover(capability);
748
+ const cards = results.map((r) => r.card);
749
+ logger2.debug("Query by capability", { capability, count: cards.length });
750
+ return cards;
751
+ } catch (error) {
752
+ throw new DiscoveryError("Failed to query by capability", error);
753
+ }
754
+ },
755
+ searchSemantic: async (query) => {
756
+ try {
757
+ const queryText = query.text || query.capability || "";
758
+ const minTrust = query.filters?.minTrustScore;
759
+ const limit = query.limit || 10;
760
+ const results = await client.discover(queryText, minTrust, limit);
761
+ const cards = results.map((r) => r.card);
762
+ logger2.debug("Semantic search", { query: queryText, count: cards.length });
763
+ return cards;
764
+ } catch (error) {
765
+ throw new DiscoveryError("Failed to perform semantic search", error);
766
+ }
767
+ },
768
+ resolveDID: async (did) => {
769
+ try {
770
+ const relays = client.getConnectedRelays();
771
+ if (relays.length === 0) {
772
+ logger2.debug("DID resolution failed: no connected relays", { did });
773
+ return null;
774
+ }
775
+ logger2.debug("Resolved DID to relay", { did, relay: relays[0] });
776
+ return { relayUrl: relays[0] };
777
+ } catch (error) {
778
+ logger2.warn("Failed to resolve DID", { did, error });
779
+ return null;
780
+ }
781
+ },
782
+ queryRelayPeers: async () => {
783
+ return client.getConnectedRelays();
784
+ }
785
+ };
786
+ }
787
+ var logger3 = createLogger("search-index");
561
788
  var SearchIndex = class {
562
789
  cards = /* @__PURE__ */ new Map();
563
790
  lunrIndex;
@@ -569,7 +796,7 @@ var SearchIndex = class {
569
796
  indexAgentCard(card) {
570
797
  this.cards.set(card.did, card);
571
798
  this.needsRebuild = true;
572
- logger2.debug("Indexed Agent Card", { did: card.did, capabilities: card.capabilities.length });
799
+ logger3.debug("Indexed Agent Card", { did: card.did, capabilities: card.capabilities.length });
573
800
  }
574
801
  /**
575
802
  * Remove an Agent Card from the index
@@ -577,7 +804,7 @@ var SearchIndex = class {
577
804
  removeAgentCard(did) {
578
805
  this.cards.delete(did);
579
806
  this.needsRebuild = true;
580
- logger2.debug("Removed Agent Card from index", { did });
807
+ logger3.debug("Removed Agent Card from index", { did });
581
808
  }
582
809
  /**
583
810
  * Search for agents matching a query
@@ -604,7 +831,7 @@ var SearchIndex = class {
604
831
  if (query.limit) {
605
832
  results = results.slice(0, query.limit);
606
833
  }
607
- logger2.debug("Search completed", { query, results: results.length });
834
+ logger3.debug("Search completed", { query, results: results.length });
608
835
  return results;
609
836
  }
610
837
  /**
@@ -621,7 +848,7 @@ var SearchIndex = class {
621
848
  this.lunrIndex = void 0;
622
849
  this.fuse = void 0;
623
850
  this.needsRebuild = false;
624
- logger2.info("Search index cleared");
851
+ logger3.info("Search index cleared");
625
852
  }
626
853
  /**
627
854
  * Get index size
@@ -633,7 +860,7 @@ var SearchIndex = class {
633
860
  * Rebuild search indexes
634
861
  */
635
862
  rebuild() {
636
- logger2.info("Rebuilding search indexes", { cards: this.cards.size });
863
+ logger3.info("Rebuilding search indexes", { cards: this.cards.size });
637
864
  const cards = Array.from(this.cards.values());
638
865
  this.lunrIndex = lunr(function() {
639
866
  this.ref("did");
@@ -660,7 +887,7 @@ var SearchIndex = class {
660
887
  includeScore: true
661
888
  });
662
889
  this.needsRebuild = false;
663
- logger2.info("Search indexes rebuilt");
890
+ logger3.info("Search indexes rebuilt");
664
891
  }
665
892
  /**
666
893
  * Search by text using Lunr
@@ -871,7 +1098,7 @@ var CapabilityMatcher = class {
871
1098
  };
872
1099
 
873
1100
  // src/discovery/semantic-search.ts
874
- var logger3 = createLogger("semantic-search");
1101
+ var logger4 = createLogger("semantic-search");
875
1102
  var SemanticSearchEngine = class {
876
1103
  constructor(dht) {
877
1104
  this.dht = dht;
@@ -885,12 +1112,12 @@ var SemanticSearchEngine = class {
885
1112
  * Local-first with network fallback
886
1113
  */
887
1114
  async search(query) {
888
- logger3.info("Searching for agents", { query });
1115
+ logger4.info("Searching for agents", { query });
889
1116
  const localResults = this.index.search(query);
890
- logger3.debug("Local search results", { count: localResults.length });
1117
+ logger4.debug("Local search results", { count: localResults.length });
891
1118
  const limit = query.limit || 10;
892
1119
  if (localResults.length < limit && this.dht) {
893
- logger3.debug("Insufficient local results, querying network");
1120
+ logger4.debug("Insufficient local results, querying network");
894
1121
  const networkResults = await this.searchNetwork(query);
895
1122
  return this.mergeResults(localResults, networkResults, limit);
896
1123
  }
@@ -936,17 +1163,17 @@ var SemanticSearchEngine = class {
936
1163
  try {
937
1164
  const capability = query.capability || this.extractPrimaryCapability(query.text);
938
1165
  if (!capability) {
939
- logger3.debug("No capability extracted from query, skipping network search");
1166
+ logger4.debug("No capability extracted from query, skipping network search");
940
1167
  return [];
941
1168
  }
942
1169
  const cards = await this.dht.queryByCapability(capability);
943
- logger3.debug("Network search results", { count: cards.length });
1170
+ logger4.debug("Network search results", { count: cards.length });
944
1171
  return cards.map((card) => {
945
1172
  const score = this.scoreCard(card, query);
946
1173
  return { card, score };
947
1174
  });
948
1175
  } catch (error) {
949
- logger3.error("Network search failed", { error });
1176
+ logger4.error("Network search failed", { error });
950
1177
  return [];
951
1178
  }
952
1179
  }
@@ -1030,236 +1257,6 @@ function createSemanticSearch(dht) {
1030
1257
  return new SemanticSearchEngine(dht);
1031
1258
  }
1032
1259
 
1033
- // src/discovery/dht.ts
1034
- var logger4 = createLogger("dht");
1035
- var peerCache = /* @__PURE__ */ new Map();
1036
- var CACHE_TTL = 5 * 60 * 1e3;
1037
- function extractValue(event) {
1038
- if (event.name === "VALUE" && event.value) return event.value;
1039
- if (event.name === "PEER_RESPONSE" && event.value) return event.value;
1040
- return null;
1041
- }
1042
- function capKey(cap) {
1043
- return cap.toLowerCase().replace(/[^a-z0-9_-]/g, "_");
1044
- }
1045
- async function readDIDList(dht, key) {
1046
- try {
1047
- for await (const event of dht.get(key, { signal: AbortSignal.timeout(3e4) })) {
1048
- const raw = extractValue(event);
1049
- if (raw) {
1050
- const text = toString(raw);
1051
- return text.split("\n").filter(Boolean);
1052
- }
1053
- }
1054
- } catch {
1055
- }
1056
- return [];
1057
- }
1058
- async function writeDIDList(dht, key, dids) {
1059
- const value = fromString([...new Set(dids)].join("\n"));
1060
- for (let attempt = 1; attempt <= 3; attempt++) {
1061
- try {
1062
- for await (const _ of dht.put(key, value, { signal: AbortSignal.timeout(3e4) })) {
1063
- }
1064
- return;
1065
- } catch (e) {
1066
- if (e?.name === "AbortError" && attempt < 3) {
1067
- logger4.debug(`DHT put timeout, retrying (${attempt}/3)...`);
1068
- continue;
1069
- }
1070
- if (e?.name !== "AbortError") throw e;
1071
- }
1072
- }
1073
- }
1074
- function createDHTOperations(libp2p) {
1075
- const operations = {};
1076
- const searchEngine = createSemanticSearch(operations);
1077
- return Object.assign(operations, {
1078
- publishAgentCard: async (card) => {
1079
- try {
1080
- const dht = libp2p.services?.dht;
1081
- if (!dht) throw new DiscoveryError("DHT service not available");
1082
- const agentKey = fromString(`/clawiverse/agent/${card.did}`);
1083
- for (let attempt = 1; attempt <= 3; attempt++) {
1084
- try {
1085
- for await (const _ of dht.put(agentKey, encodeForDHT(card), { signal: AbortSignal.timeout(3e4) })) {
1086
- }
1087
- break;
1088
- } catch (e) {
1089
- if (e?.name === "AbortError" && attempt < 3) {
1090
- logger4.debug(`DHT put agent card timeout, retrying (${attempt}/3)...`);
1091
- continue;
1092
- }
1093
- logger4.warn("DHT put agent card failed (non-fatal)", { error: e.message });
1094
- break;
1095
- }
1096
- }
1097
- const caps = (card.capabilities ?? []).flatMap((c) => {
1098
- if (typeof c === "string") return [c];
1099
- return [c.name, c.id].filter((v) => typeof v === "string" && v.length > 0);
1100
- });
1101
- caps.push("__all__");
1102
- const importantCaps = ["__all__"];
1103
- if (caps.some((cap) => capKey(cap) === "relay")) {
1104
- importantCaps.push("relay");
1105
- }
1106
- await Promise.all(importantCaps.map(async (cap) => {
1107
- const capKeyStr = `/clawiverse/cap/${capKey(cap)}`;
1108
- const capDHTKey = fromString(capKeyStr);
1109
- try {
1110
- const existing = await readDIDList(dht, capDHTKey);
1111
- if (!existing.includes(card.did)) {
1112
- await writeDIDList(dht, capDHTKey, [...existing, card.did]);
1113
- logger4.debug("Indexed capability in DHT", { cap: capKey(cap), did: card.did });
1114
- }
1115
- } catch (e) {
1116
- logger4.warn("Failed to index capability (non-fatal)", { cap: capKey(cap), error: e.message });
1117
- }
1118
- }));
1119
- searchEngine.indexAgentCard(card);
1120
- logger4.info("Published Agent Card to DHT", { did: card.did });
1121
- } catch (error) {
1122
- logger4.warn("Failed to publish Agent Card to DHT (non-fatal)", { error: error.message });
1123
- searchEngine.indexAgentCard(card);
1124
- }
1125
- },
1126
- queryAgentCard: async (did) => {
1127
- try {
1128
- const dht = libp2p.services?.dht;
1129
- if (!dht) throw new DiscoveryError("DHT service not available");
1130
- const key = fromString(`/clawiverse/agent/${did}`);
1131
- for await (const event of dht.get(key)) {
1132
- const raw = extractValue(event);
1133
- if (raw) {
1134
- const card = decodeFromCBOR(raw);
1135
- searchEngine.indexAgentCard(card);
1136
- logger4.debug("Found Agent Card in DHT", { did });
1137
- return card;
1138
- }
1139
- }
1140
- logger4.debug("Agent Card not found in DHT", { did });
1141
- return null;
1142
- } catch (error) {
1143
- logger4.warn("Failed to query Agent Card", { did, error });
1144
- return null;
1145
- }
1146
- },
1147
- queryByCapability: async (capability) => {
1148
- try {
1149
- const dht = libp2p.services?.dht;
1150
- const local = searchEngine.getAllIndexedCards().filter(
1151
- (card) => card.capabilities.some((cap) => {
1152
- const name = typeof cap === "string" ? cap : cap.name;
1153
- return name?.toLowerCase().includes(capability.toLowerCase());
1154
- })
1155
- );
1156
- if (local.length > 0) return local;
1157
- if (!dht) return [];
1158
- const capDHTKey = fromString(`/clawiverse/cap/${capKey(capability)}`);
1159
- const dids = await readDIDList(dht, capDHTKey);
1160
- logger4.debug("DHT capability index", { capability, dids });
1161
- const cards = await Promise.all(
1162
- dids.map(async (did) => {
1163
- const key = fromString(`/clawiverse/agent/${did}`);
1164
- try {
1165
- for await (const event of dht.get(key)) {
1166
- const raw = extractValue(event);
1167
- if (raw) {
1168
- const card = decodeFromCBOR(raw);
1169
- searchEngine.indexAgentCard(card);
1170
- return card;
1171
- }
1172
- }
1173
- } catch {
1174
- }
1175
- return null;
1176
- })
1177
- );
1178
- return cards.filter((c) => c !== null);
1179
- } catch (error) {
1180
- throw new DiscoveryError("Failed to query by capability", error);
1181
- }
1182
- },
1183
- searchSemantic: async (query) => {
1184
- try {
1185
- const dht = libp2p.services?.dht;
1186
- if (dht) {
1187
- const allKey = fromString("/clawiverse/cap/__all__");
1188
- const dids = await readDIDList(dht, allKey);
1189
- logger4.debug("DHT __all__ index", { count: dids.length });
1190
- await Promise.all(
1191
- dids.map(async (did) => {
1192
- const key = fromString(`/clawiverse/agent/${did}`);
1193
- try {
1194
- for await (const event of dht.get(key)) {
1195
- const raw = extractValue(event);
1196
- if (raw) {
1197
- searchEngine.indexAgentCard(decodeFromCBOR(raw));
1198
- break;
1199
- }
1200
- }
1201
- } catch {
1202
- }
1203
- })
1204
- );
1205
- }
1206
- return searchEngine.search(query);
1207
- } catch (error) {
1208
- throw new DiscoveryError("Failed to perform semantic search", error);
1209
- }
1210
- },
1211
- queryRelayPeers: async () => {
1212
- const dht = libp2p.services?.dht;
1213
- if (!dht) return [];
1214
- const capDHTKey = fromString("/clawiverse/cap/relay");
1215
- const dids = await readDIDList(dht, capDHTKey);
1216
- const addrs = [];
1217
- await Promise.all(dids.map(async (did) => {
1218
- const card = await operations.queryAgentCard(did);
1219
- if (card?.endpoints) {
1220
- addrs.push(...card.endpoints.filter((e) => !e.includes("/p2p-circuit/")));
1221
- }
1222
- }));
1223
- return addrs;
1224
- },
1225
- resolveDID: async (did) => {
1226
- try {
1227
- const cached = peerCache.get(did);
1228
- if (cached && Date.now() - cached.timestamp < CACHE_TTL) {
1229
- logger4.debug("Using cached peer info", { did });
1230
- return { peerId: cached.peerId, multiaddrs: cached.multiaddrs };
1231
- }
1232
- const dht = libp2p.services?.dht;
1233
- if (!dht) throw new DiscoveryError("DHT service not available");
1234
- const key = fromString(`/clawiverse/agent/${did}`);
1235
- for await (const event of dht.get(key)) {
1236
- const raw = extractValue(event);
1237
- if (raw) {
1238
- const card = decodeFromCBOR(raw);
1239
- if (card.peerId) {
1240
- logger4.debug("Resolved DID to peer", { did, peerId: card.peerId });
1241
- const result = { peerId: card.peerId, multiaddrs: card.endpoints || [] };
1242
- peerCache.set(did, {
1243
- peerId: result.peerId,
1244
- multiaddrs: result.multiaddrs,
1245
- timestamp: Date.now()
1246
- });
1247
- return result;
1248
- }
1249
- logger4.warn("Agent Card found but has no peerId", { did });
1250
- return null;
1251
- }
1252
- }
1253
- logger4.debug("DID not found in DHT", { did });
1254
- return null;
1255
- } catch (error) {
1256
- logger4.warn("Failed to resolve DID", { did, error });
1257
- return null;
1258
- }
1259
- }
1260
- });
1261
- }
1262
-
1263
1260
  // src/messaging/envelope.ts
1264
1261
  function createEnvelope(from, to, type, protocol, payload, replyTo) {
1265
1262
  return {
@@ -1335,23 +1332,13 @@ function decodeMessageJSON(json) {
1335
1332
  throw new MessagingError("Failed to decode message from JSON", error);
1336
1333
  }
1337
1334
  }
1335
+
1336
+ // src/messaging/router.ts
1338
1337
  var logger5 = createLogger("router");
1339
- function concatUint8Arrays(arrays) {
1340
- if (arrays.length === 0) return new Uint8Array(0);
1341
- if (arrays.length === 1) return arrays[0];
1342
- const totalLength = arrays.reduce((acc, arr) => acc + arr.length, 0);
1343
- const result = new Uint8Array(totalLength);
1344
- let offset = 0;
1345
- for (const arr of arrays) {
1346
- result.set(arr, offset);
1347
- offset += arr.length;
1348
- }
1349
- return result;
1350
- }
1351
- function createMessageRouter(libp2p, verifyFn, dht, relayPeers) {
1338
+ function createMessageRouter(relayClient, verifyFn) {
1352
1339
  const handlers = /* @__PURE__ */ new Map();
1353
1340
  let catchAllHandler;
1354
- const PROTOCOL_PREFIX = "/clawiverse/msg/1.0.0";
1341
+ const pendingRequests = /* @__PURE__ */ new Map();
1355
1342
  return {
1356
1343
  registerHandler: (protocol, handler) => {
1357
1344
  handlers.set(protocol, handler);
@@ -1365,134 +1352,14 @@ function createMessageRouter(libp2p, verifyFn, dht, relayPeers) {
1365
1352
  catchAllHandler = handler;
1366
1353
  logger5.info("Registered catch-all message handler");
1367
1354
  },
1368
- sendMessage: async (envelope, peerHint) => {
1355
+ sendMessage: async (envelope) => {
1369
1356
  try {
1370
1357
  if (!validateEnvelope(envelope)) {
1371
1358
  throw new MessagingError("Invalid message envelope");
1372
1359
  }
1373
- let targetPeerIdStr;
1374
- let targetMultiaddrs = [];
1375
- if (peerHint) {
1376
- targetPeerIdStr = peerHint.peerId;
1377
- targetMultiaddrs = peerHint.multiaddrs;
1378
- logger5.info("Using peer hint for direct addressing", { peerId: targetPeerIdStr });
1379
- } else if (dht) {
1380
- const resolved = await dht.resolveDID(envelope.to);
1381
- if (resolved) {
1382
- targetPeerIdStr = resolved.peerId;
1383
- targetMultiaddrs = resolved.multiaddrs;
1384
- }
1385
- }
1386
- if (!targetPeerIdStr) {
1387
- throw new MessagingError(
1388
- `Cannot resolve recipient: ${envelope.to} \u2014 provide peerHint or ensure agent is in DHT`
1389
- );
1390
- }
1391
- const targetPeerId = peerIdFromString(targetPeerIdStr);
1392
- if (targetMultiaddrs.length > 0) {
1393
- const mas = targetMultiaddrs.map((a) => multiaddr(a));
1394
- await libp2p.peerStore.merge(targetPeerId, { multiaddrs: mas });
1395
- }
1396
- logger5.info("Dialing peer for message delivery", {
1397
- peerId: targetPeerIdStr,
1398
- multiaddrs: targetMultiaddrs
1399
- });
1400
- let stream;
1401
- const DIAL_TIMEOUT = 3e3;
1402
- const relayMultiaddrs = targetMultiaddrs.filter((a) => a.includes("/p2p-circuit/"));
1403
- const directMultiaddrs = targetMultiaddrs.filter((a) => !a.includes("/p2p-circuit/"));
1404
- if (directMultiaddrs.length > 0) {
1405
- const directDialPromises = directMultiaddrs.map(async (addr) => {
1406
- try {
1407
- const conn = await libp2p.dial(multiaddr(addr), {
1408
- signal: AbortSignal.timeout(DIAL_TIMEOUT)
1409
- });
1410
- const s = await conn.newStream(PROTOCOL_PREFIX, { runOnLimitedConnection: true });
1411
- logger5.info("Direct dial succeeded", { addr });
1412
- return { conn, stream: s };
1413
- } catch {
1414
- return null;
1415
- }
1416
- });
1417
- const winner = await Promise.race(
1418
- directDialPromises.map((p) => p.then((r) => r || Promise.reject()))
1419
- ).catch(() => void 0);
1420
- if (winner) {
1421
- stream = winner.stream;
1422
- Promise.allSettled(directDialPromises).then((results) => {
1423
- for (const result of results) {
1424
- if (result.status === "fulfilled" && result.value && result.value.stream !== stream) {
1425
- result.value.conn.close().catch(() => {
1426
- });
1427
- }
1428
- }
1429
- });
1430
- }
1431
- }
1432
- let lastError;
1433
- if (!stream) {
1434
- const allRelayAddrs = [
1435
- ...relayMultiaddrs,
1436
- ...(relayPeers ?? []).map((r) => buildCircuitRelayAddr(r, targetPeerIdStr))
1437
- ];
1438
- const uniqueRelayAddrs = [...new Set(allRelayAddrs)];
1439
- if (uniqueRelayAddrs.length > 0) {
1440
- const relayDialPromises = uniqueRelayAddrs.map(async (addr) => {
1441
- try {
1442
- const conn = await libp2p.dial(multiaddr(addr), {
1443
- signal: AbortSignal.timeout(DIAL_TIMEOUT)
1444
- });
1445
- logger5.info("Relay connection established", { addr });
1446
- const s = await conn.newStream(PROTOCOL_PREFIX, { runOnLimitedConnection: true });
1447
- logger5.info("Relay stream opened", { addr });
1448
- return { conn, stream: s };
1449
- } catch (relayErr) {
1450
- logger5.warn("Relay dial failed", { addr, error: relayErr.message });
1451
- lastError = relayErr;
1452
- return null;
1453
- }
1454
- });
1455
- const winner = await Promise.race(
1456
- relayDialPromises.map((p) => p.then((r) => r || Promise.reject()))
1457
- ).catch(() => void 0);
1458
- if (winner) {
1459
- stream = winner.stream;
1460
- Promise.allSettled(relayDialPromises).then((results) => {
1461
- for (const result of results) {
1462
- if (result.status === "fulfilled" && result.value && result.value.stream !== stream) {
1463
- result.value.conn.close().catch(() => {
1464
- });
1465
- }
1466
- }
1467
- });
1468
- }
1469
- }
1470
- }
1471
- if (!stream && dht && "queryRelayPeers" in dht) {
1472
- const discoveredRelays = await dht.queryRelayPeers();
1473
- for (const relayAddr of discoveredRelays) {
1474
- const circuitAddr = buildCircuitRelayAddr(relayAddr, targetPeerIdStr);
1475
- try {
1476
- const conn = await libp2p.dial(multiaddr(circuitAddr));
1477
- stream = await conn.newStream(PROTOCOL_PREFIX, { runOnLimitedConnection: true });
1478
- logger5.info("DHT-discovered relay succeeded", { relayAddr });
1479
- break;
1480
- } catch (e) {
1481
- logger5.warn("DHT relay failed", { relayAddr, error: e.message });
1482
- lastError = e;
1483
- }
1484
- }
1485
- }
1486
- if (!stream) {
1487
- throw lastError ?? new MessagingError("All dial attempts failed (including DHT-discovered relays)");
1488
- }
1489
1360
  const encoded = encodeMessage(envelope);
1490
- await stream.sink(
1491
- (async function* () {
1492
- yield encoded;
1493
- })()
1494
- );
1495
- logger5.info("Message sent over libp2p stream", {
1361
+ await relayClient.sendEnvelope(envelope.to, encoded);
1362
+ logger5.info("Message sent via relay", {
1496
1363
  id: envelope.id,
1497
1364
  from: envelope.from,
1498
1365
  to: envelope.to,
@@ -1500,38 +1367,15 @@ function createMessageRouter(libp2p, verifyFn, dht, relayPeers) {
1500
1367
  });
1501
1368
  if (envelope.type === "request") {
1502
1369
  logger5.debug("Waiting for response to request", { id: envelope.id });
1503
- try {
1504
- const RESPONSE_TIMEOUT = 5e3;
1505
- const responsePromise = (async () => {
1506
- const responseChunks = [];
1507
- for await (const chunk of stream.source) {
1508
- responseChunks.push(chunk.subarray());
1509
- }
1510
- if (responseChunks.length > 0) {
1511
- const responseData = concatUint8Arrays(responseChunks);
1512
- const responseEnvelope = decodeMessage(responseData);
1513
- logger5.info("Received response", {
1514
- id: responseEnvelope.id,
1515
- replyTo: responseEnvelope.replyTo
1516
- });
1517
- return responseEnvelope;
1518
- } else {
1519
- logger5.warn("No response received for request", { id: envelope.id });
1520
- return void 0;
1521
- }
1522
- })();
1523
- const timeoutPromise = new Promise((resolve) => {
1524
- setTimeout(() => {
1525
- logger5.warn("Response timeout", { id: envelope.id, timeout: RESPONSE_TIMEOUT });
1526
- resolve(void 0);
1527
- }, RESPONSE_TIMEOUT);
1528
- });
1529
- const response = await Promise.race([responsePromise, timeoutPromise]);
1530
- return response;
1531
- } catch (error) {
1532
- logger5.warn("Error reading response", { error });
1533
- return void 0;
1534
- }
1370
+ const RESPONSE_TIMEOUT = 3e4;
1371
+ return new Promise((resolve) => {
1372
+ const timeout = setTimeout(() => {
1373
+ pendingRequests.delete(envelope.id);
1374
+ logger5.warn("Response timeout", { id: envelope.id, timeout: RESPONSE_TIMEOUT });
1375
+ resolve(void 0);
1376
+ }, RESPONSE_TIMEOUT);
1377
+ pendingRequests.set(envelope.id, { resolve, timeout });
1378
+ });
1535
1379
  }
1536
1380
  return void 0;
1537
1381
  } catch (error) {
@@ -1540,107 +1384,671 @@ function createMessageRouter(libp2p, verifyFn, dht, relayPeers) {
1540
1384
  }
1541
1385
  },
1542
1386
  start: async () => {
1543
- await libp2p.handle(PROTOCOL_PREFIX, async ({ stream }) => {
1387
+ relayClient.onDeliver(async (deliverMsg) => {
1544
1388
  try {
1545
- await handleIncomingStream(stream, handlers, catchAllHandler, verifyFn);
1389
+ const envelope = decodeMessage(deliverMsg.envelope);
1390
+ if (!validateEnvelope(envelope)) {
1391
+ logger5.warn("Received invalid message envelope");
1392
+ return;
1393
+ }
1394
+ const isValidSignature = await verifyEnvelope(envelope, async (signature, data) => {
1395
+ const senderPublicKey = extractPublicKey(envelope.from);
1396
+ return verify(signature, data, senderPublicKey);
1397
+ });
1398
+ if (!isValidSignature) {
1399
+ logger5.warn("Received message with invalid signature", {
1400
+ id: envelope.id,
1401
+ from: envelope.from
1402
+ });
1403
+ return;
1404
+ }
1405
+ try {
1406
+ const { signature, ...envelopeWithoutSig } = envelope;
1407
+ const dataBytes = new TextEncoder().encode(JSON.stringify(envelopeWithoutSig));
1408
+ const signatureBytes = Buffer.from(signature, "hex");
1409
+ const hookValid = await verifyFn(signatureBytes, dataBytes);
1410
+ if (!hookValid) {
1411
+ logger5.warn("Message rejected by custom verifier", {
1412
+ id: envelope.id,
1413
+ from: envelope.from
1414
+ });
1415
+ return;
1416
+ }
1417
+ } catch (error) {
1418
+ logger5.warn("Custom verification hook failed", {
1419
+ id: envelope.id,
1420
+ from: envelope.from,
1421
+ error: error.message
1422
+ });
1423
+ return;
1424
+ }
1425
+ logger5.info("Received message", {
1426
+ id: envelope.id,
1427
+ from: envelope.from,
1428
+ to: envelope.to,
1429
+ protocol: envelope.protocol,
1430
+ type: envelope.type
1431
+ });
1432
+ if (envelope.type === "response" && envelope.replyTo) {
1433
+ const pending = pendingRequests.get(envelope.replyTo);
1434
+ if (pending) {
1435
+ clearTimeout(pending.timeout);
1436
+ pendingRequests.delete(envelope.replyTo);
1437
+ pending.resolve(envelope);
1438
+ logger5.info("Matched response to pending request", {
1439
+ requestId: envelope.replyTo,
1440
+ responseId: envelope.id
1441
+ });
1442
+ return;
1443
+ }
1444
+ }
1445
+ const handler = handlers.get(envelope.protocol);
1446
+ let response = void 0;
1447
+ if (handler) {
1448
+ response = await handler(envelope);
1449
+ } else if (catchAllHandler) {
1450
+ logger5.debug("Using catch-all handler for protocol", { protocol: envelope.protocol });
1451
+ response = await catchAllHandler(envelope);
1452
+ } else {
1453
+ logger5.warn("No handler for protocol", { protocol: envelope.protocol });
1454
+ }
1455
+ if (response) {
1456
+ const encoded = encodeMessage(response);
1457
+ await relayClient.sendEnvelope(response.to, encoded);
1458
+ logger5.info("Sent response back to sender", {
1459
+ responseId: response.id,
1460
+ replyTo: response.replyTo
1461
+ });
1462
+ }
1546
1463
  } catch (error) {
1547
- logger5.error("Error handling incoming stream", error);
1464
+ logger5.error("Error handling incoming message", error);
1548
1465
  }
1549
- }, { runOnLimitedConnection: true });
1466
+ });
1550
1467
  logger5.info("Message router started");
1551
1468
  },
1552
1469
  stop: async () => {
1553
- await libp2p.unhandle(PROTOCOL_PREFIX);
1470
+ for (const [, pending] of pendingRequests.entries()) {
1471
+ clearTimeout(pending.timeout);
1472
+ pending.resolve(void 0);
1473
+ }
1474
+ pendingRequests.clear();
1554
1475
  handlers.clear();
1555
1476
  catchAllHandler = void 0;
1556
1477
  logger5.info("Message router stopped");
1557
1478
  }
1558
1479
  };
1559
1480
  }
1560
- function buildCircuitRelayAddr(relayAddr, targetPeerId) {
1561
- return `${relayAddr}/p2p-circuit/p2p/${targetPeerId}`;
1562
- }
1563
- async function handleIncomingStream(stream, handlers, catchAllHandler, verifyFn) {
1564
- try {
1565
- const chunks = [];
1566
- for await (const chunk of stream.source) {
1567
- chunks.push(chunk.subarray());
1568
- }
1569
- const data = concatUint8Arrays(chunks);
1570
- if (data.length === 0) {
1571
- logger5.debug("Received empty stream, ignoring");
1572
- return;
1573
- }
1574
- const envelope = decodeMessage(data);
1575
- if (!validateEnvelope(envelope)) {
1576
- logger5.warn("Received invalid message envelope");
1577
- return;
1578
- }
1579
- const isValidSignature = await verifyEnvelope(envelope, async (signature, data2) => {
1580
- const senderPublicKey = extractPublicKey(envelope.from);
1581
- return verify(signature, data2, senderPublicKey);
1582
- });
1583
- if (!isValidSignature) {
1584
- logger5.warn("Received message with invalid signature", {
1585
- id: envelope.id,
1586
- from: envelope.from
1587
- });
1588
- return;
1481
+ var logger6 = createLogger("message-storage");
1482
+ var MessageStorage = class {
1483
+ db;
1484
+ ready = false;
1485
+ constructor(dbPath) {
1486
+ this.db = new Level(dbPath, { valueEncoding: "json" });
1487
+ }
1488
+ async open() {
1489
+ await this.db.open();
1490
+ this.ready = true;
1491
+ logger6.info("Message storage opened");
1492
+ }
1493
+ async close() {
1494
+ if (this.ready) {
1495
+ await this.db.close();
1496
+ this.ready = false;
1497
+ logger6.info("Message storage closed");
1498
+ }
1499
+ }
1500
+ // ─── Message Operations ───────────────────────────────────────────────────
1501
+ async putMessage(msg) {
1502
+ const ts = String(msg.receivedAt ?? msg.sentAt ?? Date.now()).padStart(16, "0");
1503
+ const key = `msg:${msg.direction}:${ts}:${msg.envelope.id}`;
1504
+ await this.db.put(key, msg);
1505
+ const idxKey = `idx:from:${msg.envelope.from}:${ts}:${msg.envelope.id}`;
1506
+ await this.db.put(idxKey, "1");
1507
+ }
1508
+ async getMessage(id) {
1509
+ for (const direction of ["inbound", "outbound"]) {
1510
+ const prefix = `msg:${direction}:`;
1511
+ for await (const [, value] of this.db.iterator({
1512
+ gte: prefix,
1513
+ lte: prefix + "\xFF",
1514
+ valueEncoding: "json"
1515
+ })) {
1516
+ if (value.envelope.id === id) return value;
1517
+ }
1518
+ }
1519
+ return null;
1520
+ }
1521
+ async updateMessage(id, updates) {
1522
+ const msg = await this.getMessage(id);
1523
+ if (!msg) return;
1524
+ const ts = String(msg.receivedAt ?? msg.sentAt ?? Date.now()).padStart(16, "0");
1525
+ const key = `msg:${msg.direction}:${ts}:${id}`;
1526
+ await this.db.put(key, { ...msg, ...updates });
1527
+ }
1528
+ async deleteMessage(id) {
1529
+ const msg = await this.getMessage(id);
1530
+ if (!msg) return;
1531
+ const ts = String(msg.receivedAt ?? msg.sentAt ?? Date.now()).padStart(16, "0");
1532
+ const key = `msg:${msg.direction}:${ts}:${id}`;
1533
+ const idxKey = `idx:from:${msg.envelope.from}:${ts}:${id}`;
1534
+ await this.db.batch([
1535
+ { type: "del", key },
1536
+ { type: "del", key: idxKey }
1537
+ ]);
1538
+ }
1539
+ async queryMessages(direction, filter = {}, pagination = {}) {
1540
+ const { limit = 50, offset = 0 } = pagination;
1541
+ const prefix = `msg:${direction}:`;
1542
+ const results = [];
1543
+ let total = 0;
1544
+ let skipped = 0;
1545
+ for await (const [, value] of this.db.iterator({
1546
+ gte: prefix,
1547
+ lte: prefix + "\xFF",
1548
+ reverse: true,
1549
+ // newest first
1550
+ valueEncoding: "json"
1551
+ })) {
1552
+ if (!this.matchesFilter(value, filter)) continue;
1553
+ total++;
1554
+ if (skipped < offset) {
1555
+ skipped++;
1556
+ continue;
1557
+ }
1558
+ if (results.length < limit) results.push(value);
1559
+ }
1560
+ return {
1561
+ messages: results,
1562
+ total,
1563
+ hasMore: total > offset + results.length
1564
+ };
1565
+ }
1566
+ matchesFilter(msg, filter) {
1567
+ if (filter.fromDid) {
1568
+ const froms = Array.isArray(filter.fromDid) ? filter.fromDid : [filter.fromDid];
1569
+ if (!froms.includes(msg.envelope.from)) return false;
1570
+ }
1571
+ if (filter.toDid) {
1572
+ const tos = Array.isArray(filter.toDid) ? filter.toDid : [filter.toDid];
1573
+ if (!tos.includes(msg.envelope.to)) return false;
1574
+ }
1575
+ if (filter.protocol) {
1576
+ const protos = Array.isArray(filter.protocol) ? filter.protocol : [filter.protocol];
1577
+ if (!protos.includes(msg.envelope.protocol)) return false;
1589
1578
  }
1579
+ if (filter.type && msg.envelope.type !== filter.type) return false;
1580
+ if (filter.unreadOnly && msg.readAt != null) return false;
1581
+ if (filter.status) {
1582
+ const statuses = Array.isArray(filter.status) ? filter.status : [filter.status];
1583
+ if (!statuses.includes(msg.status)) return false;
1584
+ }
1585
+ if (filter.maxAge) {
1586
+ const age = Date.now() - (msg.receivedAt ?? msg.sentAt ?? 0);
1587
+ if (age > filter.maxAge) return false;
1588
+ }
1589
+ if (filter.minTrustScore != null && (msg.trustScore ?? 0) < filter.minTrustScore) return false;
1590
+ return true;
1591
+ }
1592
+ async countMessages(direction, filter = {}) {
1593
+ const prefix = `msg:${direction}:`;
1594
+ let count = 0;
1595
+ for await (const [, value] of this.db.iterator({
1596
+ gte: prefix,
1597
+ lte: prefix + "\xFF",
1598
+ valueEncoding: "json"
1599
+ })) {
1600
+ if (this.matchesFilter(value, filter)) count++;
1601
+ }
1602
+ return count;
1603
+ }
1604
+ // ─── Blocklist ────────────────────────────────────────────────────────────
1605
+ async putBlock(entry) {
1606
+ await this.db.put(`block:${entry.did}`, entry);
1607
+ }
1608
+ async getBlock(did) {
1590
1609
  try {
1591
- const { signature, ...envelopeWithoutSig } = envelope;
1592
- const dataBytes = new TextEncoder().encode(JSON.stringify(envelopeWithoutSig));
1593
- const signatureBytes = Buffer.from(signature, "hex");
1594
- const hookValid = await verifyFn(signatureBytes, dataBytes);
1595
- if (!hookValid) {
1596
- logger5.warn("Message rejected by custom verifier", {
1597
- id: envelope.id,
1598
- from: envelope.from
1610
+ return await this.db.get(`block:${did}`);
1611
+ } catch {
1612
+ return null;
1613
+ }
1614
+ }
1615
+ async deleteBlock(did) {
1616
+ try {
1617
+ await this.db.del(`block:${did}`);
1618
+ } catch {
1619
+ }
1620
+ }
1621
+ async listBlocked() {
1622
+ const results = [];
1623
+ for await (const [, value] of this.db.iterator({
1624
+ gte: "block:",
1625
+ lte: "block:\xFF",
1626
+ valueEncoding: "json"
1627
+ })) {
1628
+ results.push(value);
1629
+ }
1630
+ return results;
1631
+ }
1632
+ // ─── Allowlist ────────────────────────────────────────────────────────────
1633
+ async putAllow(entry) {
1634
+ await this.db.put(`allow:${entry.did}`, entry);
1635
+ }
1636
+ async getAllow(did) {
1637
+ try {
1638
+ return await this.db.get(`allow:${did}`);
1639
+ } catch {
1640
+ return null;
1641
+ }
1642
+ }
1643
+ async deleteAllow(did) {
1644
+ try {
1645
+ await this.db.del(`allow:${did}`);
1646
+ } catch {
1647
+ }
1648
+ }
1649
+ async listAllowed() {
1650
+ const results = [];
1651
+ for await (const [, value] of this.db.iterator({
1652
+ gte: "allow:",
1653
+ lte: "allow:\xFF",
1654
+ valueEncoding: "json"
1655
+ })) {
1656
+ results.push(value);
1657
+ }
1658
+ return results;
1659
+ }
1660
+ // ─── Seen Cache ───────────────────────────────────────────────────────────
1661
+ async putSeen(entry) {
1662
+ await this.db.put(`seen:${entry.messageId}`, entry);
1663
+ }
1664
+ async getSeen(messageId) {
1665
+ try {
1666
+ return await this.db.get(`seen:${messageId}`);
1667
+ } catch {
1668
+ return null;
1669
+ }
1670
+ }
1671
+ async cleanupSeen(maxAgeMs) {
1672
+ const cutoff = Date.now() - maxAgeMs;
1673
+ const toDelete = [];
1674
+ for await (const [key, value] of this.db.iterator({
1675
+ gte: "seen:",
1676
+ lte: "seen:\xFF",
1677
+ valueEncoding: "json"
1678
+ })) {
1679
+ if (value.seenAt < cutoff) toDelete.push(key);
1680
+ }
1681
+ await this.db.batch(toDelete.map((key) => ({ type: "del", key })));
1682
+ }
1683
+ // ─── Rate Limit State ─────────────────────────────────────────────────────
1684
+ async putRateLimit(state) {
1685
+ await this.db.put(`rate:${state.did}`, state);
1686
+ }
1687
+ async getRateLimit(did) {
1688
+ try {
1689
+ return await this.db.get(`rate:${did}`);
1690
+ } catch {
1691
+ return null;
1692
+ }
1693
+ }
1694
+ async cleanupRateLimits(maxAgeMs) {
1695
+ const cutoff = Date.now() - maxAgeMs;
1696
+ const toDelete = [];
1697
+ for await (const [key, value] of this.db.iterator({
1698
+ gte: "rate:",
1699
+ lte: "rate:\xFF",
1700
+ valueEncoding: "json"
1701
+ })) {
1702
+ if (value.lastRefill < cutoff) toDelete.push(key);
1703
+ }
1704
+ await this.db.batch(toDelete.map((key) => ({ type: "del", key })));
1705
+ }
1706
+ };
1707
+
1708
+ // src/messaging/queue.ts
1709
+ var logger7 = createLogger("message-queue");
1710
+ var MessageQueue = class {
1711
+ storage;
1712
+ subscriptions = /* @__PURE__ */ new Map();
1713
+ subCounter = 0;
1714
+ constructor(config) {
1715
+ this.storage = new MessageStorage(config.dbPath);
1716
+ }
1717
+ get store() {
1718
+ return this.storage;
1719
+ }
1720
+ async start() {
1721
+ await this.storage.open();
1722
+ logger7.info("Message queue started");
1723
+ }
1724
+ async stop() {
1725
+ await this.storage.close();
1726
+ this.subscriptions.clear();
1727
+ logger7.info("Message queue stopped");
1728
+ }
1729
+ // ─── Inbox ────────────────────────────────────────────────────────────────
1730
+ async getInbox(filter = {}, pagination = {}) {
1731
+ return this.storage.queryMessages("inbound", filter, pagination);
1732
+ }
1733
+ async getMessage(id) {
1734
+ return this.storage.getMessage(id);
1735
+ }
1736
+ async markAsRead(id) {
1737
+ await this.storage.updateMessage(id, { readAt: Date.now() });
1738
+ }
1739
+ async deleteMessage(id) {
1740
+ await this.storage.deleteMessage(id);
1741
+ }
1742
+ // ─── Outbox ───────────────────────────────────────────────────────────────
1743
+ async getOutbox(pagination = {}) {
1744
+ return this.storage.queryMessages("outbound", {}, pagination);
1745
+ }
1746
+ async retryMessage(id) {
1747
+ await this.storage.updateMessage(id, { status: "pending", error: void 0 });
1748
+ }
1749
+ // ─── Enqueue ──────────────────────────────────────────────────────────────
1750
+ async enqueueInbound(envelope, trustScore) {
1751
+ const msg = {
1752
+ envelope,
1753
+ direction: "inbound",
1754
+ status: "pending",
1755
+ receivedAt: Date.now(),
1756
+ trustScore
1757
+ };
1758
+ await this.storage.putMessage(msg);
1759
+ logger7.debug("Enqueued inbound message", { id: envelope.id, from: envelope.from });
1760
+ this.notifySubscribers(msg);
1761
+ return msg;
1762
+ }
1763
+ async enqueueOutbound(envelope) {
1764
+ const msg = {
1765
+ envelope,
1766
+ direction: "outbound",
1767
+ status: "pending",
1768
+ sentAt: Date.now()
1769
+ };
1770
+ await this.storage.putMessage(msg);
1771
+ logger7.debug("Enqueued outbound message", { id: envelope.id, to: envelope.to });
1772
+ return msg;
1773
+ }
1774
+ async markOutboundDelivered(id) {
1775
+ await this.storage.updateMessage(id, { status: "delivered" });
1776
+ }
1777
+ async markOutboundFailed(id, error) {
1778
+ await this.storage.updateMessage(id, { status: "failed", error });
1779
+ }
1780
+ // ─── Subscriptions ────────────────────────────────────────────────────────
1781
+ subscribe(filter, callback) {
1782
+ const id = `sub_${++this.subCounter}`;
1783
+ this.subscriptions.set(id, { id, filter, callback });
1784
+ logger7.debug("Subscription added", { id });
1785
+ return id;
1786
+ }
1787
+ unsubscribe(subscriptionId) {
1788
+ this.subscriptions.delete(subscriptionId);
1789
+ logger7.debug("Subscription removed", { id: subscriptionId });
1790
+ }
1791
+ notifySubscribers(msg) {
1792
+ for (const sub of this.subscriptions.values()) {
1793
+ if (this.matchesSubscriptionFilter(msg, sub.filter)) {
1794
+ Promise.resolve(sub.callback(msg)).catch((err) => {
1795
+ logger7.warn("Subscription callback error", { id: sub.id, error: err.message });
1599
1796
  });
1600
- return;
1601
1797
  }
1602
- } catch (error) {
1603
- logger5.warn("Custom verification hook failed", {
1798
+ }
1799
+ }
1800
+ matchesSubscriptionFilter(msg, filter) {
1801
+ if (filter.fromDid) {
1802
+ const froms = Array.isArray(filter.fromDid) ? filter.fromDid : [filter.fromDid];
1803
+ if (!froms.includes(msg.envelope.from)) return false;
1804
+ }
1805
+ if (filter.protocol) {
1806
+ const protos = Array.isArray(filter.protocol) ? filter.protocol : [filter.protocol];
1807
+ if (!protos.includes(msg.envelope.protocol)) return false;
1808
+ }
1809
+ if (filter.type && msg.envelope.type !== filter.type) return false;
1810
+ return true;
1811
+ }
1812
+ // ─── Stats ────────────────────────────────────────────────────────────────
1813
+ async getStats() {
1814
+ const [inboxTotal, inboxUnread, outboxPending, outboxFailed, blocked, allowed] = await Promise.all([
1815
+ this.storage.countMessages("inbound"),
1816
+ this.storage.countMessages("inbound", { unreadOnly: true }),
1817
+ this.storage.countMessages("outbound", { status: "pending" }),
1818
+ this.storage.countMessages("outbound", { status: "failed" }),
1819
+ this.storage.listBlocked().then((l) => l.length),
1820
+ this.storage.listAllowed().then((l) => l.length)
1821
+ ]);
1822
+ return {
1823
+ inboxTotal,
1824
+ inboxUnread,
1825
+ outboxPending,
1826
+ outboxFailed,
1827
+ blockedAgents: blocked,
1828
+ allowedAgents: allowed,
1829
+ rateLimitedAgents: 0
1830
+ };
1831
+ }
1832
+ };
1833
+
1834
+ // src/messaging/rate-limiter.ts
1835
+ var TokenBucket = class {
1836
+ tokens;
1837
+ lastRefill;
1838
+ capacity;
1839
+ refillRate;
1840
+ // tokens per ms
1841
+ constructor(config, initialTokens, lastRefill) {
1842
+ this.capacity = config.capacity;
1843
+ this.refillRate = config.refillRate;
1844
+ this.tokens = initialTokens ?? config.capacity;
1845
+ this.lastRefill = lastRefill ?? Date.now();
1846
+ }
1847
+ /** Attempt to consume one token. Returns true if allowed. */
1848
+ consume() {
1849
+ this.refill();
1850
+ if (this.tokens >= 1) {
1851
+ this.tokens -= 1;
1852
+ return true;
1853
+ }
1854
+ return false;
1855
+ }
1856
+ getRemaining() {
1857
+ this.refill();
1858
+ return Math.floor(this.tokens);
1859
+ }
1860
+ /** Milliseconds until at least one token is available */
1861
+ getResetTime() {
1862
+ this.refill();
1863
+ if (this.tokens >= 1) return 0;
1864
+ const needed = 1 - this.tokens;
1865
+ return Math.ceil(needed / this.refillRate);
1866
+ }
1867
+ /** Serialize state for persistence */
1868
+ toState() {
1869
+ return { tokens: this.tokens, lastRefill: this.lastRefill };
1870
+ }
1871
+ refill() {
1872
+ const now = Date.now();
1873
+ const elapsed = now - this.lastRefill;
1874
+ const newTokens = elapsed * this.refillRate;
1875
+ this.tokens = Math.min(this.capacity, this.tokens + newTokens);
1876
+ this.lastRefill = now;
1877
+ }
1878
+ };
1879
+ var DEFAULT_RATE_LIMIT_TIERS = {
1880
+ newAgent: { capacity: 10, refillRate: 10 / (60 * 1e3) },
1881
+ // 10/min, burst 10
1882
+ established: { capacity: 100, refillRate: 60 / (60 * 1e3) },
1883
+ // 60/min, burst 100
1884
+ trusted: { capacity: 1e3, refillRate: 600 / (60 * 1e3) }
1885
+ // 600/min, burst 1000
1886
+ };
1887
+ function getTierConfig(trustScore, tiers) {
1888
+ if (trustScore >= 0.6) return tiers.trusted;
1889
+ if (trustScore >= 0.3) return tiers.established;
1890
+ return tiers.newAgent;
1891
+ }
1892
+
1893
+ // src/messaging/defense.ts
1894
+ var logger8 = createLogger("defense");
1895
+ var DefenseMiddleware = class {
1896
+ trust;
1897
+ storage;
1898
+ minTrustScore;
1899
+ autoBlockThreshold;
1900
+ tiers;
1901
+ seenTtlMs;
1902
+ // In-memory LRU-style seen cache (backed by LevelDB for persistence)
1903
+ seenCache = /* @__PURE__ */ new Map();
1904
+ // id → seenAt
1905
+ MAX_SEEN_CACHE = 1e4;
1906
+ // In-memory token buckets (backed by LevelDB for persistence)
1907
+ buckets = /* @__PURE__ */ new Map();
1908
+ constructor(config) {
1909
+ this.trust = config.trustSystem;
1910
+ this.storage = config.storage;
1911
+ this.minTrustScore = config.minTrustScore ?? 0;
1912
+ this.autoBlockThreshold = config.autoBlockThreshold ?? 0.1;
1913
+ this.tiers = config.rateLimitTiers ?? DEFAULT_RATE_LIMIT_TIERS;
1914
+ this.seenTtlMs = config.seenTtlMs ?? 60 * 60 * 1e3;
1915
+ }
1916
+ /**
1917
+ * Run all defense checks on an incoming message.
1918
+ * Returns { allowed: true } if the message should be processed,
1919
+ * or { allowed: false, reason } if it should be dropped.
1920
+ */
1921
+ async checkMessage(envelope) {
1922
+ const did = envelope.from;
1923
+ if (await this.isAllowed(did)) {
1924
+ this.markAsSeen(envelope.id);
1925
+ return { allowed: true };
1926
+ }
1927
+ if (await this.isBlocked(did)) {
1928
+ logger8.debug("Message rejected: blocked", { id: envelope.id, from: did });
1929
+ return { allowed: false, reason: "blocked" };
1930
+ }
1931
+ if (this.hasSeen(envelope.id)) {
1932
+ logger8.debug("Message rejected: duplicate", { id: envelope.id });
1933
+ return { allowed: false, reason: "duplicate" };
1934
+ }
1935
+ let trustScore = 0;
1936
+ try {
1937
+ const score = await this.trust.getTrustScore(did);
1938
+ trustScore = score.interactionScore;
1939
+ if (trustScore < this.autoBlockThreshold && trustScore > 0) {
1940
+ logger8.warn("Auto-blocking low-trust agent", { did, trustScore });
1941
+ await this.blockAgent(did, `Auto-blocked: trust score ${trustScore.toFixed(2)} below threshold`);
1942
+ return { allowed: false, reason: "blocked" };
1943
+ }
1944
+ if (trustScore < this.minTrustScore) {
1945
+ logger8.debug("Message rejected: trust too low", { id: envelope.id, trustScore });
1946
+ return { allowed: false, reason: "trust_too_low", trustScore };
1947
+ }
1948
+ } catch (err) {
1949
+ logger8.warn("Trust score lookup failed, using 0", { did, error: err.message });
1950
+ }
1951
+ const rateLimitResult = await this.checkRateLimit(did, trustScore);
1952
+ if (!rateLimitResult.allowed) {
1953
+ logger8.debug("Message rejected: rate limited", {
1604
1954
  id: envelope.id,
1605
- from: envelope.from,
1606
- error: error.message
1955
+ from: did,
1956
+ resetTime: rateLimitResult.resetTime
1607
1957
  });
1608
- return;
1609
- }
1610
- logger5.info("Received message", {
1611
- id: envelope.id,
1612
- from: envelope.from,
1613
- to: envelope.to,
1614
- protocol: envelope.protocol,
1615
- payload: envelope.payload
1958
+ return {
1959
+ allowed: false,
1960
+ reason: "rate_limited",
1961
+ remainingTokens: rateLimitResult.remaining,
1962
+ resetTime: rateLimitResult.resetTime
1963
+ };
1964
+ }
1965
+ this.markAsSeen(envelope.id);
1966
+ return { allowed: true, trustScore, remainingTokens: rateLimitResult.remaining };
1967
+ }
1968
+ // ─── Blocklist ────────────────────────────────────────────────────────────
1969
+ async blockAgent(did, reason, blockedBy = "local") {
1970
+ await this.storage.putBlock({ did, reason, blockedAt: Date.now(), blockedBy });
1971
+ logger8.info("Agent blocked", { did, reason });
1972
+ }
1973
+ async unblockAgent(did) {
1974
+ await this.storage.deleteBlock(did);
1975
+ logger8.info("Agent unblocked", { did });
1976
+ }
1977
+ async isBlocked(did) {
1978
+ return await this.storage.getBlock(did) !== null;
1979
+ }
1980
+ // ─── Allowlist ────────────────────────────────────────────────────────────
1981
+ async allowAgent(did, note) {
1982
+ await this.storage.putAllow({ did, addedAt: Date.now(), note });
1983
+ logger8.info("Agent allowlisted", { did });
1984
+ }
1985
+ async removeFromAllowlist(did) {
1986
+ await this.storage.deleteAllow(did);
1987
+ logger8.info("Agent removed from allowlist", { did });
1988
+ }
1989
+ async isAllowed(did) {
1990
+ return await this.storage.getAllow(did) !== null;
1991
+ }
1992
+ // ─── Rate Limiting ────────────────────────────────────────────────────────
1993
+ async checkRateLimit(did, trustScore) {
1994
+ const tierConfig = getTierConfig(trustScore, this.tiers);
1995
+ let bucket = this.buckets.get(did);
1996
+ if (!bucket) {
1997
+ const persisted = await this.storage.getRateLimit(did);
1998
+ if (persisted) {
1999
+ bucket = new TokenBucket(tierConfig, persisted.tokens, persisted.lastRefill);
2000
+ } else {
2001
+ bucket = new TokenBucket(tierConfig);
2002
+ }
2003
+ this.buckets.set(did, bucket);
2004
+ }
2005
+ const allowed = bucket.consume();
2006
+ const state = bucket.toState();
2007
+ await this.storage.putRateLimit({
2008
+ did,
2009
+ tokens: state.tokens,
2010
+ lastRefill: state.lastRefill,
2011
+ totalRequests: 0,
2012
+ firstSeen: Date.now()
1616
2013
  });
1617
- const handler = handlers.get(envelope.protocol);
1618
- let response = void 0;
1619
- if (handler) {
1620
- response = await handler(envelope);
1621
- } else if (catchAllHandler) {
1622
- logger5.debug("Using catch-all handler for protocol", { protocol: envelope.protocol });
1623
- response = await catchAllHandler(envelope);
1624
- } else {
1625
- logger5.warn("No handler for protocol", { protocol: envelope.protocol });
1626
- }
1627
- if (response) {
1628
- const encoded = encodeMessage(response);
1629
- logger5.info("Sending response back to sender", {
1630
- responseId: response.id,
1631
- replyTo: response.replyTo,
1632
- size: encoded.length
1633
- });
1634
- await stream.sink(
1635
- (async function* () {
1636
- yield encoded;
1637
- })()
1638
- );
2014
+ return {
2015
+ allowed,
2016
+ remaining: bucket.getRemaining(),
2017
+ resetTime: bucket.getResetTime(),
2018
+ limit: tierConfig.capacity
2019
+ };
2020
+ }
2021
+ // ─── Seen Cache (deduplication) ───────────────────────────────────────────
2022
+ hasSeen(messageId) {
2023
+ return this.seenCache.has(messageId);
2024
+ }
2025
+ markAsSeen(messageId) {
2026
+ if (this.seenCache.size >= this.MAX_SEEN_CACHE) {
2027
+ const firstKey = this.seenCache.keys().next().value;
2028
+ if (firstKey) this.seenCache.delete(firstKey);
1639
2029
  }
1640
- } catch (error) {
1641
- logger5.error("Error handling incoming message", error);
2030
+ this.seenCache.set(messageId, Date.now());
2031
+ this.storage.putSeen({ messageId, seenAt: Date.now(), fromDid: "" }).catch(() => {
2032
+ });
1642
2033
  }
1643
- }
2034
+ /** Periodic cleanup of expired seen entries */
2035
+ async cleanupSeen() {
2036
+ const cutoff = Date.now() - this.seenTtlMs;
2037
+ for (const [id, seenAt] of this.seenCache) {
2038
+ if (seenAt < cutoff) this.seenCache.delete(id);
2039
+ }
2040
+ await this.storage.cleanupSeen(this.seenTtlMs);
2041
+ }
2042
+ /** Periodic cleanup of stale rate limit buckets (24h inactive) */
2043
+ async cleanupRateLimits() {
2044
+ const staleMs = 24 * 60 * 60 * 1e3;
2045
+ const cutoff = Date.now() - staleMs;
2046
+ for (const [did, bucket] of this.buckets) {
2047
+ if (bucket.toState().lastRefill < cutoff) this.buckets.delete(did);
2048
+ }
2049
+ await this.storage.cleanupRateLimits(staleMs);
2050
+ }
2051
+ };
1644
2052
 
1645
2053
  // src/trust/trust-score.ts
1646
2054
  var TrustMetrics = class {
@@ -1710,7 +2118,7 @@ function createDefaultTrustScore() {
1710
2118
  lastUpdated: Date.now()
1711
2119
  };
1712
2120
  }
1713
- var logger6 = createLogger("interaction-history");
2121
+ var logger9 = createLogger("interaction-history");
1714
2122
  var InteractionHistory = class {
1715
2123
  db;
1716
2124
  constructor(dbPath) {
@@ -1721,14 +2129,14 @@ var InteractionHistory = class {
1721
2129
  */
1722
2130
  async open() {
1723
2131
  await this.db.open();
1724
- logger6.info("Interaction history database opened", { path: this.db.location });
2132
+ logger9.info("Interaction history database opened", { path: this.db.location });
1725
2133
  }
1726
2134
  /**
1727
2135
  * Close database connection
1728
2136
  */
1729
2137
  async close() {
1730
2138
  await this.db.close();
1731
- logger6.info("Interaction history database closed");
2139
+ logger9.info("Interaction history database closed");
1732
2140
  }
1733
2141
  /**
1734
2142
  * Record an interaction
@@ -1736,7 +2144,7 @@ var InteractionHistory = class {
1736
2144
  async record(interaction) {
1737
2145
  const key = `interaction:${interaction.agentDid}:${interaction.timestamp}`;
1738
2146
  await this.db.put(key, interaction);
1739
- logger6.debug("Recorded interaction", { agentDid: interaction.agentDid, type: interaction.type });
2147
+ logger9.debug("Recorded interaction", { agentDid: interaction.agentDid, type: interaction.type });
1740
2148
  }
1741
2149
  /**
1742
2150
  * Get interaction history for an agent
@@ -1755,7 +2163,7 @@ var InteractionHistory = class {
1755
2163
  interactions.push(value);
1756
2164
  }
1757
2165
  } catch (error) {
1758
- logger6.error("Failed to get interaction history", { agentDid, error });
2166
+ logger9.error("Failed to get interaction history", { agentDid, error });
1759
2167
  }
1760
2168
  return interactions;
1761
2169
  }
@@ -1794,7 +2202,7 @@ var InteractionHistory = class {
1794
2202
  }
1795
2203
  }
1796
2204
  } catch (error) {
1797
- logger6.error("Failed to get all agents", { error });
2205
+ logger9.error("Failed to get all agents", { error });
1798
2206
  }
1799
2207
  return Array.from(agents);
1800
2208
  }
@@ -1811,7 +2219,7 @@ var InteractionHistory = class {
1811
2219
  keysToDelete.push(key);
1812
2220
  }
1813
2221
  await this.db.batch(keysToDelete.map((key) => ({ type: "del", key })));
1814
- logger6.info("Deleted interaction history", { agentDid, count: keysToDelete.length });
2222
+ logger9.info("Deleted interaction history", { agentDid, count: keysToDelete.length });
1815
2223
  }
1816
2224
  /**
1817
2225
  * Clean up old interactions (older than 90 days)
@@ -1826,14 +2234,14 @@ var InteractionHistory = class {
1826
2234
  }
1827
2235
  if (keysToDelete.length > 0) {
1828
2236
  await this.db.batch(keysToDelete.map((key) => ({ type: "del", key })));
1829
- logger6.info("Cleaned up old interactions", { count: keysToDelete.length });
2237
+ logger9.info("Cleaned up old interactions", { count: keysToDelete.length });
1830
2238
  }
1831
2239
  return keysToDelete.length;
1832
2240
  }
1833
2241
  };
1834
2242
 
1835
2243
  // src/trust/endorsement.ts
1836
- var logger7 = createLogger("endorsement");
2244
+ var logger10 = createLogger("endorsement");
1837
2245
  var EndorsementManager = class {
1838
2246
  constructor(db, getPublicKey) {
1839
2247
  this.db = db;
@@ -1860,7 +2268,7 @@ var EndorsementManager = class {
1860
2268
  ...endorsement,
1861
2269
  signature
1862
2270
  };
1863
- logger7.info("Created endorsement", { from: fromDid, to: toDid, score });
2271
+ logger10.info("Created endorsement", { from: fromDid, to: toDid, score });
1864
2272
  return signedEndorsement;
1865
2273
  }
1866
2274
  /**
@@ -1874,7 +2282,7 @@ var EndorsementManager = class {
1874
2282
  const publicKey = await this.getPublicKey(endorsement.from);
1875
2283
  return await verifyFn(signatureBytes, data, publicKey);
1876
2284
  } catch (error) {
1877
- logger7.error("Failed to verify endorsement", { error });
2285
+ logger10.error("Failed to verify endorsement", { error });
1878
2286
  return false;
1879
2287
  }
1880
2288
  }
@@ -1884,7 +2292,7 @@ var EndorsementManager = class {
1884
2292
  async publish(endorsement) {
1885
2293
  const key = `endorsement:${endorsement.to}:${endorsement.from}`;
1886
2294
  await this.db.put(key, endorsement);
1887
- logger7.info("Published endorsement", { from: endorsement.from, to: endorsement.to });
2295
+ logger10.info("Published endorsement", { from: endorsement.from, to: endorsement.to });
1888
2296
  }
1889
2297
  /**
1890
2298
  * Get all endorsements for an agent
@@ -1900,7 +2308,7 @@ var EndorsementManager = class {
1900
2308
  endorsements.push(value);
1901
2309
  }
1902
2310
  } catch (error) {
1903
- logger7.error("Failed to get endorsements", { agentDid, error });
2311
+ logger10.error("Failed to get endorsements", { agentDid, error });
1904
2312
  }
1905
2313
  return endorsements;
1906
2314
  }
@@ -1916,7 +2324,7 @@ var EndorsementManager = class {
1916
2324
  }
1917
2325
  }
1918
2326
  } catch (error) {
1919
- logger7.error("Failed to get endorsements by agent", { fromDid, error });
2327
+ logger10.error("Failed to get endorsements by agent", { fromDid, error });
1920
2328
  }
1921
2329
  return endorsements;
1922
2330
  }
@@ -1937,10 +2345,10 @@ var EndorsementManager = class {
1937
2345
  async deleteEndorsement(fromDid, toDid) {
1938
2346
  const key = `endorsement:${toDid}:${fromDid}`;
1939
2347
  await this.db.del(key);
1940
- logger7.info("Deleted endorsement", { from: fromDid, to: toDid });
2348
+ logger10.info("Deleted endorsement", { from: fromDid, to: toDid });
1941
2349
  }
1942
2350
  };
1943
- var logger8 = createLogger("sybil-defense");
2351
+ var logger11 = createLogger("sybil-defense");
1944
2352
  var SybilDefense = class {
1945
2353
  rateLimits = /* @__PURE__ */ new Map();
1946
2354
  peerFirstSeen = /* @__PURE__ */ new Map();
@@ -1970,7 +2378,7 @@ var SybilDefense = class {
1970
2378
  verifyChallenge(solution) {
1971
2379
  const { challenge, solution: solutionNonce } = solution;
1972
2380
  if (Date.now() - challenge.timestamp > 60 * 60 * 1e3) {
1973
- logger8.warn("Challenge expired", { did: challenge.did });
2381
+ logger11.warn("Challenge expired", { did: challenge.did });
1974
2382
  return false;
1975
2383
  }
1976
2384
  const data = `${challenge.did}:${challenge.nonce}:${solutionNonce}`;
@@ -1978,9 +2386,9 @@ var SybilDefense = class {
1978
2386
  const leadingZeros = this.countLeadingZeroBits(hash);
1979
2387
  const valid = leadingZeros >= challenge.difficulty;
1980
2388
  if (valid) {
1981
- logger8.info("Challenge verified", { did: challenge.did, leadingZeros });
2389
+ logger11.info("Challenge verified", { did: challenge.did, leadingZeros });
1982
2390
  } else {
1983
- logger8.warn("Challenge failed", { did: challenge.did, leadingZeros, required: challenge.difficulty });
2391
+ logger11.warn("Challenge failed", { did: challenge.did, leadingZeros, required: challenge.difficulty });
1984
2392
  }
1985
2393
  return valid;
1986
2394
  }
@@ -2020,7 +2428,7 @@ var SybilDefense = class {
2020
2428
  record.requests = record.requests.filter(
2021
2429
  (t) => now - t < this.RATE_LIMIT_WINDOW
2022
2430
  );
2023
- logger8.debug("Recorded request", { did, count: record.requests.length });
2431
+ logger11.debug("Recorded request", { did, count: record.requests.length });
2024
2432
  }
2025
2433
  /**
2026
2434
  * Get peer trust level based on age
@@ -2045,7 +2453,7 @@ var SybilDefense = class {
2045
2453
  recordPeerSeen(peerId) {
2046
2454
  if (!this.peerFirstSeen.has(peerId)) {
2047
2455
  this.peerFirstSeen.set(peerId, Date.now());
2048
- logger8.debug("Recorded new peer", { peerId });
2456
+ logger11.debug("Recorded new peer", { peerId });
2049
2457
  }
2050
2458
  }
2051
2459
  /**
@@ -2059,7 +2467,7 @@ var SybilDefense = class {
2059
2467
  this.rateLimits.delete(did);
2060
2468
  }
2061
2469
  }
2062
- logger8.info("Cleaned up Sybil defense records", {
2470
+ logger11.info("Cleaned up Sybil defense records", {
2063
2471
  rateLimits: this.rateLimits.size,
2064
2472
  peers: this.peerFirstSeen.size
2065
2473
  });
@@ -2092,7 +2500,7 @@ var SybilDefense = class {
2092
2500
  return count;
2093
2501
  }
2094
2502
  };
2095
- var logger9 = createLogger("trust-system");
2503
+ var logger12 = createLogger("trust-system");
2096
2504
  var TrustSystem = class {
2097
2505
  metrics;
2098
2506
  history;
@@ -2115,14 +2523,14 @@ var TrustSystem = class {
2115
2523
  */
2116
2524
  async start() {
2117
2525
  await this.history.open();
2118
- logger9.info("Trust system started");
2526
+ logger12.info("Trust system started");
2119
2527
  }
2120
2528
  /**
2121
2529
  * Shutdown the trust system
2122
2530
  */
2123
2531
  async stop() {
2124
2532
  await this.history.close();
2125
- logger9.info("Trust system stopped");
2533
+ logger12.info("Trust system stopped");
2126
2534
  }
2127
2535
  /**
2128
2536
  * Record an interaction
@@ -2211,13 +2619,13 @@ var TrustSystem = class {
2211
2619
  await this.history.cleanup();
2212
2620
  this.sybilDefense.cleanup();
2213
2621
  this.trustCache.clear();
2214
- logger9.info("Trust system cleanup completed");
2622
+ logger12.info("Trust system cleanup completed");
2215
2623
  }
2216
2624
  };
2217
2625
  function createTrustSystem(config) {
2218
2626
  return new TrustSystem(config);
2219
2627
  }
2220
2628
 
2221
- 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 };
2629
+ export { CLAWIVERSE_CONTEXT, CapabilityMatcher, CapabilityTypes, ClawiverseError, DEFAULT_RATE_LIMIT_TIERS, DefenseMiddleware, DiscoveryError, EndorsementManager, IdentityError, InteractionHistory, LogLevel, Logger, MessageQueue, MessageStorage, MessagingError, ParameterTypes, RELAY_PROTOCOL_VERSION, SCHEMA_ORG_CONTEXT, SearchIndex, SemanticSearchEngine, SybilDefense, TokenBucket, TransportError, TrustMetrics, TrustSystem, clawiverseContext, createAgentCard, createDefaultTrustScore, createEnvelope, createLegacyAgentCard, createLogger, createMessageRouter, createRelayClient, createRelayIndexOperations, createSemanticSearch, createTrustSystem, decodeAgentCard, decodeFromCBOR, decodeFromJSON, decodeMessage, decodeMessageJSON, deriveDID, downgradeToLegacyCard, encodeForDHT, encodeForWeb, encodeMessage, encodeMessageJSON, exportKeyPair, extractPublicKey, generateKeyPair, getAgentCardContext, getEncodedSize, getTierConfig, importKeyPair, isLegacyCard, isValidContext, matchesCapability, sign, signAgentCard, signEnvelope, signMessage, upgradeLegacyCard, validateAgentCard, validateDID, validateEnvelope, verify, verifyAgentCard, verifyEnvelope, verifyMessage };
2222
2630
  //# sourceMappingURL=index.js.map
2223
2631
  //# sourceMappingURL=index.js.map