@rookdaemon/agora 0.5.3 → 0.5.4
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.
|
@@ -659,6 +659,15 @@ function expandInlineReferences(text, directory) {
|
|
|
659
659
|
function compactInlineReferences(text, directory) {
|
|
660
660
|
return text.replace(/@([0-9a-fA-F]{16,})/g, (_full, id) => `@${shorten(id, directory)}`);
|
|
661
661
|
}
|
|
662
|
+
function compactKnownInlineReferences(text, directory) {
|
|
663
|
+
return text.replace(/@([0-9a-fA-F]{16,})/g, (_full, id) => {
|
|
664
|
+
const known = findById(id, directory);
|
|
665
|
+
if (!known) {
|
|
666
|
+
return `@${id}`;
|
|
667
|
+
}
|
|
668
|
+
return `@${shorten(id, directory)}`;
|
|
669
|
+
});
|
|
670
|
+
}
|
|
662
671
|
function sanitizeText(text) {
|
|
663
672
|
return text.replace(/[\u0000-\u0008\u000B\u000C\u000E-\u001F\u007F-\u009F]/g, "").replace(/[\uD800-\uDBFF](?![\uDC00-\uDFFF])/g, "\uFFFD").replace(/(?<![\uD800-\uDBFF])[\uDC00-\uDFFF]/g, "\uFFFD");
|
|
664
673
|
}
|
|
@@ -1221,6 +1230,7 @@ export {
|
|
|
1221
1230
|
expand,
|
|
1222
1231
|
expandInlineReferences,
|
|
1223
1232
|
compactInlineReferences,
|
|
1233
|
+
compactKnownInlineReferences,
|
|
1224
1234
|
sanitizeText,
|
|
1225
1235
|
resolveDisplayName,
|
|
1226
1236
|
resolveBroadcastName,
|
|
@@ -1240,4 +1250,4 @@ export {
|
|
|
1240
1250
|
computeTrustScores,
|
|
1241
1251
|
computeAllTrustScores
|
|
1242
1252
|
};
|
|
1243
|
-
//# sourceMappingURL=chunk-
|
|
1253
|
+
//# sourceMappingURL=chunk-LV6NP6BY.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/transport/peer-config.ts","../src/transport/http.ts","../src/transport/relay.ts","../src/relay/client.ts","../src/discovery/peer-discovery.ts","../src/discovery/bootstrap.ts","../src/utils.ts","../src/reputation/types.ts","../src/reputation/store.ts","../src/reputation/verification.ts","../src/reputation/commit-reveal.ts","../src/reputation/scoring.ts"],"sourcesContent":["import { readFileSync, writeFileSync, existsSync } from 'node:fs';\nimport { generateKeyPair } from '../identity/keypair';\n\nexport interface PeerConfigFile {\n identity: {\n publicKey: string;\n privateKey: string;\n name?: string;\n };\n relay?: string | {\n url: string;\n name?: string;\n };\n peers: Record<string, {\n url?: string;\n token?: string;\n publicKey: string;\n name?: string;\n }>;\n}\n\n/**\n * Load peer configuration from a JSON file.\n * @param path - Path to the config file\n * @returns The parsed configuration\n * @throws Error if file doesn't exist or contains invalid JSON\n */\nexport function loadPeerConfig(path: string): PeerConfigFile {\n const content = readFileSync(path, 'utf-8');\n return JSON.parse(content) as PeerConfigFile;\n}\n\n/**\n * Save peer configuration to a JSON file.\n * @param path - Path to the config file\n * @param config - The configuration to save\n */\nexport function savePeerConfig(path: string, config: PeerConfigFile): void {\n const content = JSON.stringify(config, null, 2);\n writeFileSync(path, content, 'utf-8');\n}\n\n/**\n * Initialize peer configuration, generating a new keypair if the file doesn't exist.\n * If the file exists, loads and returns it.\n * @param path - Path to the config file\n * @returns The configuration (loaded or newly created)\n */\nexport function initPeerConfig(path: string): PeerConfigFile {\n if (existsSync(path)) {\n return loadPeerConfig(path);\n }\n\n // Generate new keypair and create initial config\n const identity = generateKeyPair();\n const config: PeerConfigFile = {\n identity,\n peers: {},\n };\n\n savePeerConfig(path, config);\n return config;\n}\n","import { createEnvelope, verifyEnvelope, type Envelope, type MessageType } from '../message/envelope';\r\n\r\nexport interface PeerConfig {\r\n /** Peer's webhook URL, e.g. http://localhost:18790/hooks (undefined for relay-only peers) */\r\n url?: string;\r\n /** Peer's webhook auth token (undefined for relay-only peers) */\r\n token?: string;\r\n /** Peer's public key (hex) for verifying responses */\r\n publicKey: string;\r\n /** Optional convenience alias only (not identity) */\r\n name?: string;\r\n}\r\n\r\nexport interface TransportConfig {\r\n /** This agent's keypair */\r\n identity: { publicKey: string; privateKey: string };\r\n /** Known peers */\r\n peers: Map<string, PeerConfig>;\r\n}\r\n\r\n/**\r\n * Send a signed envelope to a peer via HTTP webhook.\r\n * Creates the envelope, signs it, and POSTs to the peer's /hooks/agent endpoint.\r\n * Returns the HTTP status code.\r\n */\r\nexport async function sendToPeer(\r\n config: TransportConfig,\r\n peerPublicKey: string,\r\n type: MessageType,\r\n payload: unknown,\r\n inReplyTo?: string\r\n): Promise<{ ok: boolean; status: number; error?: string }> {\r\n // Look up peer config\r\n const peer = config.peers.get(peerPublicKey);\r\n if (!peer) {\r\n return { ok: false, status: 0, error: 'Unknown peer' };\r\n }\r\n\r\n // Relay-only peer — no webhook URL configured\r\n if (!peer.url) {\r\n return { ok: false, status: 0, error: 'No webhook URL configured' };\r\n }\r\n\r\n // Create and sign the envelope\r\n const envelope = createEnvelope(\r\n type,\r\n config.identity.publicKey,\r\n config.identity.privateKey,\r\n payload,\r\n Date.now(),\r\n inReplyTo,\r\n [peerPublicKey]\r\n );\r\n\r\n // Encode envelope as base64url\r\n const envelopeJson = JSON.stringify(envelope);\r\n const envelopeBase64 = Buffer.from(envelopeJson).toString('base64url');\r\n\r\n // Construct webhook payload\r\n const webhookPayload = {\r\n message: `[AGORA_ENVELOPE]${envelopeBase64}`,\r\n name: 'Agora',\r\n sessionKey: `agora:${envelope.from.substring(0, 16)}`,\r\n deliver: false,\r\n };\r\n\r\n // Build headers — only include Authorization when a token is configured\r\n const headers: Record<string, string> = {\r\n 'Content-Type': 'application/json',\r\n };\r\n if (peer.token) {\r\n headers['Authorization'] = `Bearer ${peer.token}`;\r\n }\r\n\r\n const requestBody = JSON.stringify(webhookPayload);\r\n\r\n // Send HTTP POST (retry once on network error, not on 4xx/5xx)\r\n for (let attempt = 0; attempt < 2; attempt++) {\r\n try {\r\n const response = await fetch(`${peer.url}/agent`, {\r\n method: 'POST',\r\n headers,\r\n body: requestBody,\r\n });\r\n\r\n return {\r\n ok: response.ok,\r\n status: response.status,\r\n error: response.ok ? undefined : await response.text(),\r\n };\r\n } catch (err) {\r\n if (attempt === 1) {\r\n return {\r\n ok: false,\r\n status: 0,\r\n error: err instanceof Error ? err.message : String(err),\r\n };\r\n }\r\n // First attempt failed with network error — retry once\r\n }\r\n }\r\n\r\n // Unreachable, but satisfies TypeScript\r\n return { ok: false, status: 0, error: 'Unexpected send failure' };\r\n}\r\n\r\n/**\r\n * Decode and verify an inbound Agora envelope from a webhook message.\r\n * Expects the message to start with [AGORA_ENVELOPE] followed by base64.\r\n * Returns the verified envelope or an error.\r\n */\r\nexport function decodeInboundEnvelope(\r\n message: string,\r\n knownPeers: Map<string, PeerConfig>\r\n): { ok: true; envelope: Envelope } | { ok: false; reason: string } {\r\n // Check for AGORA_ENVELOPE prefix\r\n const prefix = '[AGORA_ENVELOPE]';\r\n if (!message.startsWith(prefix)) {\r\n return { ok: false, reason: 'not_agora_message' };\r\n }\r\n\r\n // Extract base64 payload\r\n const base64Payload = message.substring(prefix.length);\r\n \r\n // Check for empty payload\r\n if (!base64Payload) {\r\n return { ok: false, reason: 'invalid_base64' };\r\n }\r\n \r\n // Decode base64\r\n let envelopeJson: string;\r\n try {\r\n const decoded = Buffer.from(base64Payload, 'base64url');\r\n // Check if decoded buffer is empty or contains invalid data\r\n if (decoded.length === 0) {\r\n return { ok: false, reason: 'invalid_base64' };\r\n }\r\n envelopeJson = decoded.toString('utf-8');\r\n } catch {\r\n return { ok: false, reason: 'invalid_base64' };\r\n }\r\n\r\n // Parse JSON\r\n let envelope: Envelope;\r\n try {\r\n envelope = JSON.parse(envelopeJson);\r\n } catch {\r\n return { ok: false, reason: 'invalid_json' };\r\n }\r\n\r\n // Verify envelope integrity\r\n const verification = verifyEnvelope(envelope);\r\n if (!verification.valid) {\r\n return { ok: false, reason: verification.reason || 'verification_failed' };\r\n }\r\n\r\n // Check if sender is a known peer\r\n const senderKnown = knownPeers.has(envelope.from);\r\n if (!senderKnown) {\r\n return { ok: false, reason: 'unknown_sender' };\r\n }\r\n\r\n return { ok: true, envelope };\r\n}\r\n","import WebSocket from 'ws';\nimport { createEnvelope, type Envelope, type MessageType } from '../message/envelope';\n\n/** Minimal interface for a connected relay client (avoids importing full RelayClient) */\nexport interface RelayClientSender {\n connected(): boolean;\n send(to: string, envelope: Envelope): Promise<{ ok: boolean; error?: string }>;\n}\n\nexport interface RelayTransportConfig {\n /** This agent's keypair */\n identity: { publicKey: string; privateKey: string };\n /** Relay server WebSocket URL (e.g., wss://agora-relay.lbsa71.net) */\n relayUrl: string;\n /** Optional persistent relay client (if provided, will use it instead of connect-per-message) */\n relayClient?: RelayClientSender;\n}\n\n/**\n * Send a signed envelope to a peer via relay server.\n * If a persistent relayClient is provided in the config, uses that.\n * Otherwise, connects to relay, registers, sends message, and disconnects.\n */\nexport async function sendViaRelay(\n config: RelayTransportConfig,\n peerPublicKey: string,\n type: MessageType,\n payload: unknown,\n inReplyTo?: string\n): Promise<{ ok: boolean; error?: string }> {\n // If a persistent relay client is available, use it\n if (config.relayClient && config.relayClient.connected()) {\n const envelope = createEnvelope(\n type,\n config.identity.publicKey,\n config.identity.privateKey,\n payload,\n Date.now(),\n inReplyTo,\n [peerPublicKey]\n );\n return config.relayClient.send(peerPublicKey, envelope);\n }\n\n // Otherwise, fall back to connect-per-message\n return new Promise((resolve) => {\n const ws = new WebSocket(config.relayUrl);\n let registered = false;\n let messageSent = false;\n let resolved = false;\n\n // Helper to resolve once\n const resolveOnce = (result: { ok: boolean; error?: string }): void => {\n if (!resolved) {\n resolved = true;\n clearTimeout(timeout);\n resolve(result);\n }\n };\n\n // Set timeout for the entire operation\n const timeout = setTimeout(() => {\n if (!messageSent) {\n ws.close();\n resolveOnce({ ok: false, error: 'Relay connection timeout' });\n }\n }, 10000); // 10 second timeout\n\n ws.on('open', () => {\n // Send register message\n const registerMsg = {\n type: 'register',\n publicKey: config.identity.publicKey,\n };\n ws.send(JSON.stringify(registerMsg));\n });\n\n ws.on('message', (data: WebSocket.Data) => {\n try {\n const msg = JSON.parse(data.toString());\n\n if (msg.type === 'registered' && !registered) {\n registered = true;\n\n // Create and sign the envelope\n const envelope: Envelope = createEnvelope(\n type,\n config.identity.publicKey,\n config.identity.privateKey,\n payload,\n Date.now(),\n inReplyTo,\n [peerPublicKey]\n );\n\n // Send message via relay\n const relayMsg = {\n type: 'message',\n to: peerPublicKey,\n envelope,\n };\n ws.send(JSON.stringify(relayMsg));\n messageSent = true;\n\n // Close connection after sending\n setTimeout(() => {\n ws.close();\n resolveOnce({ ok: true });\n }, 100); // Small delay to ensure message is sent\n } else if (msg.type === 'error') {\n ws.close();\n resolveOnce({ ok: false, error: msg.message || 'Relay server error' });\n }\n } catch (err) {\n ws.close();\n resolveOnce({ ok: false, error: err instanceof Error ? err.message : String(err) });\n }\n });\n\n ws.on('error', (err) => {\n ws.close();\n resolveOnce({ ok: false, error: err.message });\n });\n\n ws.on('close', () => {\n if (!messageSent) {\n resolveOnce({ ok: false, error: 'Connection closed before message sent' });\n }\n });\n });\n}\n","import { EventEmitter } from 'node:events';\nimport WebSocket from 'ws';\nimport { verifyEnvelope, type Envelope } from '../message/envelope';\nimport type { RelayClientMessage, RelayServerMessage, RelayPeer } from './types';\n\n/**\n * Configuration for RelayClient\n */\nexport interface RelayClientConfig {\n /** WebSocket URL of the relay server */\n relayUrl: string;\n /** Agent's public key */\n publicKey: string;\n /** Agent's private key (for signing) */\n privateKey: string;\n /** Optional name for this agent */\n name?: string;\n /** Keepalive ping interval in milliseconds (default: 30000) */\n pingInterval?: number;\n /** Maximum reconnection delay in milliseconds (default: 60000) */\n maxReconnectDelay?: number;\n}\n\n/**\n * Events emitted by RelayClient\n */\nexport interface RelayClientEvents {\n /** Emitted when successfully connected and registered */\n 'connected': () => void;\n /** Emitted when disconnected from relay */\n 'disconnected': () => void;\n /** Emitted when a verified message is received */\n 'message': (envelope: Envelope, from: string, fromName?: string) => void;\n /** Emitted when a peer comes online */\n 'peer_online': (peer: RelayPeer) => void;\n /** Emitted when a peer goes offline */\n 'peer_offline': (peer: RelayPeer) => void;\n /** Emitted on errors */\n 'error': (error: Error) => void;\n}\n\n/**\n * Persistent WebSocket client for the Agora relay server.\n * Maintains a long-lived connection, handles reconnection, and routes messages.\n */\nexport class RelayClient extends EventEmitter {\n private ws: WebSocket | null = null;\n private config: RelayClientConfig;\n private reconnectAttempts = 0;\n private reconnectTimeout: NodeJS.Timeout | null = null;\n private pingInterval: NodeJS.Timeout | null = null;\n private isConnected = false;\n private isRegistered = false;\n private shouldReconnect = true;\n private onlinePeers = new Map<string, RelayPeer>();\n\n constructor(config: RelayClientConfig) {\n super();\n this.config = {\n pingInterval: 30000,\n maxReconnectDelay: 60000,\n ...config,\n };\n }\n\n /**\n * Connect to the relay server\n */\n async connect(): Promise<void> {\n if (this.ws && (this.ws.readyState === WebSocket.CONNECTING || this.ws.readyState === WebSocket.OPEN)) {\n return;\n }\n\n this.shouldReconnect = true;\n return this.doConnect();\n }\n\n /**\n * Disconnect from the relay server\n */\n disconnect(): void {\n this.shouldReconnect = false;\n this.cleanup();\n if (this.ws) {\n this.ws.close();\n this.ws = null;\n }\n }\n\n /**\n * Check if currently connected and registered\n */\n connected(): boolean {\n return this.isConnected && this.isRegistered;\n }\n\n /**\n * Send a message to a specific peer\n */\n async send(to: string, envelope: Envelope): Promise<{ ok: boolean; error?: string }> {\n if (!this.connected()) {\n return { ok: false, error: 'Not connected to relay' };\n }\n\n const message: RelayClientMessage = {\n type: 'message',\n to,\n envelope,\n };\n\n try {\n this.ws!.send(JSON.stringify(message));\n return { ok: true };\n } catch (err) {\n return { ok: false, error: err instanceof Error ? err.message : String(err) };\n }\n }\n\n /**\n * Get list of currently online peers\n */\n getOnlinePeers(): RelayPeer[] {\n return Array.from(this.onlinePeers.values());\n }\n\n /**\n * Check if a specific peer is online\n */\n isPeerOnline(publicKey: string): boolean {\n return this.onlinePeers.has(publicKey);\n }\n\n /**\n * Internal: Perform connection\n */\n private async doConnect(): Promise<void> {\n return new Promise((resolve, reject) => {\n try {\n this.ws = new WebSocket(this.config.relayUrl);\n let resolved = false;\n\n const resolveOnce = (callback: () => void): void => {\n if (!resolved) {\n resolved = true;\n callback();\n }\n };\n\n this.ws.on('open', () => {\n this.isConnected = true;\n this.reconnectAttempts = 0;\n this.startPingInterval();\n\n // Send registration message\n const registerMsg: RelayClientMessage = {\n type: 'register',\n publicKey: this.config.publicKey,\n name: this.config.name,\n };\n this.ws!.send(JSON.stringify(registerMsg));\n });\n\n this.ws.on('message', (data: WebSocket.Data) => {\n try {\n const msg = JSON.parse(data.toString()) as RelayServerMessage;\n this.handleMessage(msg);\n\n // Resolve promise on successful registration\n if (msg.type === 'registered' && !resolved) {\n resolveOnce(() => resolve());\n }\n } catch (err) {\n this.emit('error', new Error(`Failed to parse message: ${err instanceof Error ? err.message : String(err)}`));\n }\n });\n\n this.ws.on('close', () => {\n this.isConnected = false;\n this.isRegistered = false;\n this.cleanup();\n this.emit('disconnected');\n\n if (this.shouldReconnect) {\n this.scheduleReconnect();\n }\n\n if (!resolved) {\n resolveOnce(() => reject(new Error('Connection closed before registration')));\n }\n });\n\n this.ws.on('error', (err) => {\n this.emit('error', err);\n if (!resolved) {\n resolveOnce(() => reject(err));\n }\n });\n } catch (err) {\n reject(err);\n }\n });\n }\n\n /**\n * Handle incoming message from relay\n */\n private handleMessage(msg: RelayServerMessage): void {\n switch (msg.type) {\n case 'registered':\n this.isRegistered = true;\n if (msg.peers) {\n // Populate initial peer list\n for (const peer of msg.peers) {\n this.onlinePeers.set(peer.publicKey, peer);\n }\n }\n this.emit('connected');\n break;\n\n case 'message':\n if (msg.envelope && msg.from) {\n // Verify envelope signature\n const verification = verifyEnvelope(msg.envelope);\n if (!verification.valid) {\n this.emit('error', new Error(`Invalid envelope signature: ${verification.reason}`));\n return;\n }\n\n // Verify sender matches 'from' field\n const envelopeFrom = msg.envelope.from;\n if (envelopeFrom !== msg.from) {\n this.emit('error', new Error('Envelope sender does not match relay from field'));\n return;\n }\n\n // Emit verified message\n this.emit('message', msg.envelope, msg.from, msg.name);\n }\n break;\n\n case 'peer_online':\n if (msg.publicKey) {\n const peer: RelayPeer = {\n publicKey: msg.publicKey,\n name: msg.name,\n };\n this.onlinePeers.set(msg.publicKey, peer);\n this.emit('peer_online', peer);\n }\n break;\n\n case 'peer_offline':\n if (msg.publicKey) {\n const peer = this.onlinePeers.get(msg.publicKey);\n if (peer) {\n this.onlinePeers.delete(msg.publicKey);\n this.emit('peer_offline', peer);\n }\n }\n break;\n\n case 'error':\n this.emit('error', new Error(`Relay error: ${msg.message || 'Unknown error'}`));\n break;\n\n case 'pong':\n // Keepalive response, no action needed\n break;\n\n default:\n // Unknown message type, ignore\n break;\n }\n }\n\n /**\n * Schedule reconnection with exponential backoff\n */\n private scheduleReconnect(): void {\n if (this.reconnectTimeout) {\n return;\n }\n\n // Exponential backoff: 1s, 2s, 4s, 8s, 16s, 32s, 60s (max)\n const delay = Math.min(\n 1000 * Math.pow(2, this.reconnectAttempts),\n this.config.maxReconnectDelay!\n );\n\n this.reconnectAttempts++;\n\n this.reconnectTimeout = setTimeout(() => {\n this.reconnectTimeout = null;\n if (this.shouldReconnect) {\n this.doConnect().catch((err) => {\n this.emit('error', err);\n });\n }\n }, delay);\n }\n\n /**\n * Start periodic ping messages\n */\n private startPingInterval(): void {\n this.stopPingInterval();\n this.pingInterval = setInterval(() => {\n if (this.ws && this.ws.readyState === WebSocket.OPEN) {\n const ping: RelayClientMessage = { type: 'ping' };\n this.ws.send(JSON.stringify(ping));\n }\n }, this.config.pingInterval!);\n }\n\n /**\n * Stop ping interval\n */\n private stopPingInterval(): void {\n if (this.pingInterval) {\n clearInterval(this.pingInterval);\n this.pingInterval = null;\n }\n }\n\n /**\n * Cleanup resources\n */\n private cleanup(): void {\n this.stopPingInterval();\n if (this.reconnectTimeout) {\n clearTimeout(this.reconnectTimeout);\n this.reconnectTimeout = null;\n }\n this.onlinePeers.clear();\n }\n}\n","import { EventEmitter } from 'node:events';\nimport { createEnvelope, verifyEnvelope, type Envelope } from '../message/envelope';\nimport type { RelayClient } from '../relay/client';\nimport type { PeerListRequestPayload, PeerListResponsePayload, PeerReferralPayload } from '../message/types/peer-discovery';\n\n/**\n * Configuration for PeerDiscoveryService\n */\nexport interface PeerDiscoveryConfig {\n /** Agent's public key */\n publicKey: string;\n /** Agent's private key for signing */\n privateKey: string;\n /** RelayClient instance for communication */\n relayClient: RelayClient;\n /** Public key of the relay server (for sending peer list requests) */\n relayPublicKey?: string;\n}\n\n/**\n * Events emitted by PeerDiscoveryService\n */\nexport interface PeerDiscoveryEvents {\n /** Emitted when peers are discovered */\n 'peers-discovered': (peers: PeerListResponsePayload['peers']) => void;\n /** Emitted when a peer referral is received */\n 'peer-referral': (referral: PeerReferralPayload, from: string) => void;\n /** Emitted on errors */\n 'error': (error: Error) => void;\n}\n\n/**\n * Service for discovering peers on the Agora network\n */\nexport class PeerDiscoveryService extends EventEmitter {\n private config: PeerDiscoveryConfig;\n\n constructor(config: PeerDiscoveryConfig) {\n super();\n this.config = config;\n\n // Listen for peer list responses and referrals\n this.config.relayClient.on('message', (envelope: Envelope, from: string) => {\n if (envelope.type === 'peer_list_response') {\n this.handlePeerList(envelope as Envelope<PeerListResponsePayload>);\n } else if (envelope.type === 'peer_referral') {\n this.handleReferral(envelope as Envelope<PeerReferralPayload>, from);\n }\n });\n }\n\n /**\n * Request peer list from relay\n */\n async discoverViaRelay(filters?: PeerListRequestPayload['filters']): Promise<PeerListResponsePayload | null> {\n if (!this.config.relayPublicKey) {\n throw new Error('Relay public key not configured');\n }\n\n if (!this.config.relayClient.connected()) {\n throw new Error('Not connected to relay');\n }\n\n const payload: PeerListRequestPayload = filters ? { filters } : {};\n\n const envelope = createEnvelope(\n 'peer_list_request',\n this.config.publicKey,\n this.config.privateKey,\n payload,\n Date.now(),\n undefined,\n [this.config.relayPublicKey]\n );\n\n // Send request to relay\n const result = await this.config.relayClient.send(this.config.relayPublicKey, envelope);\n if (!result.ok) {\n throw new Error(`Failed to send peer list request: ${result.error}`);\n }\n\n // Wait for response (with timeout)\n return new Promise((resolve, reject) => {\n const timeout = setTimeout(() => {\n cleanup();\n reject(new Error('Peer list request timed out'));\n }, 10000); // 10 second timeout\n\n const messageHandler = (responseEnvelope: Envelope, from: string): void => {\n if (responseEnvelope.type === 'peer_list_response' && \n responseEnvelope.inReplyTo === envelope.id &&\n from === this.config.relayPublicKey) {\n cleanup();\n resolve(responseEnvelope.payload as PeerListResponsePayload);\n }\n };\n\n const cleanup = (): void => {\n clearTimeout(timeout);\n this.config.relayClient.off('message', messageHandler);\n };\n\n this.config.relayClient.on('message', messageHandler);\n });\n }\n\n /**\n * Send peer referral to another agent\n */\n async referPeer(\n recipientPublicKey: string,\n referredPublicKey: string,\n metadata?: { name?: string; endpoint?: string; comment?: string; trustScore?: number }\n ): Promise<{ ok: boolean; error?: string }> {\n if (!this.config.relayClient.connected()) {\n return { ok: false, error: 'Not connected to relay' };\n }\n\n const payload: PeerReferralPayload = {\n publicKey: referredPublicKey,\n endpoint: metadata?.endpoint,\n metadata: metadata?.name ? { name: metadata.name } : undefined,\n comment: metadata?.comment,\n trustScore: metadata?.trustScore,\n };\n\n const envelope = createEnvelope(\n 'peer_referral',\n this.config.publicKey,\n this.config.privateKey,\n payload,\n Date.now(),\n undefined,\n [recipientPublicKey]\n );\n\n return this.config.relayClient.send(recipientPublicKey, envelope);\n }\n\n /**\n * Handle incoming peer referral\n */\n private handleReferral(envelope: Envelope<PeerReferralPayload>, from: string): void {\n // Verify envelope\n const verification = verifyEnvelope(envelope);\n if (!verification.valid) {\n this.emit('error', new Error(`Invalid peer referral: ${verification.reason}`));\n return;\n }\n\n // Emit event for application to handle\n this.emit('peer-referral', envelope.payload, from);\n }\n\n /**\n * Handle incoming peer list from relay\n */\n private handlePeerList(envelope: Envelope<PeerListResponsePayload>): void {\n // Verify envelope\n const verification = verifyEnvelope(envelope);\n if (!verification.valid) {\n this.emit('error', new Error(`Invalid peer list response: ${verification.reason}`));\n return;\n }\n\n // Verify sender is the relay\n if (envelope.from !== this.config.relayPublicKey) {\n this.emit('error', new Error('Peer list response not from configured relay'));\n return;\n }\n\n // Emit event\n this.emit('peers-discovered', envelope.payload.peers);\n }\n}\n","/**\r\n * Bootstrap configuration for peer discovery on the Agora network.\r\n * Provides default bootstrap relays for initial network entry.\r\n */\r\n\r\n/**\r\n * Default bootstrap relay servers\r\n * These are well-known relays that serve as initial entry points to the network\r\n */\r\nexport const DEFAULT_BOOTSTRAP_RELAYS = [\r\n {\r\n url: 'wss://agora-relay.lbsa71.net',\r\n name: 'Primary Bootstrap Relay',\r\n // Note: Public key would need to be set when the relay is actually deployed\r\n // For now, this is a placeholder that would be configured when the relay is running\r\n },\r\n];\r\n\r\n/**\r\n * Configuration for bootstrap connection\r\n */\r\nexport interface BootstrapConfig {\r\n /** Bootstrap relay URL */\r\n relayUrl: string;\r\n /** Optional relay public key (for verification) */\r\n relayPublicKey?: string;\r\n /** Connection timeout in ms (default: 10000) */\r\n timeout?: number;\r\n}\r\n\r\n/**\r\n * Get default bootstrap relay configuration\r\n */\r\nexport function getDefaultBootstrapRelay(): BootstrapConfig {\r\n return {\r\n relayUrl: DEFAULT_BOOTSTRAP_RELAYS[0].url,\r\n timeout: 10000,\r\n };\r\n}\r\n\r\n/**\r\n * Parse bootstrap relay URL and optional public key\r\n */\r\nexport function parseBootstrapRelay(url: string, publicKey?: string): BootstrapConfig {\r\n return {\r\n relayUrl: url,\r\n relayPublicKey: publicKey,\r\n timeout: 10000,\r\n };\r\n}\r\n","/**\r\n * Get a short display version of a public key using the last 8 characters.\r\n * Ed25519 public keys all share the same OID prefix, so the last 8 characters\r\n * are more distinguishable than the first 8.\r\n *\r\n * @param publicKey - The full public key hex string\r\n * @returns \"...\" followed by the last 8 characters of the key\r\n */\r\nexport function shortKey(publicKey: string): string {\r\n return \"...\" + publicKey.slice(-8);\r\n}\r\n\r\nexport interface PeerReferenceEntry {\r\n publicKey: string;\r\n name?: string;\r\n}\r\n\r\nexport type PeerReferenceDirectory =\r\n | Record<string, PeerReferenceEntry>\r\n | Map<string, PeerReferenceEntry>\r\n | PeerReferenceEntry[];\r\n\r\nfunction toDirectoryEntries(directory?: PeerReferenceDirectory): PeerReferenceEntry[] {\r\n if (!directory) {\r\n return [];\r\n }\r\n if (Array.isArray(directory)) {\r\n return directory.filter((p) => typeof p.publicKey === 'string' && p.publicKey.length > 0);\r\n }\r\n if (directory instanceof Map) {\r\n return Array.from(directory.values()).filter((p) => typeof p.publicKey === 'string' && p.publicKey.length > 0);\r\n }\r\n return Object.values(directory).filter((p) => typeof p.publicKey === 'string' && p.publicKey.length > 0);\r\n}\r\n\r\nfunction findById(id: string, directory?: PeerReferenceDirectory): PeerReferenceEntry | undefined {\r\n return toDirectoryEntries(directory).find((entry) => entry.publicKey === id);\r\n}\r\n\r\n/**\r\n * Shorten a full peer ID for display/reference.\r\n * Canonical form:\r\n * - Configured name => \"name...<last8>\"\r\n * - Unknown/no-name => \"...<last8>\"\r\n */\r\nexport function shorten(id: string, directory?: PeerReferenceDirectory): string {\r\n const suffix = id.slice(-8);\r\n const entry = findById(id, directory);\r\n if (!entry?.name) {\r\n return `...${suffix}`;\r\n }\r\n return `${entry.name}...${suffix}`;\r\n}\r\n\r\n/**\r\n * Expand a short peer reference to a full ID.\r\n * Supports: full ID, unique name, ...last8, and name...last8.\r\n */\r\nexport function expand(shortId: string, directory: PeerReferenceDirectory): string | undefined {\r\n const entries = toDirectoryEntries(directory);\r\n if (entries.length === 0) {\r\n return undefined;\r\n }\r\n\r\n const token = shortId.trim();\r\n const direct = entries.find((entry) => entry.publicKey === token);\r\n if (direct) {\r\n return direct.publicKey;\r\n }\r\n\r\n const namedWithSuffix = token.match(/^(.+)\\.\\.\\.([0-9a-fA-F]{8})$/);\r\n if (namedWithSuffix) {\r\n const [, name, suffix] = namedWithSuffix;\r\n const matches = entries.filter((entry) => entry.name === name && entry.publicKey.toLowerCase().endsWith(suffix.toLowerCase()));\r\n if (matches.length === 1) {\r\n return matches[0].publicKey;\r\n }\r\n return undefined;\r\n }\r\n\r\n const suffixOnly = token.match(/^\\.\\.\\.([0-9a-fA-F]{8})$/);\r\n if (suffixOnly) {\r\n const [, suffix] = suffixOnly;\r\n const matches = entries.filter((entry) => entry.publicKey.toLowerCase().endsWith(suffix.toLowerCase()));\r\n if (matches.length === 1) {\r\n return matches[0].publicKey;\r\n }\r\n return undefined;\r\n }\r\n\r\n const byName = entries.filter((entry) => entry.name === token);\r\n if (byName.length === 1) {\r\n return byName[0].publicKey;\r\n }\r\n\r\n return undefined;\r\n}\r\n\r\n/**\r\n * Expand inline @references in text to full IDs using configured peers.\r\n */\r\nexport function expandInlineReferences(text: string, directory: PeerReferenceDirectory): string {\r\n return text.replace(/@([^\\s]+)/g, (_full, token: string) => {\r\n const resolved = expand(token, directory);\r\n return resolved ? `@${resolved}` : `@${token}`;\r\n });\r\n}\r\n\r\n/**\r\n * Compact inline @<full-id> references for rendering.\r\n */\r\nexport function compactInlineReferences(text: string, directory: PeerReferenceDirectory): string {\r\n return text.replace(/@([0-9a-fA-F]{16,})/g, (_full, id: string) => `@${shorten(id, directory)}`);\r\n}\r\n\r\n/**\r\n * Compact inline @<full-id> references only when the full ID exists in the\r\n * provided directory. Unknown IDs remain unchanged.\r\n */\r\nexport function compactKnownInlineReferences(text: string, directory: PeerReferenceDirectory): string {\r\n return text.replace(/@([0-9a-fA-F]{16,})/g, (_full, id: string) => {\r\n const known = findById(id, directory);\r\n if (!known) {\r\n return `@${id}`;\r\n }\r\n return `@${shorten(id, directory)}`;\r\n });\r\n}\r\n\r\n/**\r\n * Strip characters that can crash downstream width/segmenter logic in UIs.\r\n * Removes control chars (except newline/tab) and replaces lone surrogates.\r\n */\r\nexport function sanitizeText(text: string): string {\r\n return text\r\n .replace(/[\\u0000-\\u0008\\u000B\\u000C\\u000E-\\u001F\\u007F-\\u009F]/g, '')\r\n .replace(/[\\uD800-\\uDBFF](?![\\uDC00-\\uDFFF])/g, '\\uFFFD')\r\n .replace(/(?<![\\uD800-\\uDBFF])[\\uDC00-\\uDFFF]/g, '\\uFFFD');\r\n}\r\n\r\n/**\r\n * Resolve a display name for a peer.\r\n * Priority order:\r\n * 1. configured name in directory for the exact public key\r\n * 2. relay-broadcast name (if not already a short-id token)\r\n * 3. undefined\r\n */\r\nexport function resolveDisplayName(\r\n publicKey: string,\r\n peerName: string | undefined,\r\n directory?: PeerReferenceDirectory,\r\n): string | undefined {\r\n const entry = findById(publicKey, directory);\r\n if (entry?.name) {\r\n return entry.name;\r\n }\r\n\r\n if (peerName && !peerName.startsWith('...')) {\r\n return sanitizeText(peerName);\r\n }\r\n\r\n return undefined;\r\n}\r\n\r\n/**\r\n * Resolves the name to broadcast when connecting to a relay.\r\n * Priority order:\r\n * 1. CLI --name flag\r\n * 2. config.relay.name (if relay is an object with name property)\r\n * 3. config.identity.name\r\n * 4. undefined (no name broadcast)\r\n *\r\n * @param config - The Agora configuration (or compatible config with identity and optional relay)\r\n * @param cliName - Optional name from CLI --name flag\r\n * @returns The resolved name to broadcast, or undefined if none available\r\n */\r\nexport function resolveBroadcastName(\r\n config: { identity: { name?: string }; relay?: { name?: string } | string },\r\n cliName?: string\r\n): string | undefined {\r\n // Priority 1: CLI --name flag\r\n if (cliName) {\r\n return cliName;\r\n }\r\n\r\n // Priority 2: config.relay.name (if relay is an object with name property)\r\n if (config.relay) {\r\n if (typeof config.relay === 'object' && config.relay.name) {\r\n return config.relay.name;\r\n }\r\n }\r\n\r\n // Priority 3: config.identity.name\r\n if (config.identity.name) {\r\n return config.identity.name;\r\n }\r\n\r\n // Priority 4: No name available\r\n return undefined;\r\n}\r\n\r\n/**\r\n * Formats a display name with short ID postfix.\r\n * If name exists: \"name (...3f8c2247)\"\r\n * If no name: \"...3f8c2247\" (short ID only)\r\n *\r\n * @param name - Optional name to display (should not be a short ID)\r\n * @param publicKey - The public key to use for short ID\r\n * @returns Formatted display string\r\n */\r\nexport function formatDisplayName(name: string | undefined, publicKey: string): string {\r\n const shortId = shortKey(publicKey);\r\n // If name is undefined, empty, or is already a short ID, return only short ID\r\n if (!name || name.trim() === '' || name.startsWith('...')) {\r\n return shortId;\r\n }\r\n return `${name} (${shortId})`;\r\n}\r\n","/**\r\n * Core data structures for the Agora reputation layer.\r\n * Phase 1: Verification records, commit-reveal patterns, and trust scoring.\r\n */\r\n\r\n/**\r\n * A cryptographically signed verification of another agent's output or claim.\r\n * Core primitive for building computational reputation.\r\n */\r\nexport interface VerificationRecord {\r\n /** Content-addressed ID (hash of canonical JSON) */\r\n id: string;\r\n \r\n /** Public key of verifying agent */\r\n verifier: string;\r\n \r\n /** ID of message/output being verified */\r\n target: string;\r\n \r\n /** Capability domain (e.g., 'ocr', 'summarization', 'code_review') */\r\n domain: string;\r\n \r\n /** Verification verdict */\r\n verdict: 'correct' | 'incorrect' | 'disputed';\r\n \r\n /** Verifier's confidence in their check (0-1) */\r\n confidence: number;\r\n \r\n /** Optional link to independent verification data */\r\n evidence?: string;\r\n \r\n /** Unix timestamp (ms) */\r\n timestamp: number;\r\n \r\n /** Ed25519 signature over canonical JSON */\r\n signature: string;\r\n}\r\n\r\n/**\r\n * A commitment to a prediction before outcome is known.\r\n * Prevents post-hoc editing of predictions.\r\n */\r\nexport interface CommitRecord {\r\n /** Content-addressed ID */\r\n id: string;\r\n \r\n /** Public key of committing agent */\r\n agent: string;\r\n \r\n /** Domain of prediction */\r\n domain: string;\r\n \r\n /** SHA-256 hash of prediction string */\r\n commitment: string;\r\n \r\n /** Unix timestamp (ms) */\r\n timestamp: number;\r\n \r\n /** Expiry time (ms) - commitment invalid after this */\r\n expiry: number;\r\n \r\n /** Ed25519 signature */\r\n signature: string;\r\n}\r\n\r\n/**\r\n * Reveals the prediction and outcome after commitment expiry.\r\n * Enables verification of prediction accuracy.\r\n */\r\nexport interface RevealRecord {\r\n /** Content-addressed ID */\r\n id: string;\r\n \r\n /** Public key of revealing agent */\r\n agent: string;\r\n \r\n /** ID of original commit message */\r\n commitmentId: string;\r\n \r\n /** Original prediction (plaintext) */\r\n prediction: string;\r\n \r\n /** Observed outcome */\r\n outcome: string;\r\n \r\n /** Evidence for outcome (optional) */\r\n evidence?: string;\r\n \r\n /** Unix timestamp (ms) */\r\n timestamp: number;\r\n \r\n /** Ed25519 signature */\r\n signature: string;\r\n}\r\n\r\n/**\r\n * Computed reputation score for an agent in a specific domain.\r\n * Derived from verification history, not stored directly.\r\n */\r\nexport interface TrustScore {\r\n /** Public key of agent being scored */\r\n agent: string;\r\n \r\n /** Domain of reputation */\r\n domain: string;\r\n \r\n /** Computed score (0-1, where 1 = highest trust) */\r\n score: number;\r\n \r\n /** Number of verifications considered */\r\n verificationCount: number;\r\n \r\n /** Timestamp of most recent verification (ms) */\r\n lastVerified: number;\r\n \r\n /** Public keys of top verifiers (by weight) */\r\n topVerifiers: string[];\r\n}\r\n\r\n/**\r\n * Request for reputation data about a specific agent.\r\n */\r\nexport interface ReputationQuery {\r\n /** Public key of agent being queried */\r\n agent: string;\r\n \r\n /** Optional: filter by capability domain */\r\n domain?: string;\r\n \r\n /** Optional: only include verifications after this timestamp */\r\n after?: number;\r\n}\r\n\r\n/**\r\n * Response containing reputation data for a queried agent.\r\n */\r\nexport interface ReputationResponse {\r\n /** Public key of agent being reported on */\r\n agent: string;\r\n \r\n /** Domain filter (if requested) */\r\n domain?: string;\r\n \r\n /** Verification records matching the query */\r\n verifications: VerificationRecord[];\r\n \r\n /** Computed trust scores by domain */\r\n scores: Record<string, TrustScore>;\r\n}\r\n\r\n/**\r\n * Revocation of a previously issued verification.\r\n * Used when a verifier discovers their verification was incorrect.\r\n */\r\nexport interface RevocationRecord {\r\n /** Content-addressed ID of this revocation */\r\n id: string;\r\n \r\n /** Public key of agent revoking (must match original verifier) */\r\n verifier: string;\r\n \r\n /** ID of verification being revoked */\r\n verificationId: string;\r\n \r\n /** Reason for revocation */\r\n reason: string;\r\n \r\n /** Unix timestamp (ms) */\r\n timestamp: number;\r\n \r\n /** Ed25519 signature */\r\n signature: string;\r\n}\r\n\r\n/**\r\n * Validation result structure\r\n */\r\nexport interface ValidationResult {\r\n valid: boolean;\r\n errors: string[];\r\n}\r\n\r\n/**\r\n * Validate a verification record structure\r\n */\r\nexport function validateVerificationRecord(record: unknown): ValidationResult {\r\n const errors: string[] = [];\r\n \r\n if (typeof record !== 'object' || record === null) {\r\n return { valid: false, errors: ['Record must be an object'] };\r\n }\r\n \r\n const r = record as Record<string, unknown>;\r\n \r\n if (typeof r.id !== 'string' || r.id.length === 0) {\r\n errors.push('id must be a non-empty string');\r\n }\r\n \r\n if (typeof r.verifier !== 'string' || r.verifier.length === 0) {\r\n errors.push('verifier must be a non-empty string');\r\n }\r\n \r\n if (typeof r.target !== 'string' || r.target.length === 0) {\r\n errors.push('target must be a non-empty string');\r\n }\r\n \r\n if (typeof r.domain !== 'string' || r.domain.length === 0) {\r\n errors.push('domain must be a non-empty string');\r\n }\r\n \r\n if (!['correct', 'incorrect', 'disputed'].includes(r.verdict as string)) {\r\n errors.push('verdict must be one of: correct, incorrect, disputed');\r\n }\r\n \r\n if (typeof r.confidence !== 'number' || r.confidence < 0 || r.confidence > 1) {\r\n errors.push('confidence must be a number between 0 and 1');\r\n }\r\n \r\n if (r.evidence !== undefined && typeof r.evidence !== 'string') {\r\n errors.push('evidence must be a string if provided');\r\n }\r\n \r\n if (typeof r.timestamp !== 'number' || r.timestamp <= 0) {\r\n errors.push('timestamp must be a positive number');\r\n }\r\n \r\n if (typeof r.signature !== 'string' || r.signature.length === 0) {\r\n errors.push('signature must be a non-empty string');\r\n }\r\n \r\n return { valid: errors.length === 0, errors };\r\n}\r\n\r\n/**\r\n * Validate a commit record structure\r\n */\r\nexport function validateCommitRecord(record: unknown): ValidationResult {\r\n const errors: string[] = [];\r\n \r\n if (typeof record !== 'object' || record === null) {\r\n return { valid: false, errors: ['Record must be an object'] };\r\n }\r\n \r\n const r = record as Record<string, unknown>;\r\n \r\n if (typeof r.id !== 'string' || r.id.length === 0) {\r\n errors.push('id must be a non-empty string');\r\n }\r\n \r\n if (typeof r.agent !== 'string' || r.agent.length === 0) {\r\n errors.push('agent must be a non-empty string');\r\n }\r\n \r\n if (typeof r.domain !== 'string' || r.domain.length === 0) {\r\n errors.push('domain must be a non-empty string');\r\n }\r\n \r\n if (typeof r.commitment !== 'string' || r.commitment.length !== 64) {\r\n errors.push('commitment must be a 64-character hex string (SHA-256 hash)');\r\n }\r\n \r\n if (typeof r.timestamp !== 'number' || r.timestamp <= 0) {\r\n errors.push('timestamp must be a positive number');\r\n }\r\n \r\n if (typeof r.expiry !== 'number' || r.expiry <= 0) {\r\n errors.push('expiry must be a positive number');\r\n }\r\n \r\n if (typeof r.expiry === 'number' && typeof r.timestamp === 'number' && r.expiry <= r.timestamp) {\r\n errors.push('expiry must be after timestamp');\r\n }\r\n \r\n if (typeof r.signature !== 'string' || r.signature.length === 0) {\r\n errors.push('signature must be a non-empty string');\r\n }\r\n \r\n return { valid: errors.length === 0, errors };\r\n}\r\n\r\n/**\r\n * Validate a reveal record structure\r\n */\r\nexport function validateRevealRecord(record: unknown): ValidationResult {\r\n const errors: string[] = [];\r\n \r\n if (typeof record !== 'object' || record === null) {\r\n return { valid: false, errors: ['Record must be an object'] };\r\n }\r\n \r\n const r = record as Record<string, unknown>;\r\n \r\n if (typeof r.id !== 'string' || r.id.length === 0) {\r\n errors.push('id must be a non-empty string');\r\n }\r\n \r\n if (typeof r.agent !== 'string' || r.agent.length === 0) {\r\n errors.push('agent must be a non-empty string');\r\n }\r\n \r\n if (typeof r.commitmentId !== 'string' || r.commitmentId.length === 0) {\r\n errors.push('commitmentId must be a non-empty string');\r\n }\r\n \r\n if (typeof r.prediction !== 'string' || r.prediction.length === 0) {\r\n errors.push('prediction must be a non-empty string');\r\n }\r\n \r\n if (typeof r.outcome !== 'string' || r.outcome.length === 0) {\r\n errors.push('outcome must be a non-empty string');\r\n }\r\n \r\n if (r.evidence !== undefined && typeof r.evidence !== 'string') {\r\n errors.push('evidence must be a string if provided');\r\n }\r\n \r\n if (typeof r.timestamp !== 'number' || r.timestamp <= 0) {\r\n errors.push('timestamp must be a positive number');\r\n }\r\n \r\n if (typeof r.signature !== 'string' || r.signature.length === 0) {\r\n errors.push('signature must be a non-empty string');\r\n }\r\n \r\n return { valid: errors.length === 0, errors };\r\n}\r\n","/**\n * Local reputation store using JSONL append-only log.\n * Stores verification records, commits, and reveals.\n */\n\nimport { promises as fs } from 'node:fs';\nimport { dirname } from 'node:path';\nimport type { VerificationRecord, CommitRecord, RevealRecord } from './types';\nimport { validateVerificationRecord, validateCommitRecord, validateRevealRecord } from './types';\n\n/**\n * Record type discriminator for JSONL storage\n */\ntype StoredRecord = \n | ({ type: 'verification' } & VerificationRecord)\n | ({ type: 'commit' } & CommitRecord)\n | ({ type: 'reveal' } & RevealRecord);\n\n/**\n * Reputation store with JSONL persistence\n */\nexport class ReputationStore {\n private filePath: string;\n private verifications: Map<string, VerificationRecord> = new Map();\n private commits: Map<string, CommitRecord> = new Map();\n private reveals: Map<string, RevealRecord> = new Map();\n private loaded = false;\n\n constructor(filePath: string) {\n this.filePath = filePath;\n }\n\n /**\n * Load records from JSONL file\n */\n async load(): Promise<void> {\n try {\n const content = await fs.readFile(this.filePath, 'utf-8');\n const lines = content.trim().split('\\n').filter(line => line.length > 0);\n \n for (const line of lines) {\n try {\n const record = JSON.parse(line) as StoredRecord;\n \n switch (record.type) {\n case 'verification': {\n // eslint-disable-next-line @typescript-eslint/no-unused-vars\n const { type: _type, ...verification } = record;\n const validation = validateVerificationRecord(verification);\n if (validation.valid) {\n this.verifications.set(verification.id, verification as VerificationRecord);\n }\n break;\n }\n case 'commit': {\n // eslint-disable-next-line @typescript-eslint/no-unused-vars\n const { type: _type, ...commit } = record;\n const validation = validateCommitRecord(commit);\n if (validation.valid) {\n this.commits.set(commit.id, commit as CommitRecord);\n }\n break;\n }\n case 'reveal': {\n // eslint-disable-next-line @typescript-eslint/no-unused-vars\n const { type: _type, ...reveal } = record;\n const validation = validateRevealRecord(reveal);\n if (validation.valid) {\n this.reveals.set(reveal.id, reveal as RevealRecord);\n }\n break;\n }\n }\n } catch {\n // Skip invalid lines\n continue;\n }\n }\n \n this.loaded = true;\n } catch (error) {\n // File doesn't exist yet - that's okay for first run\n if ((error as NodeJS.ErrnoException).code === 'ENOENT') {\n this.loaded = true;\n return;\n }\n throw error;\n }\n }\n\n /**\n * Ensure the store is loaded\n */\n private async ensureLoaded(): Promise<void> {\n if (!this.loaded) {\n await this.load();\n }\n }\n\n /**\n * Append a record to the JSONL file\n */\n private async appendToFile(record: StoredRecord): Promise<void> {\n // Ensure directory exists\n await fs.mkdir(dirname(this.filePath), { recursive: true });\n \n // Append JSONL line\n const line = JSON.stringify(record) + '\\n';\n await fs.appendFile(this.filePath, line, 'utf-8');\n }\n\n /**\n * Add a verification record\n */\n async addVerification(verification: VerificationRecord): Promise<void> {\n await this.ensureLoaded();\n \n const validation = validateVerificationRecord(verification);\n if (!validation.valid) {\n throw new Error(`Invalid verification: ${validation.errors.join(', ')}`);\n }\n \n this.verifications.set(verification.id, verification);\n await this.appendToFile({ type: 'verification', ...verification });\n }\n\n /**\n * Add a commit record\n */\n async addCommit(commit: CommitRecord): Promise<void> {\n await this.ensureLoaded();\n \n const validation = validateCommitRecord(commit);\n if (!validation.valid) {\n throw new Error(`Invalid commit: ${validation.errors.join(', ')}`);\n }\n \n this.commits.set(commit.id, commit);\n await this.appendToFile({ type: 'commit', ...commit });\n }\n\n /**\n * Add a reveal record\n */\n async addReveal(reveal: RevealRecord): Promise<void> {\n await this.ensureLoaded();\n \n const validation = validateRevealRecord(reveal);\n if (!validation.valid) {\n throw new Error(`Invalid reveal: ${validation.errors.join(', ')}`);\n }\n \n this.reveals.set(reveal.id, reveal);\n await this.appendToFile({ type: 'reveal', ...reveal });\n }\n\n /**\n * Get all verifications\n */\n async getVerifications(): Promise<VerificationRecord[]> {\n await this.ensureLoaded();\n return Array.from(this.verifications.values());\n }\n\n /**\n * Get verifications for a specific target\n */\n async getVerificationsByTarget(target: string): Promise<VerificationRecord[]> {\n await this.ensureLoaded();\n return Array.from(this.verifications.values()).filter(v => v.target === target);\n }\n\n /**\n * Get verifications by domain\n */\n async getVerificationsByDomain(domain: string): Promise<VerificationRecord[]> {\n await this.ensureLoaded();\n return Array.from(this.verifications.values()).filter(v => v.domain === domain);\n }\n\n /**\n * Get verifications for an agent (where they are the target of verification)\n * This requires looking up the target message to find the agent\n * For now, we'll return all verifications and let the caller filter\n */\n async getVerificationsByDomainForAgent(domain: string): Promise<VerificationRecord[]> {\n return this.getVerificationsByDomain(domain);\n }\n\n /**\n * Get all commits\n */\n async getCommits(): Promise<CommitRecord[]> {\n await this.ensureLoaded();\n return Array.from(this.commits.values());\n }\n\n /**\n * Get commit by ID\n */\n async getCommit(id: string): Promise<CommitRecord | null> {\n await this.ensureLoaded();\n return this.commits.get(id) || null;\n }\n\n /**\n * Get commits by agent\n */\n async getCommitsByAgent(agent: string): Promise<CommitRecord[]> {\n await this.ensureLoaded();\n return Array.from(this.commits.values()).filter(c => c.agent === agent);\n }\n\n /**\n * Get all reveals\n */\n async getReveals(): Promise<RevealRecord[]> {\n await this.ensureLoaded();\n return Array.from(this.reveals.values());\n }\n\n /**\n * Get reveal by commitment ID\n */\n async getRevealByCommitment(commitmentId: string): Promise<RevealRecord | null> {\n await this.ensureLoaded();\n return Array.from(this.reveals.values()).find(r => r.commitmentId === commitmentId) || null;\n }\n\n /**\n * Get reveals by agent\n */\n async getRevealsByAgent(agent: string): Promise<RevealRecord[]> {\n await this.ensureLoaded();\n return Array.from(this.reveals.values()).filter(r => r.agent === agent);\n }\n}\n","/**\n * Verification record creation and validation.\n * Core primitive for computational reputation.\n */\n\nimport { createEnvelope, verifyEnvelope } from '../message/envelope';\nimport type { VerificationRecord } from './types';\nimport { validateVerificationRecord } from './types';\n\n/**\n * Create a signed verification record\n * @param verifier - Public key of the verifying agent\n * @param privateKey - Private key for signing\n * @param target - ID of the message/output being verified\n * @param domain - Capability domain\n * @param verdict - Verification verdict\n * @param confidence - Verifier's confidence (0-1)\n * @param timestamp - Timestamp for the verification (ms)\n * @param evidence - Optional link to verification evidence\n * @returns Signed VerificationRecord\n */\nexport function createVerification(\n verifier: string,\n privateKey: string,\n target: string,\n domain: string,\n verdict: 'correct' | 'incorrect' | 'disputed',\n confidence: number,\n timestamp: number,\n evidence?: string\n): VerificationRecord {\n // Validate confidence range\n if (confidence < 0 || confidence > 1) {\n throw new Error('confidence must be between 0 and 1');\n }\n \n // Create the payload for signing\n const payload: Record<string, unknown> = {\n verifier,\n target,\n domain,\n verdict,\n confidence,\n timestamp,\n };\n \n if (evidence !== undefined) {\n payload.evidence = evidence;\n }\n \n // Create signed envelope with type 'verification'\n const envelope = createEnvelope('verification', verifier, privateKey, payload, timestamp, undefined, [verifier]);\n \n // Return verification record\n const record: VerificationRecord = {\n id: envelope.id,\n verifier,\n target,\n domain,\n verdict,\n confidence,\n timestamp,\n signature: envelope.signature,\n };\n \n if (evidence !== undefined) {\n record.evidence = evidence;\n }\n \n return record;\n}\n\n/**\n * Verify the cryptographic signature of a verification record\n * @param record - The verification record to verify\n * @returns Object with valid flag and optional reason for failure\n */\nexport function verifyVerificationSignature(\n record: VerificationRecord\n): { valid: boolean; reason?: string } {\n // First validate the structure\n const structureValidation = validateVerificationRecord(record);\n if (!structureValidation.valid) {\n return { \n valid: false, \n reason: `Invalid structure: ${structureValidation.errors.join(', ')}` \n };\n }\n \n // Reconstruct the envelope for signature verification\n const payload: Record<string, unknown> = {\n verifier: record.verifier,\n target: record.target,\n domain: record.domain,\n verdict: record.verdict,\n confidence: record.confidence,\n timestamp: record.timestamp,\n };\n \n if (record.evidence !== undefined) {\n payload.evidence = record.evidence;\n }\n \n const envelope = {\n id: record.id,\n type: 'verification' as const,\n from: record.verifier,\n to: [record.verifier],\n timestamp: record.timestamp,\n payload,\n signature: record.signature,\n };\n \n // Verify the envelope signature\n return verifyEnvelope(envelope);\n}\n","/**\n * Commit-reveal pattern implementation for tamper-proof predictions.\n * Agents commit to predictions before outcomes are known, then reveal after.\n */\n\nimport { createHash } from 'node:crypto';\nimport { createEnvelope } from '../message/envelope';\nimport type { CommitRecord, RevealRecord } from './types';\nimport { validateCommitRecord, validateRevealRecord } from './types';\n\n/**\n * Create a commitment hash for a prediction\n * @param prediction - The prediction string\n * @returns SHA-256 hash of the prediction (hex string)\n */\nexport function hashPrediction(prediction: string): string {\n return createHash('sha256').update(prediction).digest('hex');\n}\n\n/**\n * Create a signed commit record\n * @param agent - Public key of the committing agent\n * @param privateKey - Private key for signing\n * @param domain - Domain of the prediction\n * @param prediction - The prediction to commit to\n * @param timestamp - Timestamp for the commit (ms)\n * @param expiryMs - Expiry time in milliseconds from timestamp\n * @returns Signed CommitRecord\n */\nexport function createCommit(\n agent: string,\n privateKey: string,\n domain: string,\n prediction: string,\n timestamp: number,\n expiryMs: number\n): CommitRecord {\n const commitment = hashPrediction(prediction);\n const expiry = timestamp + expiryMs;\n \n // Create the payload for signing\n const payload = {\n agent,\n domain,\n commitment,\n timestamp,\n expiry,\n };\n \n // Create signed envelope with type 'commit'\n const envelope = createEnvelope('commit', agent, privateKey, payload, timestamp, undefined, [agent]);\n \n // Return commit record\n return {\n id: envelope.id,\n agent,\n domain,\n commitment,\n timestamp,\n expiry,\n signature: envelope.signature,\n };\n}\n\n/**\n * Create a signed reveal record\n * @param agent - Public key of the revealing agent\n * @param privateKey - Private key for signing\n * @param commitmentId - ID of the original commit record\n * @param prediction - The original prediction (plaintext)\n * @param outcome - The observed outcome\n * @param timestamp - Timestamp for the reveal (ms)\n * @param evidence - Optional evidence for the outcome\n * @returns Signed RevealRecord\n */\nexport function createReveal(\n agent: string,\n privateKey: string,\n commitmentId: string,\n prediction: string,\n outcome: string,\n timestamp: number,\n evidence?: string\n): RevealRecord {\n \n // Create the payload for signing\n const payload: Record<string, unknown> = {\n agent,\n commitmentId,\n prediction,\n outcome,\n timestamp,\n };\n \n if (evidence !== undefined) {\n payload.evidence = evidence;\n }\n \n // Create signed envelope with type 'reveal'\n const envelope = createEnvelope('reveal', agent, privateKey, payload, timestamp, undefined, [agent]);\n \n // Return reveal record\n const record: RevealRecord = {\n id: envelope.id,\n agent,\n commitmentId,\n prediction,\n outcome,\n timestamp,\n signature: envelope.signature,\n };\n \n if (evidence !== undefined) {\n record.evidence = evidence;\n }\n \n return record;\n}\n\n/**\n * Verify a reveal against its commitment\n * @param commit - The original commit record\n * @param reveal - The reveal record\n * @returns Object with valid flag and optional reason for failure\n */\nexport function verifyReveal(\n commit: CommitRecord,\n reveal: RevealRecord\n): { valid: boolean; reason?: string } {\n // Validate structures\n const commitValidation = validateCommitRecord(commit);\n if (!commitValidation.valid) {\n return { valid: false, reason: `Invalid commit: ${commitValidation.errors.join(', ')}` };\n }\n \n const revealValidation = validateRevealRecord(reveal);\n if (!revealValidation.valid) {\n return { valid: false, reason: `Invalid reveal: ${revealValidation.errors.join(', ')}` };\n }\n \n // Check that reveal references the correct commit\n if (reveal.commitmentId !== commit.id) {\n return { valid: false, reason: 'Reveal does not reference this commit' };\n }\n \n // Check that agents match\n if (reveal.agent !== commit.agent) {\n return { valid: false, reason: 'Reveal agent does not match commit agent' };\n }\n \n // Check that reveal is after commit expiry\n if (reveal.timestamp < commit.expiry) {\n return { valid: false, reason: 'Reveal timestamp is before commit expiry' };\n }\n \n // Verify that the prediction hash matches the commitment\n const predictedHash = hashPrediction(reveal.prediction);\n if (predictedHash !== commit.commitment) {\n return { valid: false, reason: 'Prediction hash does not match commitment' };\n }\n \n return { valid: true };\n}\n","/**\r\n * Trust score computation with exponential time decay.\r\n * Domain-specific reputation scoring from verification history.\r\n */\r\n\r\nimport type { VerificationRecord, TrustScore } from './types';\r\n\r\n/**\r\n * Exponential decay function for time-based reputation degradation.\r\n * @param deltaTimeMs - Time since verification (milliseconds)\r\n * @param lambda - Decay rate (default: ln(2)/70 ≈ 0.0099, giving 70-day half-life)\r\n * @returns Weight multiplier (0-1)\r\n */\r\nexport function decay(deltaTimeMs: number, lambda = Math.log(2) / 70): number {\r\n const deltaDays = deltaTimeMs / (1000 * 60 * 60 * 24);\r\n return Math.exp(-lambda * deltaDays);\r\n}\r\n\r\n/**\r\n * Compute verdict weight\r\n * @param verdict - Verification verdict\r\n * @returns Weight value (+1 for correct, -1 for incorrect, 0 for disputed)\r\n */\r\nfunction verdictWeight(verdict: 'correct' | 'incorrect' | 'disputed'): number {\r\n switch (verdict) {\r\n case 'correct':\r\n return 1;\r\n case 'incorrect':\r\n return -1;\r\n case 'disputed':\r\n return 0;\r\n }\r\n}\r\n\r\n/**\r\n * Options for recursive trust score computation.\r\n */\r\nexport interface TrustScoreOptions {\r\n /**\r\n * Optional function to get verifier's trust score for recursive weighting.\r\n * When provided, each verification is weighted by the verifier's own trust score.\r\n * Defaults to returning 1.0 (flat weighting, backward compatible).\r\n * New agents with no score should return 0.5 (neutral bootstrapping weight).\r\n */\r\n getVerifierScore?: (verifier: string, domain: string) => number;\r\n /**\r\n * Maximum recursion depth for recursive scoring. Default: 3.\r\n * At depth 0, flat weighting (1.0) is used instead of calling getVerifierScore.\r\n */\r\n maxDepth?: number;\r\n /**\r\n * Internal: set of agents currently being scored, used for cycle detection.\r\n * Pass a shared mutable Set when making recursive calls to enable cycle detection.\r\n * When a verifier is found in this set, neutral weight (0.5) is used instead of recursing.\r\n */\r\n visitedAgents?: Set<string>;\r\n}\r\n\r\n/**\r\n * Compute trust score for an agent in a specific domain\r\n * @param agent - Public key of the agent being scored\r\n * @param domain - Capability domain\r\n * @param verifications - All verification records (will be filtered by target and domain)\r\n * @param currentTime - Current timestamp (ms)\r\n * @param options - Optional settings for recursive scoring and cycle detection\r\n * @returns TrustScore object with computed reputation\r\n */\r\nexport function computeTrustScore(\r\n agent: string,\r\n domain: string,\r\n verifications: VerificationRecord[],\r\n currentTime: number,\r\n options?: TrustScoreOptions\r\n): TrustScore {\r\n // Filter verifications for this agent and domain\r\n const relevantVerifications = verifications.filter(\r\n v => v.target === agent && v.domain === domain\r\n );\r\n \r\n if (relevantVerifications.length === 0) {\r\n return {\r\n agent,\r\n domain,\r\n score: 0,\r\n verificationCount: 0,\r\n lastVerified: 0,\r\n topVerifiers: [],\r\n };\r\n }\r\n \r\n const maxDepth = options?.maxDepth ?? 3;\r\n const visitedAgents = options?.visitedAgents;\r\n const getVerifierScore = options?.getVerifierScore;\r\n\r\n // Mark current agent as being scored (cycle detection)\r\n if (visitedAgents) {\r\n visitedAgents.add(agent);\r\n }\r\n\r\n // Compute weighted score with time decay\r\n let totalWeight = 0;\r\n const verifierWeights = new Map<string, number>();\r\n \r\n for (const verification of relevantVerifications) {\r\n const deltaTime = currentTime - verification.timestamp;\r\n const decayFactor = decay(deltaTime);\r\n const verdict = verdictWeight(verification.verdict);\r\n\r\n // Determine verifier trust weight for recursive scoring\r\n let verifierTrustWeight: number;\r\n if (!getVerifierScore || maxDepth <= 0) {\r\n // No recursive scoring or depth limit reached — use flat weight\r\n verifierTrustWeight = 1.0;\r\n } else if (visitedAgents?.has(verification.verifier)) {\r\n // Cycle detected — use neutral weight (0.5) instead of recursing\r\n verifierTrustWeight = 0.5;\r\n } else {\r\n verifierTrustWeight = getVerifierScore(verification.verifier, domain);\r\n }\r\n\r\n const weight = verdict * verification.confidence * decayFactor * verifierTrustWeight;\r\n \r\n totalWeight += weight;\r\n \r\n // Track verifier contributions\r\n const currentVerifierWeight = verifierWeights.get(verification.verifier) ?? 0;\r\n verifierWeights.set(verification.verifier, currentVerifierWeight + Math.abs(weight));\r\n }\r\n\r\n // Backtrack: remove current agent from visited set for correct DFS traversal\r\n if (visitedAgents) {\r\n visitedAgents.delete(agent);\r\n }\r\n \r\n // Normalize score to 0-1 range\r\n // Positive verifications push toward 1, negative push toward 0\r\n const rawScore = totalWeight / Math.max(relevantVerifications.length, 1);\r\n const normalizedScore = Math.max(0, Math.min(1, (rawScore + 1) / 2));\r\n \r\n // Find most recent verification\r\n const lastVerified = Math.max(...relevantVerifications.map(v => v.timestamp));\r\n \r\n // Get top verifiers by absolute weight\r\n const topVerifiers = Array.from(verifierWeights.entries())\r\n .sort((a, b) => b[1] - a[1])\r\n .slice(0, 5)\r\n .map(([verifier]) => verifier);\r\n \r\n return {\r\n agent,\r\n domain,\r\n score: normalizedScore,\r\n verificationCount: relevantVerifications.length,\r\n lastVerified,\r\n topVerifiers,\r\n };\r\n}\r\n\r\n/**\r\n * Compute trust scores for an agent across multiple domains\r\n * @param agent - Public key of the agent being scored\r\n * @param verifications - All verification records\r\n * @param currentTime - Current timestamp (ms)\r\n * @returns Map of domain to TrustScore\r\n */\r\nexport function computeTrustScores(\r\n agent: string,\r\n verifications: VerificationRecord[],\r\n currentTime: number\r\n): Map<string, TrustScore> {\r\n // Get unique domains for this agent\r\n const domains = new Set(\r\n verifications\r\n .filter(v => v.target === agent)\r\n .map(v => v.domain)\r\n );\r\n \r\n const scores = new Map<string, TrustScore>();\r\n for (const domain of domains) {\r\n const score = computeTrustScore(agent, domain, verifications, currentTime);\r\n scores.set(domain, score);\r\n }\r\n \r\n return scores;\r\n}\r\n\r\n// Alias for backward compatibility\r\nexport const computeAllTrustScores = computeTrustScores;\r\n"],"mappings":"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","names":["WebSocket","WebSocket","EventEmitter","EventEmitter"]}
|
package/dist/cli.js
CHANGED
package/dist/index.d.ts
CHANGED
|
@@ -1194,6 +1194,11 @@ declare function expandInlineReferences(text: string, directory: PeerReferenceDi
|
|
|
1194
1194
|
* Compact inline @<full-id> references for rendering.
|
|
1195
1195
|
*/
|
|
1196
1196
|
declare function compactInlineReferences(text: string, directory: PeerReferenceDirectory): string;
|
|
1197
|
+
/**
|
|
1198
|
+
* Compact inline @<full-id> references only when the full ID exists in the
|
|
1199
|
+
* provided directory. Unknown IDs remain unchanged.
|
|
1200
|
+
*/
|
|
1201
|
+
declare function compactKnownInlineReferences(text: string, directory: PeerReferenceDirectory): string;
|
|
1197
1202
|
/**
|
|
1198
1203
|
* Strip characters that can crash downstream width/segmenter logic in UIs.
|
|
1199
1204
|
* Removes control chars (except newline/tab) and replaces lone surrogates.
|
|
@@ -1841,4 +1846,4 @@ declare function syncReputationFromPeer(agentPublicKey: string, domain: string,
|
|
|
1841
1846
|
skipped: number;
|
|
1842
1847
|
}>;
|
|
1843
1848
|
|
|
1844
|
-
export { type AgoraConfig, type AgoraIdentity, type AgoraPeerConfig, AgoraService, type AgoraServiceConfig, type AnnouncePayload, type AuthenticatedRequest, type BootstrapConfig, type BufferedMessage, type Capability, type CapabilityAnnouncePayload, type CapabilityQueryPayload, type CapabilityResponsePayload, type CommitRecord, type CreateEnvelopeFn, DEFAULT_BOOTSTRAP_RELAYS, type DecodeInboundResult, type DiscoverPayload, type DiscoverResponsePayload, DiscoveryService, type Envelope, IGNORED_FILE_NAME, IgnoredPeersManager, InboundMessageGuard, type InboundSecurityOptions, type JwtPayload, type KeyPair, type Logger, MessageBuffer, MessageStore, type MessageType, type PaperDiscoveryPayload, type Peer, type PeerConfig, type PeerConfigFile, type PeerDiscoveryConfig, type PeerDiscoveryEvents, PeerDiscoveryService, type PeerListRequestPayload, type PeerListResponsePayload, type PeerReferenceDirectory, type PeerReferenceEntry, type PeerReferralPayload, PeerStore, RelayClient, type RelayClientConfig, type RelayClientEvents, type RelayClientFactory, type RelayClientLike, type RelayClientMessage, type RelayConfig, type RelayEnvelopeDedupOptions, type RelayInterface, type RelayMessageHandler, type RelayMessageHandlerWithName, type RelayPeer, type RelayRateLimitOptions, RelayServer, type RelayServerEvents, type RelayServerMessage, type RelayServerOptions, type ReplyToEnvelopeOptions, type ReputationQuery, type ReputationResponse, ReputationStore, type RestSession, type RevealRecord, type RevocationRecord, type RunRelayOptions, type SendMessageOptions, type SendMessageResult, type StoredMessage, type TransportConfig, type TrustScore, type TrustScoreOptions, type ValidationResult, type VerificationRecord, type VerifyEnvelopeFn, canonicalize, compactInlineReferences, computeAllTrustScores, computeId, computeTrustScore, computeTrustScores, createCapability, createCommit, createEnvelope, createRestRouter, createReveal, createToken, createVerification, decay, decodeInboundEnvelope, expand, expandInlineReferences, exportKeyPair, formatDisplayName, generateKeyPair, getDefaultBootstrapRelay, getDefaultConfigPath, getIgnoredPeersPath, handleReputationQuery, hashPrediction, importKeyPair, initPeerConfig, loadAgoraConfig, loadAgoraConfigAsync, loadIgnoredPeers, loadPeerConfig, parseBootstrapRelay, requireAuth, resolveBroadcastName, resolveDisplayName, revokeToken, runRelay, sanitizeText, saveIgnoredPeers, savePeerConfig, sendToPeer, shortKey, shorten, signMessage, syncReputationFromPeer, validateCapability, validateCommitRecord, validatePeerListRequest, validatePeerListResponse, validatePeerReferral, validateRevealRecord, validateVerificationRecord, verifyEnvelope, verifyReveal, verifySignature, verifyVerificationSignature };
|
|
1849
|
+
export { type AgoraConfig, type AgoraIdentity, type AgoraPeerConfig, AgoraService, type AgoraServiceConfig, type AnnouncePayload, type AuthenticatedRequest, type BootstrapConfig, type BufferedMessage, type Capability, type CapabilityAnnouncePayload, type CapabilityQueryPayload, type CapabilityResponsePayload, type CommitRecord, type CreateEnvelopeFn, DEFAULT_BOOTSTRAP_RELAYS, type DecodeInboundResult, type DiscoverPayload, type DiscoverResponsePayload, DiscoveryService, type Envelope, IGNORED_FILE_NAME, IgnoredPeersManager, InboundMessageGuard, type InboundSecurityOptions, type JwtPayload, type KeyPair, type Logger, MessageBuffer, MessageStore, type MessageType, type PaperDiscoveryPayload, type Peer, type PeerConfig, type PeerConfigFile, type PeerDiscoveryConfig, type PeerDiscoveryEvents, PeerDiscoveryService, type PeerListRequestPayload, type PeerListResponsePayload, type PeerReferenceDirectory, type PeerReferenceEntry, type PeerReferralPayload, PeerStore, RelayClient, type RelayClientConfig, type RelayClientEvents, type RelayClientFactory, type RelayClientLike, type RelayClientMessage, type RelayConfig, type RelayEnvelopeDedupOptions, type RelayInterface, type RelayMessageHandler, type RelayMessageHandlerWithName, type RelayPeer, type RelayRateLimitOptions, RelayServer, type RelayServerEvents, type RelayServerMessage, type RelayServerOptions, type ReplyToEnvelopeOptions, type ReputationQuery, type ReputationResponse, ReputationStore, type RestSession, type RevealRecord, type RevocationRecord, type RunRelayOptions, type SendMessageOptions, type SendMessageResult, type StoredMessage, type TransportConfig, type TrustScore, type TrustScoreOptions, type ValidationResult, type VerificationRecord, type VerifyEnvelopeFn, canonicalize, compactInlineReferences, compactKnownInlineReferences, computeAllTrustScores, computeId, computeTrustScore, computeTrustScores, createCapability, createCommit, createEnvelope, createRestRouter, createReveal, createToken, createVerification, decay, decodeInboundEnvelope, expand, expandInlineReferences, exportKeyPair, formatDisplayName, generateKeyPair, getDefaultBootstrapRelay, getDefaultConfigPath, getIgnoredPeersPath, handleReputationQuery, hashPrediction, importKeyPair, initPeerConfig, loadAgoraConfig, loadAgoraConfigAsync, loadIgnoredPeers, loadPeerConfig, parseBootstrapRelay, requireAuth, resolveBroadcastName, resolveDisplayName, revokeToken, runRelay, sanitizeText, saveIgnoredPeers, savePeerConfig, sendToPeer, shortKey, shorten, signMessage, syncReputationFromPeer, validateCapability, validateCommitRecord, validatePeerListRequest, validatePeerListResponse, validatePeerReferral, validateRevealRecord, validateVerificationRecord, verifyEnvelope, verifyReveal, verifySignature, verifyVerificationSignature };
|
package/dist/index.js
CHANGED
|
@@ -4,6 +4,7 @@ import {
|
|
|
4
4
|
RelayClient,
|
|
5
5
|
ReputationStore,
|
|
6
6
|
compactInlineReferences,
|
|
7
|
+
compactKnownInlineReferences,
|
|
7
8
|
computeAllTrustScores,
|
|
8
9
|
computeTrustScore,
|
|
9
10
|
computeTrustScores,
|
|
@@ -33,7 +34,7 @@ import {
|
|
|
33
34
|
validateVerificationRecord,
|
|
34
35
|
verifyReveal,
|
|
35
36
|
verifyVerificationSignature
|
|
36
|
-
} from "./chunk-
|
|
37
|
+
} from "./chunk-LV6NP6BY.js";
|
|
37
38
|
import {
|
|
38
39
|
MessageBuffer,
|
|
39
40
|
createRestRouter,
|
|
@@ -1023,6 +1024,7 @@ export {
|
|
|
1023
1024
|
ReputationStore,
|
|
1024
1025
|
canonicalize,
|
|
1025
1026
|
compactInlineReferences,
|
|
1027
|
+
compactKnownInlineReferences,
|
|
1026
1028
|
computeAllTrustScores,
|
|
1027
1029
|
computeId,
|
|
1028
1030
|
computeTrustScore,
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/registry/capability.ts","../src/registry/peer-store.ts","../src/registry/discovery-service.ts","../src/message/types/peer-discovery.ts","../src/config.ts","../src/relay/ignored-peers.ts","../src/relay/inbound-message-guard.ts","../src/service.ts","../src/reputation/network.ts","../src/reputation/sync.ts"],"sourcesContent":["import { createHash } from 'node:crypto';\r\n\r\n/**\r\n * A capability describes something an agent can do\r\n */\r\nexport interface Capability {\r\n /** Unique ID (content-addressed hash of name + version + schema) */\r\n id: string;\r\n /** Human-readable name: 'code-review', 'summarization', 'translation' */\r\n name: string;\r\n /** Semantic version */\r\n version: string;\r\n /** What the capability does */\r\n description: string;\r\n /** JSON Schema for expected input */\r\n inputSchema?: object;\r\n /** JSON Schema for expected output */\r\n outputSchema?: object;\r\n /** Discovery tags: ['code', 'typescript', 'review'] */\r\n tags: string[];\r\n}\r\n\r\n/**\r\n * Deterministic JSON serialization for capability hashing.\r\n * Recursively sorts object keys.\r\n */\r\nfunction stableStringify(value: unknown): string {\r\n if (value === null || value === undefined) return JSON.stringify(value);\r\n if (typeof value !== 'object') return JSON.stringify(value);\r\n if (Array.isArray(value)) {\r\n return '[' + value.map(stableStringify).join(',') + ']';\r\n }\r\n const keys = Object.keys(value as Record<string, unknown>).sort();\r\n const pairs = keys.map(k => JSON.stringify(k) + ':' + stableStringify((value as Record<string, unknown>)[k]));\r\n return '{' + pairs.join(',') + '}';\r\n}\r\n\r\n/**\r\n * Compute content-addressed ID for a capability based on name, version, and schemas.\r\n */\r\nfunction computeCapabilityId(name: string, version: string, inputSchema?: object, outputSchema?: object): string {\r\n const data = {\r\n name,\r\n version,\r\n ...(inputSchema !== undefined ? { inputSchema } : {}),\r\n ...(outputSchema !== undefined ? { outputSchema } : {}),\r\n };\r\n const canonical = stableStringify(data);\r\n return createHash('sha256').update(canonical).digest('hex');\r\n}\r\n\r\n/**\r\n * Creates a capability with a content-addressed ID.\r\n * \r\n * @param name - Human-readable capability name\r\n * @param version - Semantic version string\r\n * @param description - Description of what the capability does\r\n * @param options - Optional input/output schemas and tags\r\n * @returns A Capability object with computed ID\r\n */\r\nexport function createCapability(\r\n name: string,\r\n version: string,\r\n description: string,\r\n options: {\r\n inputSchema?: object;\r\n outputSchema?: object;\r\n tags?: string[];\r\n } = {}\r\n): Capability {\r\n const { inputSchema, outputSchema, tags = [] } = options;\r\n \r\n const id = computeCapabilityId(name, version, inputSchema, outputSchema);\r\n \r\n return {\r\n id,\r\n name,\r\n version,\r\n description,\r\n ...(inputSchema !== undefined ? { inputSchema } : {}),\r\n ...(outputSchema !== undefined ? { outputSchema } : {}),\r\n tags,\r\n };\r\n}\r\n\r\n/**\r\n * Validates that a capability has all required fields.\r\n * \r\n * @param capability - The capability to validate\r\n * @returns Object with `valid` boolean and optional `errors` array\r\n */\r\nexport function validateCapability(capability: unknown): { valid: boolean; errors?: string[] } {\r\n const errors: string[] = [];\r\n \r\n if (!capability || typeof capability !== 'object') {\r\n return { valid: false, errors: ['Capability must be an object'] };\r\n }\r\n \r\n const cap = capability as Record<string, unknown>;\r\n \r\n if (!cap.id || typeof cap.id !== 'string') {\r\n errors.push('Missing or invalid field: id (must be a string)');\r\n }\r\n \r\n if (!cap.name || typeof cap.name !== 'string') {\r\n errors.push('Missing or invalid field: name (must be a string)');\r\n }\r\n \r\n if (!cap.version || typeof cap.version !== 'string') {\r\n errors.push('Missing or invalid field: version (must be a string)');\r\n }\r\n \r\n if (!cap.description || typeof cap.description !== 'string') {\r\n errors.push('Missing or invalid field: description (must be a string)');\r\n }\r\n \r\n if (!Array.isArray(cap.tags)) {\r\n errors.push('Missing or invalid field: tags (must be an array)');\r\n } else if (!cap.tags.every(tag => typeof tag === 'string')) {\r\n errors.push('Invalid field: tags (all elements must be strings)');\r\n }\r\n \r\n if (cap.inputSchema !== undefined && (typeof cap.inputSchema !== 'object' || cap.inputSchema === null)) {\r\n errors.push('Invalid field: inputSchema (must be an object)');\r\n }\r\n \r\n if (cap.outputSchema !== undefined && (typeof cap.outputSchema !== 'object' || cap.outputSchema === null)) {\r\n errors.push('Invalid field: outputSchema (must be an object)');\r\n }\r\n \r\n if (errors.length > 0) {\r\n return { valid: false, errors };\r\n }\r\n \r\n return { valid: true };\r\n}\r\n","import type { Peer } from './peer';\n\n/**\n * In-memory store for known peers on the network\n */\nexport class PeerStore {\n private peers: Map<string, Peer> = new Map();\n\n /**\n * Add or update a peer in the store.\n * If a peer with the same publicKey exists, it will be updated.\n * \n * @param peer - The peer to add or update\n */\n addOrUpdatePeer(peer: Peer): void {\n this.peers.set(peer.publicKey, peer);\n }\n\n /**\n * Remove a peer from the store.\n * \n * @param publicKey - The public key of the peer to remove\n * @returns true if the peer was removed, false if it didn't exist\n */\n removePeer(publicKey: string): boolean {\n return this.peers.delete(publicKey);\n }\n\n /**\n * Get a peer by their public key.\n * \n * @param publicKey - The public key of the peer to retrieve\n * @returns The peer if found, undefined otherwise\n */\n getPeer(publicKey: string): Peer | undefined {\n return this.peers.get(publicKey);\n }\n\n /**\n * Find all peers that offer a specific capability by name.\n * \n * @param name - The capability name to search for\n * @returns Array of peers that have a capability with the given name\n */\n findByCapability(name: string): Peer[] {\n const result: Peer[] = [];\n \n for (const peer of this.peers.values()) {\n const hasCapability = peer.capabilities.some(cap => cap.name === name);\n if (hasCapability) {\n result.push(peer);\n }\n }\n \n return result;\n }\n\n /**\n * Find all peers that have capabilities with a specific tag.\n * \n * @param tag - The tag to search for\n * @returns Array of peers that have at least one capability with the given tag\n */\n findByTag(tag: string): Peer[] {\n const result: Peer[] = [];\n \n for (const peer of this.peers.values()) {\n const hasTag = peer.capabilities.some(cap => cap.tags.includes(tag));\n if (hasTag) {\n result.push(peer);\n }\n }\n \n return result;\n }\n\n /**\n * Get all peers in the store.\n * \n * @returns Array of all peers\n */\n allPeers(): Peer[] {\n return Array.from(this.peers.values());\n }\n\n /**\n * Remove peers that haven't been seen within the specified time window.\n * \n * @param maxAgeMs - Maximum age in milliseconds. Peers older than this will be removed.\n * @param currentTime - Current timestamp (ms), defaults to Date.now()\n * @returns Number of peers removed\n */\n prune(maxAgeMs: number, currentTime: number = Date.now()): number {\n const cutoff = currentTime - maxAgeMs;\n let removed = 0;\n \n for (const [publicKey, peer] of this.peers.entries()) {\n if (peer.lastSeen < cutoff) {\n this.peers.delete(publicKey);\n removed++;\n }\n }\n \n return removed;\n }\n}\n","import { createEnvelope, type Envelope } from '../message/envelope';\nimport { PeerStore } from './peer-store';\nimport type { Capability } from './capability';\nimport type { Peer } from './peer';\nimport type {\n CapabilityAnnouncePayload,\n CapabilityQueryPayload,\n CapabilityResponsePayload,\n} from './messages';\n\n/**\n * DiscoveryService manages capability-based peer discovery.\n * It maintains a local index of peer capabilities and handles\n * capability announce, query, and response messages.\n */\nexport class DiscoveryService {\n constructor(\n private peerStore: PeerStore,\n private identity: { publicKey: string; privateKey: string }\n ) {}\n\n /**\n * Announce own capabilities to the network.\n * Creates a capability_announce envelope that can be broadcast to peers.\n * \n * @param capabilities - List of capabilities this agent offers\n * @param metadata - Optional metadata about this agent\n * @returns A signed capability_announce envelope\n */\n announce(\n capabilities: Capability[],\n metadata?: { name?: string; version?: string },\n recipients: string[] = [this.identity.publicKey]\n ): Envelope<CapabilityAnnouncePayload> {\n const payload: CapabilityAnnouncePayload = {\n publicKey: this.identity.publicKey,\n capabilities,\n metadata: metadata ? {\n ...metadata,\n lastSeen: Date.now(),\n } : {\n lastSeen: Date.now(),\n },\n };\n\n return createEnvelope(\n 'capability_announce',\n this.identity.publicKey,\n this.identity.privateKey,\n payload,\n Date.now(),\n undefined,\n recipients\n );\n }\n\n /**\n * Handle an incoming capability_announce message.\n * Updates the peer store with the announced capabilities.\n * \n * @param envelope - The capability_announce envelope to process\n */\n handleAnnounce(envelope: Envelope<CapabilityAnnouncePayload>): void {\n const { payload } = envelope;\n \n const peer: Peer = {\n publicKey: payload.publicKey,\n capabilities: payload.capabilities,\n lastSeen: payload.metadata?.lastSeen || envelope.timestamp,\n metadata: payload.metadata ? {\n name: payload.metadata.name,\n version: payload.metadata.version,\n } : undefined,\n };\n\n this.peerStore.addOrUpdatePeer(peer);\n }\n\n /**\n * Create a capability query payload.\n * \n * @param queryType - Type of query: 'name', 'tag', or 'schema'\n * @param query - The query value (capability name, tag, or schema)\n * @param filters - Optional filters (limit, minTrustScore)\n * @returns A capability_query payload\n */\n query(\n queryType: 'name' | 'tag' | 'schema',\n query: string | object,\n filters?: { limit?: number; minTrustScore?: number }\n ): CapabilityQueryPayload {\n return {\n queryType,\n query,\n filters,\n };\n }\n\n /**\n * Handle an incoming capability_query message.\n * Searches the local peer store and returns matching peers.\n * \n * @param envelope - The capability_query envelope to process\n * @returns A capability_response envelope with matching peers\n */\n handleQuery(\n envelope: Envelope<CapabilityQueryPayload>\n ): Envelope<CapabilityResponsePayload> {\n const { payload } = envelope;\n let peers: Peer[] = [];\n\n // Execute query based on type\n if (payload.queryType === 'name' && typeof payload.query === 'string') {\n peers = this.peerStore.findByCapability(payload.query);\n } else if (payload.queryType === 'tag' && typeof payload.query === 'string') {\n peers = this.peerStore.findByTag(payload.query);\n } else if (payload.queryType === 'schema') {\n // Schema-based matching is deferred to Phase 2b\n // For now, return empty results\n peers = [];\n }\n\n // Apply filters\n const limit = payload.filters?.limit;\n const totalMatches = peers.length;\n \n if (limit !== undefined && limit > 0) {\n peers = peers.slice(0, limit);\n }\n\n // Transform peers to response format\n const responsePeers = peers.map(peer => ({\n publicKey: peer.publicKey,\n capabilities: peer.capabilities,\n metadata: peer.metadata ? {\n name: peer.metadata.name,\n version: peer.metadata.version,\n lastSeen: peer.lastSeen,\n } : {\n lastSeen: peer.lastSeen,\n },\n // Trust score integration deferred to Phase 2b (RFC-001)\n trustScore: undefined,\n }));\n\n const responsePayload: CapabilityResponsePayload = {\n queryId: envelope.id,\n peers: responsePeers,\n totalMatches,\n };\n\n return createEnvelope(\n 'capability_response',\n this.identity.publicKey,\n this.identity.privateKey,\n responsePayload,\n Date.now(),\n envelope.id, // inReplyTo\n [envelope.from]\n );\n }\n\n /**\n * Remove peers that haven't been seen within the specified time window.\n * \n * @param maxAgeMs - Maximum age in milliseconds\n * @returns Number of peers removed\n */\n pruneStale(maxAgeMs: number, currentTime: number = Date.now()): number {\n return this.peerStore.prune(maxAgeMs, currentTime);\n }\n}\n","/**\r\n * Peer discovery message types for the Agora network.\r\n */\r\n\r\n/**\r\n * Request peer list from relay\r\n */\r\nexport interface PeerListRequestPayload {\r\n /** Optional filters */\r\n filters?: {\r\n /** Only peers seen in last N ms */\r\n activeWithin?: number;\r\n /** Maximum peers to return */\r\n limit?: number;\r\n };\r\n}\r\n\r\n/**\r\n * Relay responds with connected peers\r\n */\r\nexport interface PeerListResponsePayload {\r\n /** List of known peers */\r\n peers: Array<{\r\n /** Peer's Ed25519 public key */\r\n publicKey: string;\r\n /** Optional metadata (if peer announced) */\r\n metadata?: {\r\n name?: string;\r\n version?: string;\r\n capabilities?: string[];\r\n };\r\n /** Last seen timestamp (ms) */\r\n lastSeen: number;\r\n }>;\r\n /** Total peer count (may be > peers.length if limited) */\r\n totalPeers: number;\r\n /** Relay's public key (for trust verification) */\r\n relayPublicKey: string;\r\n}\r\n\r\n/**\r\n * Agent recommends another agent\r\n */\r\nexport interface PeerReferralPayload {\r\n /** Referred peer's public key */\r\n publicKey: string;\r\n /** Optional endpoint (if known) */\r\n endpoint?: string;\r\n /** Optional metadata */\r\n metadata?: {\r\n name?: string;\r\n version?: string;\r\n capabilities?: string[];\r\n };\r\n /** Referrer's comment */\r\n comment?: string;\r\n /** Trust hint (RFC-001 integration) */\r\n trustScore?: number;\r\n}\r\n\r\n/**\r\n * Validate PeerListRequestPayload\r\n */\r\nexport function validatePeerListRequest(payload: unknown): { valid: boolean; errors: string[] } {\r\n const errors: string[] = [];\r\n\r\n if (typeof payload !== 'object' || payload === null) {\r\n errors.push('Payload must be an object');\r\n return { valid: false, errors };\r\n }\r\n\r\n const p = payload as Record<string, unknown>;\r\n\r\n if (p.filters !== undefined) {\r\n if (typeof p.filters !== 'object' || p.filters === null) {\r\n errors.push('filters must be an object');\r\n } else {\r\n const filters = p.filters as Record<string, unknown>;\r\n if (filters.activeWithin !== undefined && typeof filters.activeWithin !== 'number') {\r\n errors.push('filters.activeWithin must be a number');\r\n }\r\n if (filters.limit !== undefined && typeof filters.limit !== 'number') {\r\n errors.push('filters.limit must be a number');\r\n }\r\n }\r\n }\r\n\r\n return { valid: errors.length === 0, errors };\r\n}\r\n\r\n/**\r\n * Validate PeerListResponsePayload\r\n */\r\nexport function validatePeerListResponse(payload: unknown): { valid: boolean; errors: string[] } {\r\n const errors: string[] = [];\r\n\r\n if (typeof payload !== 'object' || payload === null) {\r\n errors.push('Payload must be an object');\r\n return { valid: false, errors };\r\n }\r\n\r\n const p = payload as Record<string, unknown>;\r\n\r\n if (!Array.isArray(p.peers)) {\r\n errors.push('peers must be an array');\r\n } else {\r\n p.peers.forEach((peer, index) => {\r\n if (typeof peer !== 'object' || peer === null) {\r\n errors.push(`peers[${index}] must be an object`);\r\n return;\r\n }\r\n const peerObj = peer as Record<string, unknown>;\r\n if (typeof peerObj.publicKey !== 'string') {\r\n errors.push(`peers[${index}].publicKey must be a string`);\r\n }\r\n if (typeof peerObj.lastSeen !== 'number') {\r\n errors.push(`peers[${index}].lastSeen must be a number`);\r\n }\r\n });\r\n }\r\n\r\n if (typeof p.totalPeers !== 'number') {\r\n errors.push('totalPeers must be a number');\r\n }\r\n\r\n if (typeof p.relayPublicKey !== 'string') {\r\n errors.push('relayPublicKey must be a string');\r\n }\r\n\r\n return { valid: errors.length === 0, errors };\r\n}\r\n\r\n/**\r\n * Validate PeerReferralPayload\r\n */\r\nexport function validatePeerReferral(payload: unknown): { valid: boolean; errors: string[] } {\r\n const errors: string[] = [];\r\n\r\n if (typeof payload !== 'object' || payload === null) {\r\n errors.push('Payload must be an object');\r\n return { valid: false, errors };\r\n }\r\n\r\n const p = payload as Record<string, unknown>;\r\n\r\n if (typeof p.publicKey !== 'string') {\r\n errors.push('publicKey must be a string');\r\n }\r\n\r\n if (p.endpoint !== undefined && typeof p.endpoint !== 'string') {\r\n errors.push('endpoint must be a string');\r\n }\r\n\r\n if (p.comment !== undefined && typeof p.comment !== 'string') {\r\n errors.push('comment must be a string');\r\n }\r\n\r\n if (p.trustScore !== undefined && typeof p.trustScore !== 'number') {\r\n errors.push('trustScore must be a number');\r\n }\r\n\r\n return { valid: errors.length === 0, errors };\r\n}\r\n","import { readFileSync, existsSync } from 'node:fs';\r\nimport { readFile } from 'node:fs/promises';\r\nimport { resolve } from 'node:path';\r\nimport { homedir } from 'node:os';\r\n\r\n/**\r\n * Normalized relay configuration (supports both string and object in config file).\r\n */\r\nexport interface RelayConfig {\r\n url: string;\r\n autoConnect: boolean;\r\n name?: string;\r\n reconnectMaxMs?: number;\r\n}\r\n\r\n/**\r\n * Peer entry in config (webhook URL, token, public key).\r\n */\r\nexport interface AgoraPeerConfig {\r\n publicKey: string;\r\n /** Webhook URL (undefined for relay-only peers) */\r\n url?: string;\r\n /** Webhook auth token (undefined for relay-only peers) */\r\n token?: string;\r\n name?: string;\r\n}\r\n\r\n/**\r\n * Identity with optional display name (e.g. for relay registration).\r\n */\r\nexport interface AgoraIdentity {\r\n publicKey: string;\r\n privateKey: string;\r\n name?: string;\r\n}\r\n\r\n/**\r\n * Canonical Agora configuration shape.\r\n * Use loadAgoraConfig() to load from file with normalized relay.\r\n */\r\nexport interface AgoraConfig {\r\n identity: AgoraIdentity;\r\n peers: Record<string, AgoraPeerConfig>;\r\n relay?: RelayConfig;\r\n}\r\n\r\n/**\r\n * Default config file path: AGORA_CONFIG env or ~/.config/agora/config.json\r\n */\r\nexport function getDefaultConfigPath(): string {\r\n if (process.env.AGORA_CONFIG) {\r\n return resolve(process.env.AGORA_CONFIG);\r\n }\r\n return resolve(homedir(), '.config', 'agora', 'config.json');\r\n}\r\n\r\n/**\r\n * Parse and normalize config from a JSON object (shared by sync and async loaders).\r\n */\r\nfunction parseConfig(config: Record<string, unknown>): AgoraConfig {\r\n const rawIdentity = config.identity as Record<string, unknown> | undefined;\r\n if (!rawIdentity?.publicKey || !rawIdentity?.privateKey) {\r\n throw new Error('Invalid config: missing identity.publicKey or identity.privateKey');\r\n }\r\n const identity: AgoraIdentity = {\r\n publicKey: rawIdentity.publicKey as string,\r\n privateKey: rawIdentity.privateKey as string,\r\n name: typeof rawIdentity.name === 'string' ? rawIdentity.name : undefined,\r\n };\r\n\r\n const peers: Record<string, AgoraPeerConfig> = {};\r\n if (config.peers && typeof config.peers === 'object') {\r\n for (const entry of Object.values(config.peers)) {\r\n const peer = entry as Record<string, unknown>;\r\n if (peer && typeof peer.publicKey === 'string') {\r\n peers[peer.publicKey as string] = {\r\n publicKey: peer.publicKey as string,\r\n url: typeof peer.url === 'string' ? peer.url : undefined,\r\n token: typeof peer.token === 'string' ? peer.token : undefined,\r\n name: typeof peer.name === 'string' ? peer.name : undefined,\r\n };\r\n }\r\n }\r\n }\r\n\r\n let relay: RelayConfig | undefined;\r\n const rawRelay = config.relay;\r\n if (typeof rawRelay === 'string') {\r\n relay = { url: rawRelay, autoConnect: true };\r\n } else if (rawRelay && typeof rawRelay === 'object') {\r\n const r = rawRelay as Record<string, unknown>;\r\n if (typeof r.url === 'string') {\r\n relay = {\r\n url: r.url,\r\n autoConnect: typeof r.autoConnect === 'boolean' ? r.autoConnect : true,\r\n name: typeof r.name === 'string' ? r.name : undefined,\r\n reconnectMaxMs: typeof r.reconnectMaxMs === 'number' ? r.reconnectMaxMs : undefined,\r\n };\r\n }\r\n }\r\n\r\n return {\r\n identity,\r\n peers,\r\n ...(relay ? { relay } : {}),\r\n };\r\n}\r\n\r\n/**\r\n * Load and normalize Agora configuration from a JSON file (sync).\r\n * Supports relay as string (backward compat) or object { url?, autoConnect?, name?, reconnectMaxMs? }.\r\n *\r\n * @param path - Config file path; defaults to getDefaultConfigPath()\r\n * @returns Normalized AgoraConfig\r\n * @throws Error if file doesn't exist or config is invalid\r\n */\r\nexport function loadAgoraConfig(path?: string): AgoraConfig {\r\n const configPath = path ?? getDefaultConfigPath();\r\n\r\n if (!existsSync(configPath)) {\r\n throw new Error(`Config file not found at ${configPath}. Run 'npx @rookdaemon/agora init' first.`);\r\n }\r\n\r\n const content = readFileSync(configPath, 'utf-8');\r\n let config: Record<string, unknown>;\r\n try {\r\n config = JSON.parse(content) as Record<string, unknown>;\r\n } catch {\r\n throw new Error(`Invalid JSON in config file: ${configPath}`);\r\n }\r\n\r\n return parseConfig(config);\r\n}\r\n\r\n/**\r\n * Load and normalize Agora configuration from a JSON file (async).\r\n *\r\n * @param path - Config file path; defaults to getDefaultConfigPath()\r\n * @returns Normalized AgoraConfig\r\n * @throws Error if file doesn't exist or config is invalid\r\n */\r\nexport async function loadAgoraConfigAsync(path?: string): Promise<AgoraConfig> {\r\n const configPath = path ?? getDefaultConfigPath();\r\n\r\n let content: string;\r\n try {\r\n content = await readFile(configPath, 'utf-8');\r\n } catch (err) {\r\n const code = err && typeof err === 'object' && 'code' in err ? (err as NodeJS.ErrnoException).code : undefined;\r\n if (code === 'ENOENT') {\r\n throw new Error(`Config file not found at ${configPath}. Run 'npx @rookdaemon/agora init' first.`);\r\n }\r\n throw err;\r\n }\r\n\r\n let config: Record<string, unknown>;\r\n try {\r\n config = JSON.parse(content) as Record<string, unknown>;\r\n } catch {\r\n throw new Error(`Invalid JSON in config file: ${configPath}`);\r\n }\r\n\r\n return parseConfig(config);\r\n}\r\n","import { readFileSync, writeFileSync, existsSync, mkdirSync } from 'node:fs';\r\nimport { join, dirname } from 'node:path';\r\nimport { getDefaultConfigPath } from '../config';\r\n\r\nexport const IGNORED_FILE_NAME = 'IGNORED_PEERS.md';\r\n\r\nexport function getIgnoredPeersPath(storageDir?: string): string {\r\n if (storageDir) {\r\n return join(storageDir, IGNORED_FILE_NAME);\r\n }\r\n const configPath = getDefaultConfigPath();\r\n return join(dirname(configPath), IGNORED_FILE_NAME);\r\n}\r\n\r\nexport function loadIgnoredPeers(filePath?: string): string[] {\r\n const path = filePath ?? getIgnoredPeersPath();\r\n if (!existsSync(path)) return [];\r\n\r\n const lines = readFileSync(path, 'utf-8')\r\n .split('\\n')\r\n .map((line) => line.trim())\r\n .filter((line) => line.length > 0 && !line.startsWith('#'));\r\n\r\n return Array.from(new Set(lines));\r\n}\r\n\r\nexport function saveIgnoredPeers(peers: string[], filePath?: string): void {\r\n const path = filePath ?? getIgnoredPeersPath();\r\n const dir = dirname(path);\r\n if (!existsSync(dir)) {\r\n mkdirSync(dir, { recursive: true });\r\n }\r\n\r\n const unique = Array.from(new Set(peers.map((peer) => peer.trim()).filter(Boolean))).sort();\r\n const content = [\r\n '# Ignored peers',\r\n '# One public key per line',\r\n ...unique,\r\n '',\r\n ].join('\\n');\r\n\r\n writeFileSync(path, content, 'utf-8');\r\n}\r\n\r\nexport class IgnoredPeersManager {\r\n private readonly peers: Set<string>;\r\n private readonly filePath: string;\r\n\r\n constructor(filePath?: string) {\r\n this.filePath = filePath ?? getIgnoredPeersPath();\r\n this.peers = new Set(loadIgnoredPeers(this.filePath));\r\n }\r\n\r\n ignorePeer(publicKey: string): boolean {\r\n const normalized = publicKey.trim();\r\n if (!normalized) {\r\n return false;\r\n }\r\n const added = !this.peers.has(normalized);\r\n this.peers.add(normalized);\r\n if (added) {\r\n this.persist();\r\n }\r\n return added;\r\n }\r\n\r\n unignorePeer(publicKey: string): boolean {\r\n const normalized = publicKey.trim();\r\n const removed = this.peers.delete(normalized);\r\n if (removed) {\r\n this.persist();\r\n }\r\n return removed;\r\n }\r\n\r\n listIgnoredPeers(): string[] {\r\n return Array.from(this.peers.values()).sort();\r\n }\r\n\r\n private persist(): void {\r\n saveIgnoredPeers(this.listIgnoredPeers(), this.filePath);\r\n }\r\n}\r\n","import { createHash } from 'node:crypto';\r\nimport type { Envelope } from '../message/envelope';\r\n\r\ninterface SenderWindow {\r\n count: number;\r\n windowStart: number;\r\n}\r\n\r\nexport interface InboundSecurityOptions {\r\n rateLimitEnabled?: boolean;\r\n rateLimitMaxMessages?: number;\r\n rateLimitWindowMs?: number;\r\n envelopeDedupEnabled?: boolean;\r\n envelopeDedupMaxIds?: number;\r\n contentDedupEnabled?: boolean;\r\n contentDedupWindowMs?: number;\r\n ignoredPeers?: string[];\r\n}\r\n\r\n/**\r\n * Shared inbound guard for relay-fed consumers.\r\n *\r\n * Provides:\r\n * - static ignore list checks\r\n * - per-sender rate limiting\r\n * - envelope ID deduplication\r\n * - content-window deduplication\r\n */\r\nexport class InboundMessageGuard {\r\n private readonly senderWindows = new Map<string, SenderWindow>();\r\n private readonly envelopeIds = new Set<string>();\r\n private readonly contentDedup = new Map<string, number>();\r\n private readonly ignoredPeers = new Set<string>();\r\n\r\n private rateLimitEnabled: boolean;\r\n private rateLimitMaxMessages: number;\r\n private rateLimitWindowMs: number;\r\n private envelopeDedupEnabled: boolean;\r\n private envelopeDedupMaxIds: number;\r\n private contentDedupEnabled: boolean;\r\n private contentDedupWindowMs: number;\r\n\r\n private static readonly MAX_SENDER_ENTRIES = 500;\r\n private static readonly MAX_CONTENT_ENTRIES = 5000;\r\n\r\n constructor(options: InboundSecurityOptions = {}) {\r\n this.rateLimitEnabled = options.rateLimitEnabled ?? true;\r\n this.rateLimitMaxMessages = options.rateLimitMaxMessages ?? 10;\r\n this.rateLimitWindowMs = options.rateLimitWindowMs ?? 60_000;\r\n this.envelopeDedupEnabled = options.envelopeDedupEnabled ?? true;\r\n this.envelopeDedupMaxIds = options.envelopeDedupMaxIds ?? 1000;\r\n this.contentDedupEnabled = options.contentDedupEnabled ?? true;\r\n this.contentDedupWindowMs = options.contentDedupWindowMs ?? 1_800_000;\r\n\r\n for (const peer of options.ignoredPeers ?? []) {\r\n const normalized = peer.trim();\r\n if (normalized) {\r\n this.ignoredPeers.add(normalized);\r\n }\r\n }\r\n }\r\n\r\n shouldDrop(envelope: Envelope, senderPublicKey: string): { drop: boolean; reason?: string } {\r\n if (this.ignoredPeers.has(senderPublicKey)) {\r\n return { drop: true, reason: 'ignored_peer' };\r\n }\r\n\r\n if (this.isRateLimited(senderPublicKey)) {\r\n return { drop: true, reason: 'rate_limited' };\r\n }\r\n\r\n if (this.isDuplicateEnvelopeId(envelope.id)) {\r\n return { drop: true, reason: 'duplicate_envelope_id' };\r\n }\r\n\r\n if (this.isDuplicateContent(senderPublicKey, envelope.type, envelope.payload)) {\r\n return { drop: true, reason: 'duplicate_content' };\r\n }\r\n\r\n return { drop: false };\r\n }\r\n\r\n ignorePeer(publicKey: string): boolean {\r\n const normalized = publicKey.trim();\r\n if (!normalized) {\r\n return false;\r\n }\r\n const existed = this.ignoredPeers.has(normalized);\r\n this.ignoredPeers.add(normalized);\r\n return !existed;\r\n }\r\n\r\n unignorePeer(publicKey: string): boolean {\r\n return this.ignoredPeers.delete(publicKey.trim());\r\n }\r\n\r\n listIgnoredPeers(): string[] {\r\n return Array.from(this.ignoredPeers.values()).sort();\r\n }\r\n\r\n private isRateLimited(senderPublicKey: string): boolean {\r\n if (!this.rateLimitEnabled) {\r\n return false;\r\n }\r\n\r\n const now = Date.now();\r\n const current = this.senderWindows.get(senderPublicKey);\r\n\r\n if (!current && this.senderWindows.size >= InboundMessageGuard.MAX_SENDER_ENTRIES) {\r\n this.evictOldestSender();\r\n }\r\n\r\n if (!current || now - current.windowStart > this.rateLimitWindowMs) {\r\n this.senderWindows.set(senderPublicKey, { count: 1, windowStart: now });\r\n return false;\r\n }\r\n\r\n current.count += 1;\r\n return current.count > this.rateLimitMaxMessages;\r\n }\r\n\r\n private evictOldestSender(): void {\r\n let oldestKey: string | null = null;\r\n let oldestTime = Number.POSITIVE_INFINITY;\r\n\r\n for (const [key, value] of this.senderWindows.entries()) {\r\n if (value.windowStart < oldestTime) {\r\n oldestTime = value.windowStart;\r\n oldestKey = key;\r\n }\r\n }\r\n\r\n if (oldestKey) {\r\n this.senderWindows.delete(oldestKey);\r\n }\r\n }\r\n\r\n private isDuplicateEnvelopeId(envelopeId: string): boolean {\r\n if (!this.envelopeDedupEnabled) {\r\n return false;\r\n }\r\n\r\n if (this.envelopeIds.has(envelopeId)) {\r\n return true;\r\n }\r\n\r\n this.envelopeIds.add(envelopeId);\r\n if (this.envelopeIds.size > this.envelopeDedupMaxIds) {\r\n const oldest = this.envelopeIds.values().next().value;\r\n if (oldest !== undefined) {\r\n this.envelopeIds.delete(oldest);\r\n }\r\n }\r\n\r\n return false;\r\n }\r\n\r\n private isDuplicateContent(senderPublicKey: string, type: string, payload: unknown): boolean {\r\n if (!this.contentDedupEnabled) {\r\n return false;\r\n }\r\n\r\n const hash = createHash('sha256')\r\n .update(senderPublicKey)\r\n .update(type)\r\n .update(JSON.stringify(payload ?? null))\r\n .digest('hex');\r\n\r\n const now = Date.now();\r\n const firstSeen = this.contentDedup.get(hash);\r\n\r\n if (firstSeen !== undefined && now - firstSeen < this.contentDedupWindowMs) {\r\n return true;\r\n }\r\n\r\n this.contentDedup.set(hash, now);\r\n\r\n if (this.contentDedup.size > InboundMessageGuard.MAX_CONTENT_ENTRIES) {\r\n for (const [key, ts] of this.contentDedup.entries()) {\r\n if (now - ts >= this.contentDedupWindowMs) {\r\n this.contentDedup.delete(key);\r\n }\r\n }\r\n if (this.contentDedup.size > InboundMessageGuard.MAX_CONTENT_ENTRIES) {\r\n const oldestKey = this.contentDedup.keys().next().value;\r\n if (oldestKey !== undefined) {\r\n this.contentDedup.delete(oldestKey);\r\n }\r\n }\r\n }\r\n\r\n return false;\r\n }\r\n}\r\n","import type { AgoraIdentity, RelayConfig } from './config';\r\nimport { getDefaultConfigPath, loadAgoraConfigAsync } from './config';\r\nimport type { Envelope } from './message/envelope';\r\nimport type { MessageType } from './message/envelope';\r\nimport { RelayClient } from './relay/client';\r\nimport type { PeerConfig } from './transport/http';\r\nimport { decodeInboundEnvelope, sendToPeer } from './transport/http';\r\nimport { sendViaRelay } from './transport/relay';\r\nimport { shortKey } from './utils';\r\n\r\n/**\r\n * Service config: identity, peers keyed by name, optional relay.\r\n */\r\nexport interface AgoraServiceConfig {\r\n identity: AgoraIdentity;\r\n peers: Map<string, PeerConfig>;\r\n relay?: RelayConfig;\r\n}\r\n\r\nexport interface SendMessageOptions {\r\n peerName: string;\r\n type: MessageType;\r\n payload: unknown;\r\n inReplyTo?: string;\r\n /** Skip relay, send directly via HTTP only. Fails if peer has no URL or is unreachable. */\r\n direct?: boolean;\r\n /** Skip direct HTTP, always use relay even if peer has a URL. */\r\n relayOnly?: boolean;\r\n}\r\n\r\nexport interface SendMessageResult {\r\n ok: boolean;\r\n status: number;\r\n error?: string;\r\n}\r\n\r\nexport interface ReplyToEnvelopeOptions {\r\n /** The public key of the target (from envelope.sender) */\r\n targetPubkey: string;\r\n /** Message type for the reply */\r\n type: MessageType;\r\n /** Reply payload */\r\n payload: unknown;\r\n /** The envelope ID being replied to (required — this IS a reply) */\r\n inReplyTo: string;\r\n}\r\n\r\nexport interface DecodeInboundResult {\r\n ok: boolean;\r\n envelope?: Envelope;\r\n reason?: string;\r\n}\r\n\r\n/** Handler for relay messages. (envelope, fromPublicKey, fromName?) */\r\nexport type RelayMessageHandlerWithName = (envelope: Envelope, from: string, fromName?: string) => void;\r\n\r\n/** @deprecated Use RelayMessageHandlerWithName. Kept for backward compatibility. */\r\nexport type RelayMessageHandler = (envelope: Envelope) => void;\r\n\r\nexport interface Logger {\r\n debug(message: string): void;\r\n}\r\n\r\nexport interface RelayClientLike {\r\n connect(): Promise<void>;\r\n disconnect(): void;\r\n connected(): boolean;\r\n send(to: string, envelope: Envelope): Promise<{ ok: boolean; error?: string }>;\r\n on(event: 'message', handler: (envelope: Envelope, from: string, fromName?: string) => void): void;\r\n on(event: 'error', handler: (error: Error) => void): void;\r\n}\r\n\r\nexport interface RelayClientFactory {\r\n (opts: {\r\n relayUrl: string;\r\n publicKey: string;\r\n privateKey: string;\r\n name?: string;\r\n pingInterval: number;\r\n maxReconnectDelay: number;\r\n }): RelayClientLike;\r\n}\r\n\r\n/**\r\n * High-level Agora service: send by peer name, decode inbound, relay lifecycle.\r\n */\r\nexport class AgoraService {\r\n private config: AgoraServiceConfig;\r\n private relayClient: RelayClientLike | null = null;\r\n private readonly onRelayMessage: RelayMessageHandlerWithName;\r\n private logger: Logger | null;\r\n private relayClientFactory: RelayClientFactory | null;\r\n\r\n /**\r\n * @param config - Service config (identity, peers, optional relay)\r\n * @param onRelayMessage - Required callback for relay messages. Ensures no messages are lost between init and connect.\r\n * @param logger - Optional debug logger\r\n * @param relayClientFactory - Optional factory for relay client (for testing)\r\n */\r\n constructor(\r\n config: AgoraServiceConfig,\r\n onRelayMessage: RelayMessageHandlerWithName,\r\n logger?: Logger,\r\n relayClientFactory?: RelayClientFactory\r\n ) {\r\n this.config = config;\r\n this.onRelayMessage = onRelayMessage;\r\n this.logger = logger ?? null;\r\n this.relayClientFactory = relayClientFactory ?? null;\r\n }\r\n\r\n private resolvePeer(identifier: string): PeerConfig | undefined {\r\n const direct = this.config.peers.get(identifier);\r\n if (direct) {\r\n return direct;\r\n }\r\n\r\n for (const peer of this.config.peers.values()) {\r\n if (peer.publicKey === identifier || peer.name === identifier) {\r\n return peer;\r\n }\r\n }\r\n\r\n return undefined;\r\n }\r\n\r\n /**\r\n * Send a signed message to a named peer.\r\n * Tries HTTP webhook first; falls back to relay if HTTP is unavailable.\r\n */\r\n async sendMessage(options: SendMessageOptions): Promise<SendMessageResult> {\r\n const peer = this.resolvePeer(options.peerName);\r\n if (!peer) {\r\n return {\r\n ok: false,\r\n status: 0,\r\n error: `Unknown peer: ${options.peerName}`,\r\n };\r\n }\r\n\r\n // Try HTTP first (only if peer has a webhook URL and --relay-only not set)\r\n if (peer.url && !options.relayOnly) {\r\n const transportConfig = {\r\n identity: {\r\n publicKey: this.config.identity.publicKey,\r\n privateKey: this.config.identity.privateKey,\r\n },\r\n peers: new Map<string, PeerConfig>([[peer.publicKey, peer]]),\r\n };\r\n\r\n const httpResult = await sendToPeer(\r\n transportConfig,\r\n peer.publicKey,\r\n options.type,\r\n options.payload,\r\n options.inReplyTo\r\n );\r\n\r\n if (httpResult.ok) {\r\n return httpResult;\r\n }\r\n\r\n this.logger?.debug(`HTTP send to ${options.peerName} failed: ${httpResult.error}`);\r\n\r\n // --direct flag: do not fall back to relay\r\n if (options.direct) {\r\n return {\r\n ok: false,\r\n status: httpResult.status,\r\n error: `Direct send to ${options.peerName} failed: ${httpResult.error}`,\r\n };\r\n }\r\n } else if (options.direct && !peer.url) {\r\n // --direct requested but peer has no URL configured\r\n return {\r\n ok: false,\r\n status: 0,\r\n error: `Direct send failed: peer '${options.peerName}' has no URL configured`,\r\n };\r\n }\r\n\r\n // Fall back to relay\r\n if (this.relayClient?.connected() && this.config.relay) {\r\n const relayResult = await sendViaRelay(\r\n {\r\n identity: this.config.identity,\r\n relayUrl: this.config.relay.url,\r\n relayClient: this.relayClient,\r\n },\r\n peer.publicKey,\r\n options.type,\r\n options.payload,\r\n options.inReplyTo\r\n );\r\n\r\n return {\r\n ok: relayResult.ok,\r\n status: 0,\r\n error: relayResult.error,\r\n };\r\n }\r\n\r\n // Both failed\r\n return {\r\n ok: false,\r\n status: 0,\r\n error: peer.url\r\n ? `HTTP send failed and relay not available for peer: ${options.peerName}`\r\n : `No webhook URL and relay not available for peer: ${options.peerName}`,\r\n };\r\n }\r\n\r\n /**\r\n * Reply to an envelope from any sender via relay.\r\n * Unlike sendMessage(), this does NOT require the target to be a configured peer.\r\n * Uses the target's public key directly — relay-only (no HTTP, since unknown peers have no URL).\r\n */\r\n async replyToEnvelope(options: ReplyToEnvelopeOptions): Promise<SendMessageResult> {\r\n if (!this.relayClient?.connected() || !this.config.relay) {\r\n return {\r\n ok: false,\r\n status: 0,\r\n error: 'Relay not connected — cannot reply to envelope without relay',\r\n };\r\n }\r\n\r\n this.logger?.debug(\r\n `Replying to envelope via relay: target=${shortKey(options.targetPubkey)} type=${options.type} inReplyTo=${options.inReplyTo}`\r\n );\r\n\r\n const relayResult = await sendViaRelay(\r\n {\r\n identity: this.config.identity,\r\n relayUrl: this.config.relay.url,\r\n relayClient: this.relayClient,\r\n },\r\n options.targetPubkey,\r\n options.type,\r\n options.payload,\r\n options.inReplyTo\r\n );\r\n\r\n return {\r\n ok: relayResult.ok,\r\n status: 0,\r\n error: relayResult.error,\r\n };\r\n }\r\n\r\n /**\r\n * Decode and verify an inbound envelope from a webhook message.\r\n */\r\n async decodeInbound(message: string): Promise<DecodeInboundResult> {\r\n const peersByPubKey = new Map<string, PeerConfig>();\r\n for (const peer of this.config.peers.values()) {\r\n peersByPubKey.set(peer.publicKey, peer);\r\n }\r\n const result = decodeInboundEnvelope(message, peersByPubKey);\r\n if (result.ok) {\r\n return { ok: true, envelope: result.envelope };\r\n }\r\n return { ok: false, reason: result.reason };\r\n }\r\n\r\n getPeers(): string[] {\r\n return Array.from(this.config.peers.keys());\r\n }\r\n\r\n getPeerConfig(name: string): PeerConfig | undefined {\r\n return this.resolvePeer(name);\r\n }\r\n\r\n /**\r\n * Connect to the relay server.\r\n */\r\n async connectRelay(url: string): Promise<void> {\r\n if (this.relayClient) {\r\n return;\r\n }\r\n\r\n const maxReconnectDelay = this.config.relay?.reconnectMaxMs ?? 300000;\r\n let name = this.config.identity.name ?? this.config.relay?.name;\r\n // Never use the short key (id) as the relay display name; treat it as no name\r\n if (name && name === shortKey(this.config.identity.publicKey)) {\r\n name = undefined;\r\n }\r\n const opts = {\r\n relayUrl: url,\r\n publicKey: this.config.identity.publicKey,\r\n privateKey: this.config.identity.privateKey,\r\n name,\r\n pingInterval: 30000,\r\n maxReconnectDelay,\r\n };\r\n\r\n if (this.relayClientFactory) {\r\n this.relayClient = this.relayClientFactory(opts);\r\n } else {\r\n this.relayClient = new RelayClient(opts);\r\n }\r\n\r\n this.relayClient.on('error', (error: Error) => {\r\n this.logger?.debug(`Agora relay error: ${error.message}`);\r\n });\r\n\r\n this.relayClient.on('message', (envelope: Envelope, from: string, fromName?: string) => {\r\n this.onRelayMessage(envelope, from, fromName);\r\n });\r\n\r\n try {\r\n await this.relayClient.connect();\r\n } catch (error) {\r\n const message = error instanceof Error ? error.message : String(error);\r\n this.logger?.debug(`Agora relay connect failed (${url}): ${message}`);\r\n this.relayClient = null;\r\n }\r\n }\r\n\r\n async disconnectRelay(): Promise<void> {\r\n if (this.relayClient) {\r\n this.relayClient.disconnect();\r\n this.relayClient = null;\r\n }\r\n }\r\n\r\n isRelayConnected(): boolean {\r\n return this.relayClient?.connected() ?? false;\r\n }\r\n\r\n /**\r\n * Load Agora configuration and return service config (peers as Map).\r\n */\r\n static async loadConfig(path?: string): Promise<AgoraServiceConfig> {\r\n const configPath = path ?? getDefaultConfigPath();\r\n const loaded = await loadAgoraConfigAsync(configPath);\r\n\r\n const peers = new Map<string, PeerConfig>();\r\n for (const p of Object.values(loaded.peers)) {\r\n peers.set(p.publicKey, {\r\n publicKey: p.publicKey,\r\n url: p.url,\r\n token: p.token,\r\n name: p.name,\r\n } satisfies PeerConfig);\r\n }\r\n\r\n return {\r\n identity: loaded.identity,\r\n peers,\r\n relay: loaded.relay,\r\n };\r\n }\r\n}\r\n","/**\r\n * Network reputation query handler.\r\n * Handles incoming reputation_query messages and returns reputation_response.\r\n */\r\n\r\nimport type { ReputationQuery, ReputationResponse, TrustScore } from './types.js';\r\nimport { computeTrustScore, computeTrustScores } from './scoring.js';\r\nimport type { ReputationStore } from './store.js';\r\n\r\n/** Maximum number of verification records to include in a response */\r\nconst MAX_VERIFICATIONS_IN_RESPONSE = 50;\r\n\r\n/**\r\n * Handle an incoming reputation query by reading from the local store,\r\n * computing trust scores, and returning a response.\r\n *\r\n * @param query - The reputation query (agent, optional domain, optional after timestamp)\r\n * @param store - Local reputation store to read from\r\n * @param currentTime - Current timestamp (ms) for score computation\r\n * @returns Reputation response with computed scores and verification records\r\n */\r\nexport async function handleReputationQuery(\r\n query: ReputationQuery,\r\n store: ReputationStore,\r\n currentTime: number\r\n): Promise<ReputationResponse> {\r\n // Get all verifications from the store for score computation\r\n const allVerifications = await store.getVerifications();\r\n\r\n // Filter to verifications targeting the queried agent\r\n let relevantVerifications = allVerifications.filter(v => v.target === query.agent);\r\n\r\n // Apply domain filter if specified\r\n if (query.domain !== undefined) {\r\n relevantVerifications = relevantVerifications.filter(v => v.domain === query.domain);\r\n }\r\n\r\n // Apply after-timestamp filter if specified\r\n if (query.after !== undefined) {\r\n const after = query.after;\r\n relevantVerifications = relevantVerifications.filter(v => v.timestamp > after);\r\n }\r\n\r\n // Compute trust scores\r\n let scores: Record<string, TrustScore>;\r\n\r\n if (query.domain !== undefined) {\r\n const score = computeTrustScore(query.agent, query.domain, allVerifications, currentTime);\r\n scores = { [query.domain]: score };\r\n } else {\r\n const scoreMap = computeTrustScores(query.agent, allVerifications, currentTime);\r\n scores = {};\r\n for (const [domain, score] of scoreMap.entries()) {\r\n scores[domain] = score;\r\n }\r\n }\r\n\r\n // Size-limit verifications to most recent MAX_VERIFICATIONS_IN_RESPONSE\r\n const limitedVerifications = relevantVerifications\r\n .slice()\r\n .sort((a, b) => b.timestamp - a.timestamp)\r\n .slice(0, MAX_VERIFICATIONS_IN_RESPONSE);\r\n\r\n const response: ReputationResponse = {\r\n agent: query.agent,\r\n verifications: limitedVerifications,\r\n scores,\r\n };\r\n\r\n if (query.domain !== undefined) {\r\n response.domain = query.domain;\r\n }\r\n\r\n return response;\r\n}\r\n","/**\r\n * Cross-peer reputation synchronization.\r\n * Pull and merge verification records from trusted peers.\r\n */\r\n\r\nimport type { ReputationQuery, ReputationResponse } from './types.js';\r\nimport { verifyVerificationSignature } from './verification.js';\r\nimport type { ReputationStore } from './store.js';\r\n\r\n/**\r\n * Pull reputation data from a peer and merge it into the local store.\r\n *\r\n * Flow:\r\n * 1. Send `reputation_query` to peer via sendMessage\r\n * 2. Receive `reputation_response` with verification records\r\n * 3. For each record: verify signature, check domain matches, check not duplicate\r\n * 4. Append new records to local store\r\n * 5. Return count of added/skipped\r\n *\r\n * @param agentPublicKey - Public key of the agent whose reputation to sync\r\n * @param domain - Domain to sync reputation for\r\n * @param store - Local reputation store to merge records into\r\n * @param sendMessage - Function that sends a reputation_query and returns the response\r\n * @returns Counts of records added and skipped\r\n */\r\nexport async function syncReputationFromPeer(\r\n agentPublicKey: string,\r\n domain: string,\r\n store: ReputationStore,\r\n sendMessage: (type: string, payload: ReputationQuery) => Promise<ReputationResponse>\r\n): Promise<{ added: number; skipped: number }> {\r\n // Build and send the query\r\n const query: ReputationQuery = {\r\n agent: agentPublicKey,\r\n domain,\r\n };\r\n\r\n const response = await sendMessage('reputation_query', query);\r\n\r\n // Build set of existing record IDs for fast deduplication\r\n const existing = await store.getVerifications();\r\n const existingIds = new Set(existing.map(v => v.id));\r\n\r\n let added = 0;\r\n let skipped = 0;\r\n\r\n for (const record of response.verifications) {\r\n // Skip duplicate records (content-addressed by ID)\r\n if (existingIds.has(record.id)) {\r\n skipped++;\r\n continue;\r\n }\r\n\r\n // Verify cryptographic signature before accepting\r\n const sigResult = verifyVerificationSignature(record);\r\n if (!sigResult.valid) {\r\n skipped++;\r\n continue;\r\n }\r\n\r\n // Ensure the record's domain matches what we requested\r\n if (record.domain !== domain) {\r\n skipped++;\r\n continue;\r\n }\r\n\r\n // Add to local store and track for deduplication within this batch\r\n await store.addVerification(record);\r\n existingIds.add(record.id);\r\n added++;\r\n }\r\n\r\n return { added, skipped };\r\n}\r\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAAA,SAAS,kBAAkB;AA0B3B,SAAS,gBAAgB,OAAwB;AAC/C,MAAI,UAAU,QAAQ,UAAU,OAAW,QAAO,KAAK,UAAU,KAAK;AACtE,MAAI,OAAO,UAAU,SAAU,QAAO,KAAK,UAAU,KAAK;AAC1D,MAAI,MAAM,QAAQ,KAAK,GAAG;AACxB,WAAO,MAAM,MAAM,IAAI,eAAe,EAAE,KAAK,GAAG,IAAI;AAAA,EACtD;AACA,QAAM,OAAO,OAAO,KAAK,KAAgC,EAAE,KAAK;AAChE,QAAM,QAAQ,KAAK,IAAI,OAAK,KAAK,UAAU,CAAC,IAAI,MAAM,gBAAiB,MAAkC,CAAC,CAAC,CAAC;AAC5G,SAAO,MAAM,MAAM,KAAK,GAAG,IAAI;AACjC;AAKA,SAAS,oBAAoB,MAAc,SAAiB,aAAsB,cAA+B;AAC/G,QAAM,OAAO;AAAA,IACX;AAAA,IACA;AAAA,IACA,GAAI,gBAAgB,SAAY,EAAE,YAAY,IAAI,CAAC;AAAA,IACnD,GAAI,iBAAiB,SAAY,EAAE,aAAa,IAAI,CAAC;AAAA,EACvD;AACA,QAAM,YAAY,gBAAgB,IAAI;AACtC,SAAO,WAAW,QAAQ,EAAE,OAAO,SAAS,EAAE,OAAO,KAAK;AAC5D;AAWO,SAAS,iBACd,MACA,SACA,aACA,UAII,CAAC,GACO;AACZ,QAAM,EAAE,aAAa,cAAc,OAAO,CAAC,EAAE,IAAI;AAEjD,QAAM,KAAK,oBAAoB,MAAM,SAAS,aAAa,YAAY;AAEvE,SAAO;AAAA,IACL;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA,GAAI,gBAAgB,SAAY,EAAE,YAAY,IAAI,CAAC;AAAA,IACnD,GAAI,iBAAiB,SAAY,EAAE,aAAa,IAAI,CAAC;AAAA,IACrD;AAAA,EACF;AACF;AAQO,SAAS,mBAAmB,YAA4D;AAC7F,QAAM,SAAmB,CAAC;AAE1B,MAAI,CAAC,cAAc,OAAO,eAAe,UAAU;AACjD,WAAO,EAAE,OAAO,OAAO,QAAQ,CAAC,8BAA8B,EAAE;AAAA,EAClE;AAEA,QAAM,MAAM;AAEZ,MAAI,CAAC,IAAI,MAAM,OAAO,IAAI,OAAO,UAAU;AACzC,WAAO,KAAK,iDAAiD;AAAA,EAC/D;AAEA,MAAI,CAAC,IAAI,QAAQ,OAAO,IAAI,SAAS,UAAU;AAC7C,WAAO,KAAK,mDAAmD;AAAA,EACjE;AAEA,MAAI,CAAC,IAAI,WAAW,OAAO,IAAI,YAAY,UAAU;AACnD,WAAO,KAAK,sDAAsD;AAAA,EACpE;AAEA,MAAI,CAAC,IAAI,eAAe,OAAO,IAAI,gBAAgB,UAAU;AAC3D,WAAO,KAAK,0DAA0D;AAAA,EACxE;AAEA,MAAI,CAAC,MAAM,QAAQ,IAAI,IAAI,GAAG;AAC5B,WAAO,KAAK,mDAAmD;AAAA,EACjE,WAAW,CAAC,IAAI,KAAK,MAAM,SAAO,OAAO,QAAQ,QAAQ,GAAG;AAC1D,WAAO,KAAK,oDAAoD;AAAA,EAClE;AAEA,MAAI,IAAI,gBAAgB,WAAc,OAAO,IAAI,gBAAgB,YAAY,IAAI,gBAAgB,OAAO;AACtG,WAAO,KAAK,gDAAgD;AAAA,EAC9D;AAEA,MAAI,IAAI,iBAAiB,WAAc,OAAO,IAAI,iBAAiB,YAAY,IAAI,iBAAiB,OAAO;AACzG,WAAO,KAAK,iDAAiD;AAAA,EAC/D;AAEA,MAAI,OAAO,SAAS,GAAG;AACrB,WAAO,EAAE,OAAO,OAAO,OAAO;AAAA,EAChC;AAEA,SAAO,EAAE,OAAO,KAAK;AACvB;;;AClIO,IAAM,YAAN,MAAgB;AAAA,EACb,QAA2B,oBAAI,IAAI;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAQ3C,gBAAgB,MAAkB;AAChC,SAAK,MAAM,IAAI,KAAK,WAAW,IAAI;AAAA,EACrC;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAQA,WAAW,WAA4B;AACrC,WAAO,KAAK,MAAM,OAAO,SAAS;AAAA,EACpC;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAQA,QAAQ,WAAqC;AAC3C,WAAO,KAAK,MAAM,IAAI,SAAS;AAAA,EACjC;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAQA,iBAAiB,MAAsB;AACrC,UAAM,SAAiB,CAAC;AAExB,eAAW,QAAQ,KAAK,MAAM,OAAO,GAAG;AACtC,YAAM,gBAAgB,KAAK,aAAa,KAAK,SAAO,IAAI,SAAS,IAAI;AACrE,UAAI,eAAe;AACjB,eAAO,KAAK,IAAI;AAAA,MAClB;AAAA,IACF;AAEA,WAAO;AAAA,EACT;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAQA,UAAU,KAAqB;AAC7B,UAAM,SAAiB,CAAC;AAExB,eAAW,QAAQ,KAAK,MAAM,OAAO,GAAG;AACtC,YAAM,SAAS,KAAK,aAAa,KAAK,SAAO,IAAI,KAAK,SAAS,GAAG,CAAC;AACnE,UAAI,QAAQ;AACV,eAAO,KAAK,IAAI;AAAA,MAClB;AAAA,IACF;AAEA,WAAO;AAAA,EACT;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAOA,WAAmB;AACjB,WAAO,MAAM,KAAK,KAAK,MAAM,OAAO,CAAC;AAAA,EACvC;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EASA,MAAM,UAAkB,cAAsB,KAAK,IAAI,GAAW;AAChE,UAAM,SAAS,cAAc;AAC7B,QAAI,UAAU;AAEd,eAAW,CAAC,WAAW,IAAI,KAAK,KAAK,MAAM,QAAQ,GAAG;AACpD,UAAI,KAAK,WAAW,QAAQ;AAC1B,aAAK,MAAM,OAAO,SAAS;AAC3B;AAAA,MACF;AAAA,IACF;AAEA,WAAO;AAAA,EACT;AACF;;;AC1FO,IAAM,mBAAN,MAAuB;AAAA,EAC5B,YACU,WACA,UACR;AAFQ;AACA;AAAA,EACP;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAUH,SACE,cACA,UACA,aAAuB,CAAC,KAAK,SAAS,SAAS,GACV;AACrC,UAAM,UAAqC;AAAA,MACzC,WAAW,KAAK,SAAS;AAAA,MACzB;AAAA,MACA,UAAU,WAAW;AAAA,QACnB,GAAG;AAAA,QACH,UAAU,KAAK,IAAI;AAAA,MACrB,IAAI;AAAA,QACF,UAAU,KAAK,IAAI;AAAA,MACrB;AAAA,IACF;AAEA,WAAO;AAAA,MACL;AAAA,MACA,KAAK,SAAS;AAAA,MACd,KAAK,SAAS;AAAA,MACd;AAAA,MACA,KAAK,IAAI;AAAA,MACT;AAAA,MACA;AAAA,IACF;AAAA,EACF;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAQA,eAAe,UAAqD;AAClE,UAAM,EAAE,QAAQ,IAAI;AAEpB,UAAM,OAAa;AAAA,MACjB,WAAW,QAAQ;AAAA,MACnB,cAAc,QAAQ;AAAA,MACtB,UAAU,QAAQ,UAAU,YAAY,SAAS;AAAA,MACjD,UAAU,QAAQ,WAAW;AAAA,QAC3B,MAAM,QAAQ,SAAS;AAAA,QACvB,SAAS,QAAQ,SAAS;AAAA,MAC5B,IAAI;AAAA,IACN;AAEA,SAAK,UAAU,gBAAgB,IAAI;AAAA,EACrC;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAUA,MACE,WACA,OACA,SACwB;AACxB,WAAO;AAAA,MACL;AAAA,MACA;AAAA,MACA;AAAA,IACF;AAAA,EACF;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EASA,YACE,UACqC;AACrC,UAAM,EAAE,QAAQ,IAAI;AACpB,QAAI,QAAgB,CAAC;AAGrB,QAAI,QAAQ,cAAc,UAAU,OAAO,QAAQ,UAAU,UAAU;AACrE,cAAQ,KAAK,UAAU,iBAAiB,QAAQ,KAAK;AAAA,IACvD,WAAW,QAAQ,cAAc,SAAS,OAAO,QAAQ,UAAU,UAAU;AAC3E,cAAQ,KAAK,UAAU,UAAU,QAAQ,KAAK;AAAA,IAChD,WAAW,QAAQ,cAAc,UAAU;AAGzC,cAAQ,CAAC;AAAA,IACX;AAGA,UAAM,QAAQ,QAAQ,SAAS;AAC/B,UAAM,eAAe,MAAM;AAE3B,QAAI,UAAU,UAAa,QAAQ,GAAG;AACpC,cAAQ,MAAM,MAAM,GAAG,KAAK;AAAA,IAC9B;AAGA,UAAM,gBAAgB,MAAM,IAAI,WAAS;AAAA,MACvC,WAAW,KAAK;AAAA,MAChB,cAAc,KAAK;AAAA,MACnB,UAAU,KAAK,WAAW;AAAA,QACxB,MAAM,KAAK,SAAS;AAAA,QACpB,SAAS,KAAK,SAAS;AAAA,QACvB,UAAU,KAAK;AAAA,MACjB,IAAI;AAAA,QACF,UAAU,KAAK;AAAA,MACjB;AAAA;AAAA,MAEA,YAAY;AAAA,IACd,EAAE;AAEF,UAAM,kBAA6C;AAAA,MACjD,SAAS,SAAS;AAAA,MAClB,OAAO;AAAA,MACP;AAAA,IACF;AAEA,WAAO;AAAA,MACL;AAAA,MACA,KAAK,SAAS;AAAA,MACd,KAAK,SAAS;AAAA,MACd;AAAA,MACA,KAAK,IAAI;AAAA,MACT,SAAS;AAAA;AAAA,MACT,CAAC,SAAS,IAAI;AAAA,IAChB;AAAA,EACF;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAQA,WAAW,UAAkB,cAAsB,KAAK,IAAI,GAAW;AACrE,WAAO,KAAK,UAAU,MAAM,UAAU,WAAW;AAAA,EACnD;AACF;;;AC5GO,SAAS,wBAAwB,SAAwD;AAC9F,QAAM,SAAmB,CAAC;AAE1B,MAAI,OAAO,YAAY,YAAY,YAAY,MAAM;AACnD,WAAO,KAAK,2BAA2B;AACvC,WAAO,EAAE,OAAO,OAAO,OAAO;AAAA,EAChC;AAEA,QAAM,IAAI;AAEV,MAAI,EAAE,YAAY,QAAW;AAC3B,QAAI,OAAO,EAAE,YAAY,YAAY,EAAE,YAAY,MAAM;AACvD,aAAO,KAAK,2BAA2B;AAAA,IACzC,OAAO;AACL,YAAM,UAAU,EAAE;AAClB,UAAI,QAAQ,iBAAiB,UAAa,OAAO,QAAQ,iBAAiB,UAAU;AAClF,eAAO,KAAK,uCAAuC;AAAA,MACrD;AACA,UAAI,QAAQ,UAAU,UAAa,OAAO,QAAQ,UAAU,UAAU;AACpE,eAAO,KAAK,gCAAgC;AAAA,MAC9C;AAAA,IACF;AAAA,EACF;AAEA,SAAO,EAAE,OAAO,OAAO,WAAW,GAAG,OAAO;AAC9C;AAKO,SAAS,yBAAyB,SAAwD;AAC/F,QAAM,SAAmB,CAAC;AAE1B,MAAI,OAAO,YAAY,YAAY,YAAY,MAAM;AACnD,WAAO,KAAK,2BAA2B;AACvC,WAAO,EAAE,OAAO,OAAO,OAAO;AAAA,EAChC;AAEA,QAAM,IAAI;AAEV,MAAI,CAAC,MAAM,QAAQ,EAAE,KAAK,GAAG;AAC3B,WAAO,KAAK,wBAAwB;AAAA,EACtC,OAAO;AACL,MAAE,MAAM,QAAQ,CAAC,MAAM,UAAU;AAC/B,UAAI,OAAO,SAAS,YAAY,SAAS,MAAM;AAC7C,eAAO,KAAK,SAAS,KAAK,qBAAqB;AAC/C;AAAA,MACF;AACA,YAAM,UAAU;AAChB,UAAI,OAAO,QAAQ,cAAc,UAAU;AACzC,eAAO,KAAK,SAAS,KAAK,8BAA8B;AAAA,MAC1D;AACA,UAAI,OAAO,QAAQ,aAAa,UAAU;AACxC,eAAO,KAAK,SAAS,KAAK,6BAA6B;AAAA,MACzD;AAAA,IACF,CAAC;AAAA,EACH;AAEA,MAAI,OAAO,EAAE,eAAe,UAAU;AACpC,WAAO,KAAK,6BAA6B;AAAA,EAC3C;AAEA,MAAI,OAAO,EAAE,mBAAmB,UAAU;AACxC,WAAO,KAAK,iCAAiC;AAAA,EAC/C;AAEA,SAAO,EAAE,OAAO,OAAO,WAAW,GAAG,OAAO;AAC9C;AAKO,SAAS,qBAAqB,SAAwD;AAC3F,QAAM,SAAmB,CAAC;AAE1B,MAAI,OAAO,YAAY,YAAY,YAAY,MAAM;AACnD,WAAO,KAAK,2BAA2B;AACvC,WAAO,EAAE,OAAO,OAAO,OAAO;AAAA,EAChC;AAEA,QAAM,IAAI;AAEV,MAAI,OAAO,EAAE,cAAc,UAAU;AACnC,WAAO,KAAK,4BAA4B;AAAA,EAC1C;AAEA,MAAI,EAAE,aAAa,UAAa,OAAO,EAAE,aAAa,UAAU;AAC9D,WAAO,KAAK,2BAA2B;AAAA,EACzC;AAEA,MAAI,EAAE,YAAY,UAAa,OAAO,EAAE,YAAY,UAAU;AAC5D,WAAO,KAAK,0BAA0B;AAAA,EACxC;AAEA,MAAI,EAAE,eAAe,UAAa,OAAO,EAAE,eAAe,UAAU;AAClE,WAAO,KAAK,6BAA6B;AAAA,EAC3C;AAEA,SAAO,EAAE,OAAO,OAAO,WAAW,GAAG,OAAO;AAC9C;;;AClKA,SAAS,cAAc,kBAAkB;AACzC,SAAS,gBAAgB;AACzB,SAAS,eAAe;AACxB,SAAS,eAAe;AA8CjB,SAAS,uBAA+B;AAC7C,MAAI,QAAQ,IAAI,cAAc;AAC5B,WAAO,QAAQ,QAAQ,IAAI,YAAY;AAAA,EACzC;AACA,SAAO,QAAQ,QAAQ,GAAG,WAAW,SAAS,aAAa;AAC7D;AAKA,SAAS,YAAY,QAA8C;AACjE,QAAM,cAAc,OAAO;AAC3B,MAAI,CAAC,aAAa,aAAa,CAAC,aAAa,YAAY;AACvD,UAAM,IAAI,MAAM,mEAAmE;AAAA,EACrF;AACA,QAAM,WAA0B;AAAA,IAC9B,WAAW,YAAY;AAAA,IACvB,YAAY,YAAY;AAAA,IACxB,MAAM,OAAO,YAAY,SAAS,WAAW,YAAY,OAAO;AAAA,EAClE;AAEA,QAAM,QAAyC,CAAC;AAChD,MAAI,OAAO,SAAS,OAAO,OAAO,UAAU,UAAU;AACpD,eAAW,SAAS,OAAO,OAAO,OAAO,KAAK,GAAG;AAC/C,YAAM,OAAO;AACb,UAAI,QAAQ,OAAO,KAAK,cAAc,UAAU;AAC9C,cAAM,KAAK,SAAmB,IAAI;AAAA,UAChC,WAAW,KAAK;AAAA,UAChB,KAAK,OAAO,KAAK,QAAQ,WAAW,KAAK,MAAM;AAAA,UAC/C,OAAO,OAAO,KAAK,UAAU,WAAW,KAAK,QAAQ;AAAA,UACrD,MAAM,OAAO,KAAK,SAAS,WAAW,KAAK,OAAO;AAAA,QACpD;AAAA,MACF;AAAA,IACF;AAAA,EACF;AAEA,MAAI;AACJ,QAAM,WAAW,OAAO;AACxB,MAAI,OAAO,aAAa,UAAU;AAChC,YAAQ,EAAE,KAAK,UAAU,aAAa,KAAK;AAAA,EAC7C,WAAW,YAAY,OAAO,aAAa,UAAU;AACnD,UAAM,IAAI;AACV,QAAI,OAAO,EAAE,QAAQ,UAAU;AAC7B,cAAQ;AAAA,QACN,KAAK,EAAE;AAAA,QACP,aAAa,OAAO,EAAE,gBAAgB,YAAY,EAAE,cAAc;AAAA,QAClE,MAAM,OAAO,EAAE,SAAS,WAAW,EAAE,OAAO;AAAA,QAC5C,gBAAgB,OAAO,EAAE,mBAAmB,WAAW,EAAE,iBAAiB;AAAA,MAC5E;AAAA,IACF;AAAA,EACF;AAEA,SAAO;AAAA,IACL;AAAA,IACA;AAAA,IACA,GAAI,QAAQ,EAAE,MAAM,IAAI,CAAC;AAAA,EAC3B;AACF;AAUO,SAAS,gBAAgB,MAA4B;AAC1D,QAAM,aAAa,QAAQ,qBAAqB;AAEhD,MAAI,CAAC,WAAW,UAAU,GAAG;AAC3B,UAAM,IAAI,MAAM,4BAA4B,UAAU,2CAA2C;AAAA,EACnG;AAEA,QAAM,UAAU,aAAa,YAAY,OAAO;AAChD,MAAI;AACJ,MAAI;AACF,aAAS,KAAK,MAAM,OAAO;AAAA,EAC7B,QAAQ;AACN,UAAM,IAAI,MAAM,gCAAgC,UAAU,EAAE;AAAA,EAC9D;AAEA,SAAO,YAAY,MAAM;AAC3B;AASA,eAAsB,qBAAqB,MAAqC;AAC9E,QAAM,aAAa,QAAQ,qBAAqB;AAEhD,MAAI;AACJ,MAAI;AACF,cAAU,MAAM,SAAS,YAAY,OAAO;AAAA,EAC9C,SAAS,KAAK;AACZ,UAAM,OAAO,OAAO,OAAO,QAAQ,YAAY,UAAU,MAAO,IAA8B,OAAO;AACrG,QAAI,SAAS,UAAU;AACrB,YAAM,IAAI,MAAM,4BAA4B,UAAU,2CAA2C;AAAA,IACnG;AACA,UAAM;AAAA,EACR;AAEA,MAAI;AACJ,MAAI;AACF,aAAS,KAAK,MAAM,OAAO;AAAA,EAC7B,QAAQ;AACN,UAAM,IAAI,MAAM,gCAAgC,UAAU,EAAE;AAAA,EAC9D;AAEA,SAAO,YAAY,MAAM;AAC3B;;;ACnKA,SAAS,gBAAAA,eAAc,eAAe,cAAAC,aAAY,iBAAiB;AACnE,SAAS,MAAM,eAAe;AAGvB,IAAM,oBAAoB;AAE1B,SAAS,oBAAoB,YAA6B;AAC/D,MAAI,YAAY;AACd,WAAO,KAAK,YAAY,iBAAiB;AAAA,EAC3C;AACA,QAAM,aAAa,qBAAqB;AACxC,SAAO,KAAK,QAAQ,UAAU,GAAG,iBAAiB;AACpD;AAEO,SAAS,iBAAiB,UAA6B;AAC5D,QAAM,OAAO,YAAY,oBAAoB;AAC7C,MAAI,CAACC,YAAW,IAAI,EAAG,QAAO,CAAC;AAE/B,QAAM,QAAQC,cAAa,MAAM,OAAO,EACrC,MAAM,IAAI,EACV,IAAI,CAAC,SAAS,KAAK,KAAK,CAAC,EACzB,OAAO,CAAC,SAAS,KAAK,SAAS,KAAK,CAAC,KAAK,WAAW,GAAG,CAAC;AAE5D,SAAO,MAAM,KAAK,IAAI,IAAI,KAAK,CAAC;AAClC;AAEO,SAAS,iBAAiB,OAAiB,UAAyB;AACzE,QAAM,OAAO,YAAY,oBAAoB;AAC7C,QAAM,MAAM,QAAQ,IAAI;AACxB,MAAI,CAACD,YAAW,GAAG,GAAG;AACpB,cAAU,KAAK,EAAE,WAAW,KAAK,CAAC;AAAA,EACpC;AAEA,QAAM,SAAS,MAAM,KAAK,IAAI,IAAI,MAAM,IAAI,CAAC,SAAS,KAAK,KAAK,CAAC,EAAE,OAAO,OAAO,CAAC,CAAC,EAAE,KAAK;AAC1F,QAAM,UAAU;AAAA,IACd;AAAA,IACA;AAAA,IACA,GAAG;AAAA,IACH;AAAA,EACF,EAAE,KAAK,IAAI;AAEX,gBAAc,MAAM,SAAS,OAAO;AACtC;AAEO,IAAM,sBAAN,MAA0B;AAAA,EACd;AAAA,EACA;AAAA,EAEjB,YAAY,UAAmB;AAC7B,SAAK,WAAW,YAAY,oBAAoB;AAChD,SAAK,QAAQ,IAAI,IAAI,iBAAiB,KAAK,QAAQ,CAAC;AAAA,EACtD;AAAA,EAEA,WAAW,WAA4B;AACrC,UAAM,aAAa,UAAU,KAAK;AAClC,QAAI,CAAC,YAAY;AACf,aAAO;AAAA,IACT;AACA,UAAM,QAAQ,CAAC,KAAK,MAAM,IAAI,UAAU;AACxC,SAAK,MAAM,IAAI,UAAU;AACzB,QAAI,OAAO;AACT,WAAK,QAAQ;AAAA,IACf;AACA,WAAO;AAAA,EACT;AAAA,EAEA,aAAa,WAA4B;AACvC,UAAM,aAAa,UAAU,KAAK;AAClC,UAAM,UAAU,KAAK,MAAM,OAAO,UAAU;AAC5C,QAAI,SAAS;AACX,WAAK,QAAQ;AAAA,IACf;AACA,WAAO;AAAA,EACT;AAAA,EAEA,mBAA6B;AAC3B,WAAO,MAAM,KAAK,KAAK,MAAM,OAAO,CAAC,EAAE,KAAK;AAAA,EAC9C;AAAA,EAEQ,UAAgB;AACtB,qBAAiB,KAAK,iBAAiB,GAAG,KAAK,QAAQ;AAAA,EACzD;AACF;;;AClFA,SAAS,cAAAE,mBAAkB;AA4BpB,IAAM,sBAAN,MAAM,qBAAoB;AAAA,EACd,gBAAgB,oBAAI,IAA0B;AAAA,EAC9C,cAAc,oBAAI,IAAY;AAAA,EAC9B,eAAe,oBAAI,IAAoB;AAAA,EACvC,eAAe,oBAAI,IAAY;AAAA,EAExC;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EAER,OAAwB,qBAAqB;AAAA,EAC7C,OAAwB,sBAAsB;AAAA,EAE9C,YAAY,UAAkC,CAAC,GAAG;AAChD,SAAK,mBAAmB,QAAQ,oBAAoB;AACpD,SAAK,uBAAuB,QAAQ,wBAAwB;AAC5D,SAAK,oBAAoB,QAAQ,qBAAqB;AACtD,SAAK,uBAAuB,QAAQ,wBAAwB;AAC5D,SAAK,sBAAsB,QAAQ,uBAAuB;AAC1D,SAAK,sBAAsB,QAAQ,uBAAuB;AAC1D,SAAK,uBAAuB,QAAQ,wBAAwB;AAE5D,eAAW,QAAQ,QAAQ,gBAAgB,CAAC,GAAG;AAC7C,YAAM,aAAa,KAAK,KAAK;AAC7B,UAAI,YAAY;AACd,aAAK,aAAa,IAAI,UAAU;AAAA,MAClC;AAAA,IACF;AAAA,EACF;AAAA,EAEA,WAAW,UAAoB,iBAA6D;AAC1F,QAAI,KAAK,aAAa,IAAI,eAAe,GAAG;AAC1C,aAAO,EAAE,MAAM,MAAM,QAAQ,eAAe;AAAA,IAC9C;AAEA,QAAI,KAAK,cAAc,eAAe,GAAG;AACvC,aAAO,EAAE,MAAM,MAAM,QAAQ,eAAe;AAAA,IAC9C;AAEA,QAAI,KAAK,sBAAsB,SAAS,EAAE,GAAG;AAC3C,aAAO,EAAE,MAAM,MAAM,QAAQ,wBAAwB;AAAA,IACvD;AAEA,QAAI,KAAK,mBAAmB,iBAAiB,SAAS,MAAM,SAAS,OAAO,GAAG;AAC7E,aAAO,EAAE,MAAM,MAAM,QAAQ,oBAAoB;AAAA,IACnD;AAEA,WAAO,EAAE,MAAM,MAAM;AAAA,EACvB;AAAA,EAEA,WAAW,WAA4B;AACrC,UAAM,aAAa,UAAU,KAAK;AAClC,QAAI,CAAC,YAAY;AACf,aAAO;AAAA,IACT;AACA,UAAM,UAAU,KAAK,aAAa,IAAI,UAAU;AAChD,SAAK,aAAa,IAAI,UAAU;AAChC,WAAO,CAAC;AAAA,EACV;AAAA,EAEA,aAAa,WAA4B;AACvC,WAAO,KAAK,aAAa,OAAO,UAAU,KAAK,CAAC;AAAA,EAClD;AAAA,EAEA,mBAA6B;AAC3B,WAAO,MAAM,KAAK,KAAK,aAAa,OAAO,CAAC,EAAE,KAAK;AAAA,EACrD;AAAA,EAEQ,cAAc,iBAAkC;AACtD,QAAI,CAAC,KAAK,kBAAkB;AAC1B,aAAO;AAAA,IACT;AAEA,UAAM,MAAM,KAAK,IAAI;AACrB,UAAM,UAAU,KAAK,cAAc,IAAI,eAAe;AAEtD,QAAI,CAAC,WAAW,KAAK,cAAc,QAAQ,qBAAoB,oBAAoB;AACjF,WAAK,kBAAkB;AAAA,IACzB;AAEA,QAAI,CAAC,WAAW,MAAM,QAAQ,cAAc,KAAK,mBAAmB;AAClE,WAAK,cAAc,IAAI,iBAAiB,EAAE,OAAO,GAAG,aAAa,IAAI,CAAC;AACtE,aAAO;AAAA,IACT;AAEA,YAAQ,SAAS;AACjB,WAAO,QAAQ,QAAQ,KAAK;AAAA,EAC9B;AAAA,EAEQ,oBAA0B;AAChC,QAAI,YAA2B;AAC/B,QAAI,aAAa,OAAO;AAExB,eAAW,CAAC,KAAK,KAAK,KAAK,KAAK,cAAc,QAAQ,GAAG;AACvD,UAAI,MAAM,cAAc,YAAY;AAClC,qBAAa,MAAM;AACnB,oBAAY;AAAA,MACd;AAAA,IACF;AAEA,QAAI,WAAW;AACb,WAAK,cAAc,OAAO,SAAS;AAAA,IACrC;AAAA,EACF;AAAA,EAEQ,sBAAsB,YAA6B;AACzD,QAAI,CAAC,KAAK,sBAAsB;AAC9B,aAAO;AAAA,IACT;AAEA,QAAI,KAAK,YAAY,IAAI,UAAU,GAAG;AACpC,aAAO;AAAA,IACT;AAEA,SAAK,YAAY,IAAI,UAAU;AAC/B,QAAI,KAAK,YAAY,OAAO,KAAK,qBAAqB;AACpD,YAAM,SAAS,KAAK,YAAY,OAAO,EAAE,KAAK,EAAE;AAChD,UAAI,WAAW,QAAW;AACxB,aAAK,YAAY,OAAO,MAAM;AAAA,MAChC;AAAA,IACF;AAEA,WAAO;AAAA,EACT;AAAA,EAEQ,mBAAmB,iBAAyB,MAAc,SAA2B;AAC3F,QAAI,CAAC,KAAK,qBAAqB;AAC7B,aAAO;AAAA,IACT;AAEA,UAAM,OAAOA,YAAW,QAAQ,EAC7B,OAAO,eAAe,EACtB,OAAO,IAAI,EACX,OAAO,KAAK,UAAU,WAAW,IAAI,CAAC,EACtC,OAAO,KAAK;AAEf,UAAM,MAAM,KAAK,IAAI;AACrB,UAAM,YAAY,KAAK,aAAa,IAAI,IAAI;AAE5C,QAAI,cAAc,UAAa,MAAM,YAAY,KAAK,sBAAsB;AAC1E,aAAO;AAAA,IACT;AAEA,SAAK,aAAa,IAAI,MAAM,GAAG;AAE/B,QAAI,KAAK,aAAa,OAAO,qBAAoB,qBAAqB;AACpE,iBAAW,CAAC,KAAK,EAAE,KAAK,KAAK,aAAa,QAAQ,GAAG;AACnD,YAAI,MAAM,MAAM,KAAK,sBAAsB;AACzC,eAAK,aAAa,OAAO,GAAG;AAAA,QAC9B;AAAA,MACF;AACA,UAAI,KAAK,aAAa,OAAO,qBAAoB,qBAAqB;AACpE,cAAM,YAAY,KAAK,aAAa,KAAK,EAAE,KAAK,EAAE;AAClD,YAAI,cAAc,QAAW;AAC3B,eAAK,aAAa,OAAO,SAAS;AAAA,QACpC;AAAA,MACF;AAAA,IACF;AAEA,WAAO;AAAA,EACT;AACF;;;AC3GO,IAAM,eAAN,MAAmB;AAAA,EAChB;AAAA,EACA,cAAsC;AAAA,EAC7B;AAAA,EACT;AAAA,EACA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAQR,YACE,QACA,gBACA,QACA,oBACA;AACA,SAAK,SAAS;AACd,SAAK,iBAAiB;AACtB,SAAK,SAAS,UAAU;AACxB,SAAK,qBAAqB,sBAAsB;AAAA,EAClD;AAAA,EAEQ,YAAY,YAA4C;AAC9D,UAAM,SAAS,KAAK,OAAO,MAAM,IAAI,UAAU;AAC/C,QAAI,QAAQ;AACV,aAAO;AAAA,IACT;AAEA,eAAW,QAAQ,KAAK,OAAO,MAAM,OAAO,GAAG;AAC7C,UAAI,KAAK,cAAc,cAAc,KAAK,SAAS,YAAY;AAC7D,eAAO;AAAA,MACT;AAAA,IACF;AAEA,WAAO;AAAA,EACT;AAAA;AAAA;AAAA;AAAA;AAAA,EAMA,MAAM,YAAY,SAAyD;AACzE,UAAM,OAAO,KAAK,YAAY,QAAQ,QAAQ;AAC9C,QAAI,CAAC,MAAM;AACT,aAAO;AAAA,QACL,IAAI;AAAA,QACJ,QAAQ;AAAA,QACR,OAAO,iBAAiB,QAAQ,QAAQ;AAAA,MAC1C;AAAA,IACF;AAGA,QAAI,KAAK,OAAO,CAAC,QAAQ,WAAW;AAClC,YAAM,kBAAkB;AAAA,QACtB,UAAU;AAAA,UACR,WAAW,KAAK,OAAO,SAAS;AAAA,UAChC,YAAY,KAAK,OAAO,SAAS;AAAA,QACnC;AAAA,QACA,OAAO,oBAAI,IAAwB,CAAC,CAAC,KAAK,WAAW,IAAI,CAAC,CAAC;AAAA,MAC7D;AAEA,YAAM,aAAa,MAAM;AAAA,QACvB;AAAA,QACA,KAAK;AAAA,QACL,QAAQ;AAAA,QACR,QAAQ;AAAA,QACR,QAAQ;AAAA,MACV;AAEA,UAAI,WAAW,IAAI;AACjB,eAAO;AAAA,MACT;AAEA,WAAK,QAAQ,MAAM,gBAAgB,QAAQ,QAAQ,YAAY,WAAW,KAAK,EAAE;AAGjF,UAAI,QAAQ,QAAQ;AAClB,eAAO;AAAA,UACL,IAAI;AAAA,UACJ,QAAQ,WAAW;AAAA,UACnB,OAAO,kBAAkB,QAAQ,QAAQ,YAAY,WAAW,KAAK;AAAA,QACvE;AAAA,MACF;AAAA,IACF,WAAW,QAAQ,UAAU,CAAC,KAAK,KAAK;AAEtC,aAAO;AAAA,QACL,IAAI;AAAA,QACJ,QAAQ;AAAA,QACR,OAAO,6BAA6B,QAAQ,QAAQ;AAAA,MACtD;AAAA,IACF;AAGA,QAAI,KAAK,aAAa,UAAU,KAAK,KAAK,OAAO,OAAO;AACtD,YAAM,cAAc,MAAM;AAAA,QACxB;AAAA,UACE,UAAU,KAAK,OAAO;AAAA,UACtB,UAAU,KAAK,OAAO,MAAM;AAAA,UAC5B,aAAa,KAAK;AAAA,QACpB;AAAA,QACA,KAAK;AAAA,QACL,QAAQ;AAAA,QACR,QAAQ;AAAA,QACR,QAAQ;AAAA,MACV;AAEA,aAAO;AAAA,QACL,IAAI,YAAY;AAAA,QAChB,QAAQ;AAAA,QACR,OAAO,YAAY;AAAA,MACrB;AAAA,IACF;AAGA,WAAO;AAAA,MACL,IAAI;AAAA,MACJ,QAAQ;AAAA,MACR,OAAO,KAAK,MACR,sDAAsD,QAAQ,QAAQ,KACtE,oDAAoD,QAAQ,QAAQ;AAAA,IAC1E;AAAA,EACF;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAOA,MAAM,gBAAgB,SAA6D;AACjF,QAAI,CAAC,KAAK,aAAa,UAAU,KAAK,CAAC,KAAK,OAAO,OAAO;AACxD,aAAO;AAAA,QACL,IAAI;AAAA,QACJ,QAAQ;AAAA,QACR,OAAO;AAAA,MACT;AAAA,IACF;AAEA,SAAK,QAAQ;AAAA,MACX,0CAA0C,SAAS,QAAQ,YAAY,CAAC,SAAS,QAAQ,IAAI,cAAc,QAAQ,SAAS;AAAA,IAC9H;AAEA,UAAM,cAAc,MAAM;AAAA,MACxB;AAAA,QACE,UAAU,KAAK,OAAO;AAAA,QACtB,UAAU,KAAK,OAAO,MAAM;AAAA,QAC5B,aAAa,KAAK;AAAA,MACpB;AAAA,MACA,QAAQ;AAAA,MACR,QAAQ;AAAA,MACR,QAAQ;AAAA,MACR,QAAQ;AAAA,IACV;AAEA,WAAO;AAAA,MACL,IAAI,YAAY;AAAA,MAChB,QAAQ;AAAA,MACR,OAAO,YAAY;AAAA,IACrB;AAAA,EACF;AAAA;AAAA;AAAA;AAAA,EAKA,MAAM,cAAc,SAA+C;AACjE,UAAM,gBAAgB,oBAAI,IAAwB;AAClD,eAAW,QAAQ,KAAK,OAAO,MAAM,OAAO,GAAG;AAC7C,oBAAc,IAAI,KAAK,WAAW,IAAI;AAAA,IACxC;AACA,UAAM,SAAS,sBAAsB,SAAS,aAAa;AAC3D,QAAI,OAAO,IAAI;AACb,aAAO,EAAE,IAAI,MAAM,UAAU,OAAO,SAAS;AAAA,IAC/C;AACA,WAAO,EAAE,IAAI,OAAO,QAAQ,OAAO,OAAO;AAAA,EAC5C;AAAA,EAEA,WAAqB;AACnB,WAAO,MAAM,KAAK,KAAK,OAAO,MAAM,KAAK,CAAC;AAAA,EAC5C;AAAA,EAEA,cAAc,MAAsC;AAClD,WAAO,KAAK,YAAY,IAAI;AAAA,EAC9B;AAAA;AAAA;AAAA;AAAA,EAKA,MAAM,aAAa,KAA4B;AAC7C,QAAI,KAAK,aAAa;AACpB;AAAA,IACF;AAEA,UAAM,oBAAoB,KAAK,OAAO,OAAO,kBAAkB;AAC/D,QAAI,OAAO,KAAK,OAAO,SAAS,QAAQ,KAAK,OAAO,OAAO;AAE3D,QAAI,QAAQ,SAAS,SAAS,KAAK,OAAO,SAAS,SAAS,GAAG;AAC7D,aAAO;AAAA,IACT;AACA,UAAM,OAAO;AAAA,MACX,UAAU;AAAA,MACV,WAAW,KAAK,OAAO,SAAS;AAAA,MAChC,YAAY,KAAK,OAAO,SAAS;AAAA,MACjC;AAAA,MACA,cAAc;AAAA,MACd;AAAA,IACF;AAEA,QAAI,KAAK,oBAAoB;AAC3B,WAAK,cAAc,KAAK,mBAAmB,IAAI;AAAA,IACjD,OAAO;AACL,WAAK,cAAc,IAAI,YAAY,IAAI;AAAA,IACzC;AAEA,SAAK,YAAY,GAAG,SAAS,CAAC,UAAiB;AAC7C,WAAK,QAAQ,MAAM,sBAAsB,MAAM,OAAO,EAAE;AAAA,IAC1D,CAAC;AAED,SAAK,YAAY,GAAG,WAAW,CAAC,UAAoB,MAAc,aAAsB;AACtF,WAAK,eAAe,UAAU,MAAM,QAAQ;AAAA,IAC9C,CAAC;AAED,QAAI;AACF,YAAM,KAAK,YAAY,QAAQ;AAAA,IACjC,SAAS,OAAO;AACd,YAAM,UAAU,iBAAiB,QAAQ,MAAM,UAAU,OAAO,KAAK;AACrE,WAAK,QAAQ,MAAM,+BAA+B,GAAG,MAAM,OAAO,EAAE;AACpE,WAAK,cAAc;AAAA,IACrB;AAAA,EACF;AAAA,EAEA,MAAM,kBAAiC;AACrC,QAAI,KAAK,aAAa;AACpB,WAAK,YAAY,WAAW;AAC5B,WAAK,cAAc;AAAA,IACrB;AAAA,EACF;AAAA,EAEA,mBAA4B;AAC1B,WAAO,KAAK,aAAa,UAAU,KAAK;AAAA,EAC1C;AAAA;AAAA;AAAA;AAAA,EAKA,aAAa,WAAW,MAA4C;AAClE,UAAM,aAAa,QAAQ,qBAAqB;AAChD,UAAM,SAAS,MAAM,qBAAqB,UAAU;AAEpD,UAAM,QAAQ,oBAAI,IAAwB;AAC1C,eAAW,KAAK,OAAO,OAAO,OAAO,KAAK,GAAG;AAC3C,YAAM,IAAI,EAAE,WAAW;AAAA,QACrB,WAAW,EAAE;AAAA,QACb,KAAK,EAAE;AAAA,QACP,OAAO,EAAE;AAAA,QACT,MAAM,EAAE;AAAA,MACV,CAAsB;AAAA,IACxB;AAEA,WAAO;AAAA,MACL,UAAU,OAAO;AAAA,MACjB;AAAA,MACA,OAAO,OAAO;AAAA,IAChB;AAAA,EACF;AACF;;;ACtVA,IAAM,gCAAgC;AAWtC,eAAsB,sBACpB,OACA,OACA,aAC6B;AAE7B,QAAM,mBAAmB,MAAM,MAAM,iBAAiB;AAGtD,MAAI,wBAAwB,iBAAiB,OAAO,OAAK,EAAE,WAAW,MAAM,KAAK;AAGjF,MAAI,MAAM,WAAW,QAAW;AAC9B,4BAAwB,sBAAsB,OAAO,OAAK,EAAE,WAAW,MAAM,MAAM;AAAA,EACrF;AAGA,MAAI,MAAM,UAAU,QAAW;AAC7B,UAAM,QAAQ,MAAM;AACpB,4BAAwB,sBAAsB,OAAO,OAAK,EAAE,YAAY,KAAK;AAAA,EAC/E;AAGA,MAAI;AAEJ,MAAI,MAAM,WAAW,QAAW;AAC9B,UAAM,QAAQ,kBAAkB,MAAM,OAAO,MAAM,QAAQ,kBAAkB,WAAW;AACxF,aAAS,EAAE,CAAC,MAAM,MAAM,GAAG,MAAM;AAAA,EACnC,OAAO;AACL,UAAM,WAAW,mBAAmB,MAAM,OAAO,kBAAkB,WAAW;AAC9E,aAAS,CAAC;AACV,eAAW,CAAC,QAAQ,KAAK,KAAK,SAAS,QAAQ,GAAG;AAChD,aAAO,MAAM,IAAI;AAAA,IACnB;AAAA,EACF;AAGA,QAAM,uBAAuB,sBAC1B,MAAM,EACN,KAAK,CAAC,GAAG,MAAM,EAAE,YAAY,EAAE,SAAS,EACxC,MAAM,GAAG,6BAA6B;AAEzC,QAAM,WAA+B;AAAA,IACnC,OAAO,MAAM;AAAA,IACb,eAAe;AAAA,IACf;AAAA,EACF;AAEA,MAAI,MAAM,WAAW,QAAW;AAC9B,aAAS,SAAS,MAAM;AAAA,EAC1B;AAEA,SAAO;AACT;;;ACjDA,eAAsB,uBACpB,gBACA,QACA,OACA,aAC6C;AAE7C,QAAM,QAAyB;AAAA,IAC7B,OAAO;AAAA,IACP;AAAA,EACF;AAEA,QAAM,WAAW,MAAM,YAAY,oBAAoB,KAAK;AAG5D,QAAM,WAAW,MAAM,MAAM,iBAAiB;AAC9C,QAAM,cAAc,IAAI,IAAI,SAAS,IAAI,OAAK,EAAE,EAAE,CAAC;AAEnD,MAAI,QAAQ;AACZ,MAAI,UAAU;AAEd,aAAW,UAAU,SAAS,eAAe;AAE3C,QAAI,YAAY,IAAI,OAAO,EAAE,GAAG;AAC9B;AACA;AAAA,IACF;AAGA,UAAM,YAAY,4BAA4B,MAAM;AACpD,QAAI,CAAC,UAAU,OAAO;AACpB;AACA;AAAA,IACF;AAGA,QAAI,OAAO,WAAW,QAAQ;AAC5B;AACA;AAAA,IACF;AAGA,UAAM,MAAM,gBAAgB,MAAM;AAClC,gBAAY,IAAI,OAAO,EAAE;AACzB;AAAA,EACF;AAEA,SAAO,EAAE,OAAO,QAAQ;AAC1B;","names":["readFileSync","existsSync","existsSync","readFileSync","createHash"]}
|
|
1
|
+
{"version":3,"sources":["../src/registry/capability.ts","../src/registry/peer-store.ts","../src/registry/discovery-service.ts","../src/message/types/peer-discovery.ts","../src/config.ts","../src/relay/ignored-peers.ts","../src/relay/inbound-message-guard.ts","../src/service.ts","../src/reputation/network.ts","../src/reputation/sync.ts"],"sourcesContent":["import { createHash } from 'node:crypto';\r\n\r\n/**\r\n * A capability describes something an agent can do\r\n */\r\nexport interface Capability {\r\n /** Unique ID (content-addressed hash of name + version + schema) */\r\n id: string;\r\n /** Human-readable name: 'code-review', 'summarization', 'translation' */\r\n name: string;\r\n /** Semantic version */\r\n version: string;\r\n /** What the capability does */\r\n description: string;\r\n /** JSON Schema for expected input */\r\n inputSchema?: object;\r\n /** JSON Schema for expected output */\r\n outputSchema?: object;\r\n /** Discovery tags: ['code', 'typescript', 'review'] */\r\n tags: string[];\r\n}\r\n\r\n/**\r\n * Deterministic JSON serialization for capability hashing.\r\n * Recursively sorts object keys.\r\n */\r\nfunction stableStringify(value: unknown): string {\r\n if (value === null || value === undefined) return JSON.stringify(value);\r\n if (typeof value !== 'object') return JSON.stringify(value);\r\n if (Array.isArray(value)) {\r\n return '[' + value.map(stableStringify).join(',') + ']';\r\n }\r\n const keys = Object.keys(value as Record<string, unknown>).sort();\r\n const pairs = keys.map(k => JSON.stringify(k) + ':' + stableStringify((value as Record<string, unknown>)[k]));\r\n return '{' + pairs.join(',') + '}';\r\n}\r\n\r\n/**\r\n * Compute content-addressed ID for a capability based on name, version, and schemas.\r\n */\r\nfunction computeCapabilityId(name: string, version: string, inputSchema?: object, outputSchema?: object): string {\r\n const data = {\r\n name,\r\n version,\r\n ...(inputSchema !== undefined ? { inputSchema } : {}),\r\n ...(outputSchema !== undefined ? { outputSchema } : {}),\r\n };\r\n const canonical = stableStringify(data);\r\n return createHash('sha256').update(canonical).digest('hex');\r\n}\r\n\r\n/**\r\n * Creates a capability with a content-addressed ID.\r\n * \r\n * @param name - Human-readable capability name\r\n * @param version - Semantic version string\r\n * @param description - Description of what the capability does\r\n * @param options - Optional input/output schemas and tags\r\n * @returns A Capability object with computed ID\r\n */\r\nexport function createCapability(\r\n name: string,\r\n version: string,\r\n description: string,\r\n options: {\r\n inputSchema?: object;\r\n outputSchema?: object;\r\n tags?: string[];\r\n } = {}\r\n): Capability {\r\n const { inputSchema, outputSchema, tags = [] } = options;\r\n \r\n const id = computeCapabilityId(name, version, inputSchema, outputSchema);\r\n \r\n return {\r\n id,\r\n name,\r\n version,\r\n description,\r\n ...(inputSchema !== undefined ? { inputSchema } : {}),\r\n ...(outputSchema !== undefined ? { outputSchema } : {}),\r\n tags,\r\n };\r\n}\r\n\r\n/**\r\n * Validates that a capability has all required fields.\r\n * \r\n * @param capability - The capability to validate\r\n * @returns Object with `valid` boolean and optional `errors` array\r\n */\r\nexport function validateCapability(capability: unknown): { valid: boolean; errors?: string[] } {\r\n const errors: string[] = [];\r\n \r\n if (!capability || typeof capability !== 'object') {\r\n return { valid: false, errors: ['Capability must be an object'] };\r\n }\r\n \r\n const cap = capability as Record<string, unknown>;\r\n \r\n if (!cap.id || typeof cap.id !== 'string') {\r\n errors.push('Missing or invalid field: id (must be a string)');\r\n }\r\n \r\n if (!cap.name || typeof cap.name !== 'string') {\r\n errors.push('Missing or invalid field: name (must be a string)');\r\n }\r\n \r\n if (!cap.version || typeof cap.version !== 'string') {\r\n errors.push('Missing or invalid field: version (must be a string)');\r\n }\r\n \r\n if (!cap.description || typeof cap.description !== 'string') {\r\n errors.push('Missing or invalid field: description (must be a string)');\r\n }\r\n \r\n if (!Array.isArray(cap.tags)) {\r\n errors.push('Missing or invalid field: tags (must be an array)');\r\n } else if (!cap.tags.every(tag => typeof tag === 'string')) {\r\n errors.push('Invalid field: tags (all elements must be strings)');\r\n }\r\n \r\n if (cap.inputSchema !== undefined && (typeof cap.inputSchema !== 'object' || cap.inputSchema === null)) {\r\n errors.push('Invalid field: inputSchema (must be an object)');\r\n }\r\n \r\n if (cap.outputSchema !== undefined && (typeof cap.outputSchema !== 'object' || cap.outputSchema === null)) {\r\n errors.push('Invalid field: outputSchema (must be an object)');\r\n }\r\n \r\n if (errors.length > 0) {\r\n return { valid: false, errors };\r\n }\r\n \r\n return { valid: true };\r\n}\r\n","import type { Peer } from './peer';\n\n/**\n * In-memory store for known peers on the network\n */\nexport class PeerStore {\n private peers: Map<string, Peer> = new Map();\n\n /**\n * Add or update a peer in the store.\n * If a peer with the same publicKey exists, it will be updated.\n * \n * @param peer - The peer to add or update\n */\n addOrUpdatePeer(peer: Peer): void {\n this.peers.set(peer.publicKey, peer);\n }\n\n /**\n * Remove a peer from the store.\n * \n * @param publicKey - The public key of the peer to remove\n * @returns true if the peer was removed, false if it didn't exist\n */\n removePeer(publicKey: string): boolean {\n return this.peers.delete(publicKey);\n }\n\n /**\n * Get a peer by their public key.\n * \n * @param publicKey - The public key of the peer to retrieve\n * @returns The peer if found, undefined otherwise\n */\n getPeer(publicKey: string): Peer | undefined {\n return this.peers.get(publicKey);\n }\n\n /**\n * Find all peers that offer a specific capability by name.\n * \n * @param name - The capability name to search for\n * @returns Array of peers that have a capability with the given name\n */\n findByCapability(name: string): Peer[] {\n const result: Peer[] = [];\n \n for (const peer of this.peers.values()) {\n const hasCapability = peer.capabilities.some(cap => cap.name === name);\n if (hasCapability) {\n result.push(peer);\n }\n }\n \n return result;\n }\n\n /**\n * Find all peers that have capabilities with a specific tag.\n * \n * @param tag - The tag to search for\n * @returns Array of peers that have at least one capability with the given tag\n */\n findByTag(tag: string): Peer[] {\n const result: Peer[] = [];\n \n for (const peer of this.peers.values()) {\n const hasTag = peer.capabilities.some(cap => cap.tags.includes(tag));\n if (hasTag) {\n result.push(peer);\n }\n }\n \n return result;\n }\n\n /**\n * Get all peers in the store.\n * \n * @returns Array of all peers\n */\n allPeers(): Peer[] {\n return Array.from(this.peers.values());\n }\n\n /**\n * Remove peers that haven't been seen within the specified time window.\n * \n * @param maxAgeMs - Maximum age in milliseconds. Peers older than this will be removed.\n * @param currentTime - Current timestamp (ms), defaults to Date.now()\n * @returns Number of peers removed\n */\n prune(maxAgeMs: number, currentTime: number = Date.now()): number {\n const cutoff = currentTime - maxAgeMs;\n let removed = 0;\n \n for (const [publicKey, peer] of this.peers.entries()) {\n if (peer.lastSeen < cutoff) {\n this.peers.delete(publicKey);\n removed++;\n }\n }\n \n return removed;\n }\n}\n","import { createEnvelope, type Envelope } from '../message/envelope';\nimport { PeerStore } from './peer-store';\nimport type { Capability } from './capability';\nimport type { Peer } from './peer';\nimport type {\n CapabilityAnnouncePayload,\n CapabilityQueryPayload,\n CapabilityResponsePayload,\n} from './messages';\n\n/**\n * DiscoveryService manages capability-based peer discovery.\n * It maintains a local index of peer capabilities and handles\n * capability announce, query, and response messages.\n */\nexport class DiscoveryService {\n constructor(\n private peerStore: PeerStore,\n private identity: { publicKey: string; privateKey: string }\n ) {}\n\n /**\n * Announce own capabilities to the network.\n * Creates a capability_announce envelope that can be broadcast to peers.\n * \n * @param capabilities - List of capabilities this agent offers\n * @param metadata - Optional metadata about this agent\n * @returns A signed capability_announce envelope\n */\n announce(\n capabilities: Capability[],\n metadata?: { name?: string; version?: string },\n recipients: string[] = [this.identity.publicKey]\n ): Envelope<CapabilityAnnouncePayload> {\n const payload: CapabilityAnnouncePayload = {\n publicKey: this.identity.publicKey,\n capabilities,\n metadata: metadata ? {\n ...metadata,\n lastSeen: Date.now(),\n } : {\n lastSeen: Date.now(),\n },\n };\n\n return createEnvelope(\n 'capability_announce',\n this.identity.publicKey,\n this.identity.privateKey,\n payload,\n Date.now(),\n undefined,\n recipients\n );\n }\n\n /**\n * Handle an incoming capability_announce message.\n * Updates the peer store with the announced capabilities.\n * \n * @param envelope - The capability_announce envelope to process\n */\n handleAnnounce(envelope: Envelope<CapabilityAnnouncePayload>): void {\n const { payload } = envelope;\n \n const peer: Peer = {\n publicKey: payload.publicKey,\n capabilities: payload.capabilities,\n lastSeen: payload.metadata?.lastSeen || envelope.timestamp,\n metadata: payload.metadata ? {\n name: payload.metadata.name,\n version: payload.metadata.version,\n } : undefined,\n };\n\n this.peerStore.addOrUpdatePeer(peer);\n }\n\n /**\n * Create a capability query payload.\n * \n * @param queryType - Type of query: 'name', 'tag', or 'schema'\n * @param query - The query value (capability name, tag, or schema)\n * @param filters - Optional filters (limit, minTrustScore)\n * @returns A capability_query payload\n */\n query(\n queryType: 'name' | 'tag' | 'schema',\n query: string | object,\n filters?: { limit?: number; minTrustScore?: number }\n ): CapabilityQueryPayload {\n return {\n queryType,\n query,\n filters,\n };\n }\n\n /**\n * Handle an incoming capability_query message.\n * Searches the local peer store and returns matching peers.\n * \n * @param envelope - The capability_query envelope to process\n * @returns A capability_response envelope with matching peers\n */\n handleQuery(\n envelope: Envelope<CapabilityQueryPayload>\n ): Envelope<CapabilityResponsePayload> {\n const { payload } = envelope;\n let peers: Peer[] = [];\n\n // Execute query based on type\n if (payload.queryType === 'name' && typeof payload.query === 'string') {\n peers = this.peerStore.findByCapability(payload.query);\n } else if (payload.queryType === 'tag' && typeof payload.query === 'string') {\n peers = this.peerStore.findByTag(payload.query);\n } else if (payload.queryType === 'schema') {\n // Schema-based matching is deferred to Phase 2b\n // For now, return empty results\n peers = [];\n }\n\n // Apply filters\n const limit = payload.filters?.limit;\n const totalMatches = peers.length;\n \n if (limit !== undefined && limit > 0) {\n peers = peers.slice(0, limit);\n }\n\n // Transform peers to response format\n const responsePeers = peers.map(peer => ({\n publicKey: peer.publicKey,\n capabilities: peer.capabilities,\n metadata: peer.metadata ? {\n name: peer.metadata.name,\n version: peer.metadata.version,\n lastSeen: peer.lastSeen,\n } : {\n lastSeen: peer.lastSeen,\n },\n // Trust score integration deferred to Phase 2b (RFC-001)\n trustScore: undefined,\n }));\n\n const responsePayload: CapabilityResponsePayload = {\n queryId: envelope.id,\n peers: responsePeers,\n totalMatches,\n };\n\n return createEnvelope(\n 'capability_response',\n this.identity.publicKey,\n this.identity.privateKey,\n responsePayload,\n Date.now(),\n envelope.id, // inReplyTo\n [envelope.from]\n );\n }\n\n /**\n * Remove peers that haven't been seen within the specified time window.\n * \n * @param maxAgeMs - Maximum age in milliseconds\n * @returns Number of peers removed\n */\n pruneStale(maxAgeMs: number, currentTime: number = Date.now()): number {\n return this.peerStore.prune(maxAgeMs, currentTime);\n }\n}\n","/**\r\n * Peer discovery message types for the Agora network.\r\n */\r\n\r\n/**\r\n * Request peer list from relay\r\n */\r\nexport interface PeerListRequestPayload {\r\n /** Optional filters */\r\n filters?: {\r\n /** Only peers seen in last N ms */\r\n activeWithin?: number;\r\n /** Maximum peers to return */\r\n limit?: number;\r\n };\r\n}\r\n\r\n/**\r\n * Relay responds with connected peers\r\n */\r\nexport interface PeerListResponsePayload {\r\n /** List of known peers */\r\n peers: Array<{\r\n /** Peer's Ed25519 public key */\r\n publicKey: string;\r\n /** Optional metadata (if peer announced) */\r\n metadata?: {\r\n name?: string;\r\n version?: string;\r\n capabilities?: string[];\r\n };\r\n /** Last seen timestamp (ms) */\r\n lastSeen: number;\r\n }>;\r\n /** Total peer count (may be > peers.length if limited) */\r\n totalPeers: number;\r\n /** Relay's public key (for trust verification) */\r\n relayPublicKey: string;\r\n}\r\n\r\n/**\r\n * Agent recommends another agent\r\n */\r\nexport interface PeerReferralPayload {\r\n /** Referred peer's public key */\r\n publicKey: string;\r\n /** Optional endpoint (if known) */\r\n endpoint?: string;\r\n /** Optional metadata */\r\n metadata?: {\r\n name?: string;\r\n version?: string;\r\n capabilities?: string[];\r\n };\r\n /** Referrer's comment */\r\n comment?: string;\r\n /** Trust hint (RFC-001 integration) */\r\n trustScore?: number;\r\n}\r\n\r\n/**\r\n * Validate PeerListRequestPayload\r\n */\r\nexport function validatePeerListRequest(payload: unknown): { valid: boolean; errors: string[] } {\r\n const errors: string[] = [];\r\n\r\n if (typeof payload !== 'object' || payload === null) {\r\n errors.push('Payload must be an object');\r\n return { valid: false, errors };\r\n }\r\n\r\n const p = payload as Record<string, unknown>;\r\n\r\n if (p.filters !== undefined) {\r\n if (typeof p.filters !== 'object' || p.filters === null) {\r\n errors.push('filters must be an object');\r\n } else {\r\n const filters = p.filters as Record<string, unknown>;\r\n if (filters.activeWithin !== undefined && typeof filters.activeWithin !== 'number') {\r\n errors.push('filters.activeWithin must be a number');\r\n }\r\n if (filters.limit !== undefined && typeof filters.limit !== 'number') {\r\n errors.push('filters.limit must be a number');\r\n }\r\n }\r\n }\r\n\r\n return { valid: errors.length === 0, errors };\r\n}\r\n\r\n/**\r\n * Validate PeerListResponsePayload\r\n */\r\nexport function validatePeerListResponse(payload: unknown): { valid: boolean; errors: string[] } {\r\n const errors: string[] = [];\r\n\r\n if (typeof payload !== 'object' || payload === null) {\r\n errors.push('Payload must be an object');\r\n return { valid: false, errors };\r\n }\r\n\r\n const p = payload as Record<string, unknown>;\r\n\r\n if (!Array.isArray(p.peers)) {\r\n errors.push('peers must be an array');\r\n } else {\r\n p.peers.forEach((peer, index) => {\r\n if (typeof peer !== 'object' || peer === null) {\r\n errors.push(`peers[${index}] must be an object`);\r\n return;\r\n }\r\n const peerObj = peer as Record<string, unknown>;\r\n if (typeof peerObj.publicKey !== 'string') {\r\n errors.push(`peers[${index}].publicKey must be a string`);\r\n }\r\n if (typeof peerObj.lastSeen !== 'number') {\r\n errors.push(`peers[${index}].lastSeen must be a number`);\r\n }\r\n });\r\n }\r\n\r\n if (typeof p.totalPeers !== 'number') {\r\n errors.push('totalPeers must be a number');\r\n }\r\n\r\n if (typeof p.relayPublicKey !== 'string') {\r\n errors.push('relayPublicKey must be a string');\r\n }\r\n\r\n return { valid: errors.length === 0, errors };\r\n}\r\n\r\n/**\r\n * Validate PeerReferralPayload\r\n */\r\nexport function validatePeerReferral(payload: unknown): { valid: boolean; errors: string[] } {\r\n const errors: string[] = [];\r\n\r\n if (typeof payload !== 'object' || payload === null) {\r\n errors.push('Payload must be an object');\r\n return { valid: false, errors };\r\n }\r\n\r\n const p = payload as Record<string, unknown>;\r\n\r\n if (typeof p.publicKey !== 'string') {\r\n errors.push('publicKey must be a string');\r\n }\r\n\r\n if (p.endpoint !== undefined && typeof p.endpoint !== 'string') {\r\n errors.push('endpoint must be a string');\r\n }\r\n\r\n if (p.comment !== undefined && typeof p.comment !== 'string') {\r\n errors.push('comment must be a string');\r\n }\r\n\r\n if (p.trustScore !== undefined && typeof p.trustScore !== 'number') {\r\n errors.push('trustScore must be a number');\r\n }\r\n\r\n return { valid: errors.length === 0, errors };\r\n}\r\n","import { readFileSync, existsSync } from 'node:fs';\r\nimport { readFile } from 'node:fs/promises';\r\nimport { resolve } from 'node:path';\r\nimport { homedir } from 'node:os';\r\n\r\n/**\r\n * Normalized relay configuration (supports both string and object in config file).\r\n */\r\nexport interface RelayConfig {\r\n url: string;\r\n autoConnect: boolean;\r\n name?: string;\r\n reconnectMaxMs?: number;\r\n}\r\n\r\n/**\r\n * Peer entry in config (webhook URL, token, public key).\r\n */\r\nexport interface AgoraPeerConfig {\r\n publicKey: string;\r\n /** Webhook URL (undefined for relay-only peers) */\r\n url?: string;\r\n /** Webhook auth token (undefined for relay-only peers) */\r\n token?: string;\r\n name?: string;\r\n}\r\n\r\n/**\r\n * Identity with optional display name (e.g. for relay registration).\r\n */\r\nexport interface AgoraIdentity {\r\n publicKey: string;\r\n privateKey: string;\r\n name?: string;\r\n}\r\n\r\n/**\r\n * Canonical Agora configuration shape.\r\n * Use loadAgoraConfig() to load from file with normalized relay.\r\n */\r\nexport interface AgoraConfig {\r\n identity: AgoraIdentity;\r\n peers: Record<string, AgoraPeerConfig>;\r\n relay?: RelayConfig;\r\n}\r\n\r\n/**\r\n * Default config file path: AGORA_CONFIG env or ~/.config/agora/config.json\r\n */\r\nexport function getDefaultConfigPath(): string {\r\n if (process.env.AGORA_CONFIG) {\r\n return resolve(process.env.AGORA_CONFIG);\r\n }\r\n return resolve(homedir(), '.config', 'agora', 'config.json');\r\n}\r\n\r\n/**\r\n * Parse and normalize config from a JSON object (shared by sync and async loaders).\r\n */\r\nfunction parseConfig(config: Record<string, unknown>): AgoraConfig {\r\n const rawIdentity = config.identity as Record<string, unknown> | undefined;\r\n if (!rawIdentity?.publicKey || !rawIdentity?.privateKey) {\r\n throw new Error('Invalid config: missing identity.publicKey or identity.privateKey');\r\n }\r\n const identity: AgoraIdentity = {\r\n publicKey: rawIdentity.publicKey as string,\r\n privateKey: rawIdentity.privateKey as string,\r\n name: typeof rawIdentity.name === 'string' ? rawIdentity.name : undefined,\r\n };\r\n\r\n const peers: Record<string, AgoraPeerConfig> = {};\r\n if (config.peers && typeof config.peers === 'object') {\r\n for (const entry of Object.values(config.peers)) {\r\n const peer = entry as Record<string, unknown>;\r\n if (peer && typeof peer.publicKey === 'string') {\r\n peers[peer.publicKey as string] = {\r\n publicKey: peer.publicKey as string,\r\n url: typeof peer.url === 'string' ? peer.url : undefined,\r\n token: typeof peer.token === 'string' ? peer.token : undefined,\r\n name: typeof peer.name === 'string' ? peer.name : undefined,\r\n };\r\n }\r\n }\r\n }\r\n\r\n let relay: RelayConfig | undefined;\r\n const rawRelay = config.relay;\r\n if (typeof rawRelay === 'string') {\r\n relay = { url: rawRelay, autoConnect: true };\r\n } else if (rawRelay && typeof rawRelay === 'object') {\r\n const r = rawRelay as Record<string, unknown>;\r\n if (typeof r.url === 'string') {\r\n relay = {\r\n url: r.url,\r\n autoConnect: typeof r.autoConnect === 'boolean' ? r.autoConnect : true,\r\n name: typeof r.name === 'string' ? r.name : undefined,\r\n reconnectMaxMs: typeof r.reconnectMaxMs === 'number' ? r.reconnectMaxMs : undefined,\r\n };\r\n }\r\n }\r\n\r\n return {\r\n identity,\r\n peers,\r\n ...(relay ? { relay } : {}),\r\n };\r\n}\r\n\r\n/**\r\n * Load and normalize Agora configuration from a JSON file (sync).\r\n * Supports relay as string (backward compat) or object { url?, autoConnect?, name?, reconnectMaxMs? }.\r\n *\r\n * @param path - Config file path; defaults to getDefaultConfigPath()\r\n * @returns Normalized AgoraConfig\r\n * @throws Error if file doesn't exist or config is invalid\r\n */\r\nexport function loadAgoraConfig(path?: string): AgoraConfig {\r\n const configPath = path ?? getDefaultConfigPath();\r\n\r\n if (!existsSync(configPath)) {\r\n throw new Error(`Config file not found at ${configPath}. Run 'npx @rookdaemon/agora init' first.`);\r\n }\r\n\r\n const content = readFileSync(configPath, 'utf-8');\r\n let config: Record<string, unknown>;\r\n try {\r\n config = JSON.parse(content) as Record<string, unknown>;\r\n } catch {\r\n throw new Error(`Invalid JSON in config file: ${configPath}`);\r\n }\r\n\r\n return parseConfig(config);\r\n}\r\n\r\n/**\r\n * Load and normalize Agora configuration from a JSON file (async).\r\n *\r\n * @param path - Config file path; defaults to getDefaultConfigPath()\r\n * @returns Normalized AgoraConfig\r\n * @throws Error if file doesn't exist or config is invalid\r\n */\r\nexport async function loadAgoraConfigAsync(path?: string): Promise<AgoraConfig> {\r\n const configPath = path ?? getDefaultConfigPath();\r\n\r\n let content: string;\r\n try {\r\n content = await readFile(configPath, 'utf-8');\r\n } catch (err) {\r\n const code = err && typeof err === 'object' && 'code' in err ? (err as NodeJS.ErrnoException).code : undefined;\r\n if (code === 'ENOENT') {\r\n throw new Error(`Config file not found at ${configPath}. Run 'npx @rookdaemon/agora init' first.`);\r\n }\r\n throw err;\r\n }\r\n\r\n let config: Record<string, unknown>;\r\n try {\r\n config = JSON.parse(content) as Record<string, unknown>;\r\n } catch {\r\n throw new Error(`Invalid JSON in config file: ${configPath}`);\r\n }\r\n\r\n return parseConfig(config);\r\n}\r\n","import { readFileSync, writeFileSync, existsSync, mkdirSync } from 'node:fs';\r\nimport { join, dirname } from 'node:path';\r\nimport { getDefaultConfigPath } from '../config';\r\n\r\nexport const IGNORED_FILE_NAME = 'IGNORED_PEERS.md';\r\n\r\nexport function getIgnoredPeersPath(storageDir?: string): string {\r\n if (storageDir) {\r\n return join(storageDir, IGNORED_FILE_NAME);\r\n }\r\n const configPath = getDefaultConfigPath();\r\n return join(dirname(configPath), IGNORED_FILE_NAME);\r\n}\r\n\r\nexport function loadIgnoredPeers(filePath?: string): string[] {\r\n const path = filePath ?? getIgnoredPeersPath();\r\n if (!existsSync(path)) return [];\r\n\r\n const lines = readFileSync(path, 'utf-8')\r\n .split('\\n')\r\n .map((line) => line.trim())\r\n .filter((line) => line.length > 0 && !line.startsWith('#'));\r\n\r\n return Array.from(new Set(lines));\r\n}\r\n\r\nexport function saveIgnoredPeers(peers: string[], filePath?: string): void {\r\n const path = filePath ?? getIgnoredPeersPath();\r\n const dir = dirname(path);\r\n if (!existsSync(dir)) {\r\n mkdirSync(dir, { recursive: true });\r\n }\r\n\r\n const unique = Array.from(new Set(peers.map((peer) => peer.trim()).filter(Boolean))).sort();\r\n const content = [\r\n '# Ignored peers',\r\n '# One public key per line',\r\n ...unique,\r\n '',\r\n ].join('\\n');\r\n\r\n writeFileSync(path, content, 'utf-8');\r\n}\r\n\r\nexport class IgnoredPeersManager {\r\n private readonly peers: Set<string>;\r\n private readonly filePath: string;\r\n\r\n constructor(filePath?: string) {\r\n this.filePath = filePath ?? getIgnoredPeersPath();\r\n this.peers = new Set(loadIgnoredPeers(this.filePath));\r\n }\r\n\r\n ignorePeer(publicKey: string): boolean {\r\n const normalized = publicKey.trim();\r\n if (!normalized) {\r\n return false;\r\n }\r\n const added = !this.peers.has(normalized);\r\n this.peers.add(normalized);\r\n if (added) {\r\n this.persist();\r\n }\r\n return added;\r\n }\r\n\r\n unignorePeer(publicKey: string): boolean {\r\n const normalized = publicKey.trim();\r\n const removed = this.peers.delete(normalized);\r\n if (removed) {\r\n this.persist();\r\n }\r\n return removed;\r\n }\r\n\r\n listIgnoredPeers(): string[] {\r\n return Array.from(this.peers.values()).sort();\r\n }\r\n\r\n private persist(): void {\r\n saveIgnoredPeers(this.listIgnoredPeers(), this.filePath);\r\n }\r\n}\r\n","import { createHash } from 'node:crypto';\r\nimport type { Envelope } from '../message/envelope';\r\n\r\ninterface SenderWindow {\r\n count: number;\r\n windowStart: number;\r\n}\r\n\r\nexport interface InboundSecurityOptions {\r\n rateLimitEnabled?: boolean;\r\n rateLimitMaxMessages?: number;\r\n rateLimitWindowMs?: number;\r\n envelopeDedupEnabled?: boolean;\r\n envelopeDedupMaxIds?: number;\r\n contentDedupEnabled?: boolean;\r\n contentDedupWindowMs?: number;\r\n ignoredPeers?: string[];\r\n}\r\n\r\n/**\r\n * Shared inbound guard for relay-fed consumers.\r\n *\r\n * Provides:\r\n * - static ignore list checks\r\n * - per-sender rate limiting\r\n * - envelope ID deduplication\r\n * - content-window deduplication\r\n */\r\nexport class InboundMessageGuard {\r\n private readonly senderWindows = new Map<string, SenderWindow>();\r\n private readonly envelopeIds = new Set<string>();\r\n private readonly contentDedup = new Map<string, number>();\r\n private readonly ignoredPeers = new Set<string>();\r\n\r\n private rateLimitEnabled: boolean;\r\n private rateLimitMaxMessages: number;\r\n private rateLimitWindowMs: number;\r\n private envelopeDedupEnabled: boolean;\r\n private envelopeDedupMaxIds: number;\r\n private contentDedupEnabled: boolean;\r\n private contentDedupWindowMs: number;\r\n\r\n private static readonly MAX_SENDER_ENTRIES = 500;\r\n private static readonly MAX_CONTENT_ENTRIES = 5000;\r\n\r\n constructor(options: InboundSecurityOptions = {}) {\r\n this.rateLimitEnabled = options.rateLimitEnabled ?? true;\r\n this.rateLimitMaxMessages = options.rateLimitMaxMessages ?? 10;\r\n this.rateLimitWindowMs = options.rateLimitWindowMs ?? 60_000;\r\n this.envelopeDedupEnabled = options.envelopeDedupEnabled ?? true;\r\n this.envelopeDedupMaxIds = options.envelopeDedupMaxIds ?? 1000;\r\n this.contentDedupEnabled = options.contentDedupEnabled ?? true;\r\n this.contentDedupWindowMs = options.contentDedupWindowMs ?? 1_800_000;\r\n\r\n for (const peer of options.ignoredPeers ?? []) {\r\n const normalized = peer.trim();\r\n if (normalized) {\r\n this.ignoredPeers.add(normalized);\r\n }\r\n }\r\n }\r\n\r\n shouldDrop(envelope: Envelope, senderPublicKey: string): { drop: boolean; reason?: string } {\r\n if (this.ignoredPeers.has(senderPublicKey)) {\r\n return { drop: true, reason: 'ignored_peer' };\r\n }\r\n\r\n if (this.isRateLimited(senderPublicKey)) {\r\n return { drop: true, reason: 'rate_limited' };\r\n }\r\n\r\n if (this.isDuplicateEnvelopeId(envelope.id)) {\r\n return { drop: true, reason: 'duplicate_envelope_id' };\r\n }\r\n\r\n if (this.isDuplicateContent(senderPublicKey, envelope.type, envelope.payload)) {\r\n return { drop: true, reason: 'duplicate_content' };\r\n }\r\n\r\n return { drop: false };\r\n }\r\n\r\n ignorePeer(publicKey: string): boolean {\r\n const normalized = publicKey.trim();\r\n if (!normalized) {\r\n return false;\r\n }\r\n const existed = this.ignoredPeers.has(normalized);\r\n this.ignoredPeers.add(normalized);\r\n return !existed;\r\n }\r\n\r\n unignorePeer(publicKey: string): boolean {\r\n return this.ignoredPeers.delete(publicKey.trim());\r\n }\r\n\r\n listIgnoredPeers(): string[] {\r\n return Array.from(this.ignoredPeers.values()).sort();\r\n }\r\n\r\n private isRateLimited(senderPublicKey: string): boolean {\r\n if (!this.rateLimitEnabled) {\r\n return false;\r\n }\r\n\r\n const now = Date.now();\r\n const current = this.senderWindows.get(senderPublicKey);\r\n\r\n if (!current && this.senderWindows.size >= InboundMessageGuard.MAX_SENDER_ENTRIES) {\r\n this.evictOldestSender();\r\n }\r\n\r\n if (!current || now - current.windowStart > this.rateLimitWindowMs) {\r\n this.senderWindows.set(senderPublicKey, { count: 1, windowStart: now });\r\n return false;\r\n }\r\n\r\n current.count += 1;\r\n return current.count > this.rateLimitMaxMessages;\r\n }\r\n\r\n private evictOldestSender(): void {\r\n let oldestKey: string | null = null;\r\n let oldestTime = Number.POSITIVE_INFINITY;\r\n\r\n for (const [key, value] of this.senderWindows.entries()) {\r\n if (value.windowStart < oldestTime) {\r\n oldestTime = value.windowStart;\r\n oldestKey = key;\r\n }\r\n }\r\n\r\n if (oldestKey) {\r\n this.senderWindows.delete(oldestKey);\r\n }\r\n }\r\n\r\n private isDuplicateEnvelopeId(envelopeId: string): boolean {\r\n if (!this.envelopeDedupEnabled) {\r\n return false;\r\n }\r\n\r\n if (this.envelopeIds.has(envelopeId)) {\r\n return true;\r\n }\r\n\r\n this.envelopeIds.add(envelopeId);\r\n if (this.envelopeIds.size > this.envelopeDedupMaxIds) {\r\n const oldest = this.envelopeIds.values().next().value;\r\n if (oldest !== undefined) {\r\n this.envelopeIds.delete(oldest);\r\n }\r\n }\r\n\r\n return false;\r\n }\r\n\r\n private isDuplicateContent(senderPublicKey: string, type: string, payload: unknown): boolean {\r\n if (!this.contentDedupEnabled) {\r\n return false;\r\n }\r\n\r\n const hash = createHash('sha256')\r\n .update(senderPublicKey)\r\n .update(type)\r\n .update(JSON.stringify(payload ?? null))\r\n .digest('hex');\r\n\r\n const now = Date.now();\r\n const firstSeen = this.contentDedup.get(hash);\r\n\r\n if (firstSeen !== undefined && now - firstSeen < this.contentDedupWindowMs) {\r\n return true;\r\n }\r\n\r\n this.contentDedup.set(hash, now);\r\n\r\n if (this.contentDedup.size > InboundMessageGuard.MAX_CONTENT_ENTRIES) {\r\n for (const [key, ts] of this.contentDedup.entries()) {\r\n if (now - ts >= this.contentDedupWindowMs) {\r\n this.contentDedup.delete(key);\r\n }\r\n }\r\n if (this.contentDedup.size > InboundMessageGuard.MAX_CONTENT_ENTRIES) {\r\n const oldestKey = this.contentDedup.keys().next().value;\r\n if (oldestKey !== undefined) {\r\n this.contentDedup.delete(oldestKey);\r\n }\r\n }\r\n }\r\n\r\n return false;\r\n }\r\n}\r\n","import type { AgoraIdentity, RelayConfig } from './config';\r\nimport { getDefaultConfigPath, loadAgoraConfigAsync } from './config';\r\nimport type { Envelope } from './message/envelope';\r\nimport type { MessageType } from './message/envelope';\r\nimport { RelayClient } from './relay/client';\r\nimport type { PeerConfig } from './transport/http';\r\nimport { decodeInboundEnvelope, sendToPeer } from './transport/http';\r\nimport { sendViaRelay } from './transport/relay';\r\nimport { shortKey } from './utils';\r\n\r\n/**\r\n * Service config: identity, peers keyed by name, optional relay.\r\n */\r\nexport interface AgoraServiceConfig {\r\n identity: AgoraIdentity;\r\n peers: Map<string, PeerConfig>;\r\n relay?: RelayConfig;\r\n}\r\n\r\nexport interface SendMessageOptions {\r\n peerName: string;\r\n type: MessageType;\r\n payload: unknown;\r\n inReplyTo?: string;\r\n /** Skip relay, send directly via HTTP only. Fails if peer has no URL or is unreachable. */\r\n direct?: boolean;\r\n /** Skip direct HTTP, always use relay even if peer has a URL. */\r\n relayOnly?: boolean;\r\n}\r\n\r\nexport interface SendMessageResult {\r\n ok: boolean;\r\n status: number;\r\n error?: string;\r\n}\r\n\r\nexport interface ReplyToEnvelopeOptions {\r\n /** The public key of the target (from envelope.sender) */\r\n targetPubkey: string;\r\n /** Message type for the reply */\r\n type: MessageType;\r\n /** Reply payload */\r\n payload: unknown;\r\n /** The envelope ID being replied to (required — this IS a reply) */\r\n inReplyTo: string;\r\n}\r\n\r\nexport interface DecodeInboundResult {\r\n ok: boolean;\r\n envelope?: Envelope;\r\n reason?: string;\r\n}\r\n\r\n/** Handler for relay messages. (envelope, fromPublicKey, fromName?) */\r\nexport type RelayMessageHandlerWithName = (envelope: Envelope, from: string, fromName?: string) => void;\r\n\r\n/** @deprecated Use RelayMessageHandlerWithName. Kept for backward compatibility. */\r\nexport type RelayMessageHandler = (envelope: Envelope) => void;\r\n\r\nexport interface Logger {\r\n debug(message: string): void;\r\n}\r\n\r\nexport interface RelayClientLike {\r\n connect(): Promise<void>;\r\n disconnect(): void;\r\n connected(): boolean;\r\n send(to: string, envelope: Envelope): Promise<{ ok: boolean; error?: string }>;\r\n on(event: 'message', handler: (envelope: Envelope, from: string, fromName?: string) => void): void;\r\n on(event: 'error', handler: (error: Error) => void): void;\r\n}\r\n\r\nexport interface RelayClientFactory {\r\n (opts: {\r\n relayUrl: string;\r\n publicKey: string;\r\n privateKey: string;\r\n name?: string;\r\n pingInterval: number;\r\n maxReconnectDelay: number;\r\n }): RelayClientLike;\r\n}\r\n\r\n/**\r\n * High-level Agora service: send by peer name, decode inbound, relay lifecycle.\r\n */\r\nexport class AgoraService {\r\n private config: AgoraServiceConfig;\r\n private relayClient: RelayClientLike | null = null;\r\n private readonly onRelayMessage: RelayMessageHandlerWithName;\r\n private logger: Logger | null;\r\n private relayClientFactory: RelayClientFactory | null;\r\n\r\n /**\r\n * @param config - Service config (identity, peers, optional relay)\r\n * @param onRelayMessage - Required callback for relay messages. Ensures no messages are lost between init and connect.\r\n * @param logger - Optional debug logger\r\n * @param relayClientFactory - Optional factory for relay client (for testing)\r\n */\r\n constructor(\r\n config: AgoraServiceConfig,\r\n onRelayMessage: RelayMessageHandlerWithName,\r\n logger?: Logger,\r\n relayClientFactory?: RelayClientFactory\r\n ) {\r\n this.config = config;\r\n this.onRelayMessage = onRelayMessage;\r\n this.logger = logger ?? null;\r\n this.relayClientFactory = relayClientFactory ?? null;\r\n }\r\n\r\n private resolvePeer(identifier: string): PeerConfig | undefined {\r\n const direct = this.config.peers.get(identifier);\r\n if (direct) {\r\n return direct;\r\n }\r\n\r\n for (const peer of this.config.peers.values()) {\r\n if (peer.publicKey === identifier || peer.name === identifier) {\r\n return peer;\r\n }\r\n }\r\n\r\n return undefined;\r\n }\r\n\r\n /**\r\n * Send a signed message to a named peer.\r\n * Tries HTTP webhook first; falls back to relay if HTTP is unavailable.\r\n */\r\n async sendMessage(options: SendMessageOptions): Promise<SendMessageResult> {\r\n const peer = this.resolvePeer(options.peerName);\r\n if (!peer) {\r\n return {\r\n ok: false,\r\n status: 0,\r\n error: `Unknown peer: ${options.peerName}`,\r\n };\r\n }\r\n\r\n // Try HTTP first (only if peer has a webhook URL and --relay-only not set)\r\n if (peer.url && !options.relayOnly) {\r\n const transportConfig = {\r\n identity: {\r\n publicKey: this.config.identity.publicKey,\r\n privateKey: this.config.identity.privateKey,\r\n },\r\n peers: new Map<string, PeerConfig>([[peer.publicKey, peer]]),\r\n };\r\n\r\n const httpResult = await sendToPeer(\r\n transportConfig,\r\n peer.publicKey,\r\n options.type,\r\n options.payload,\r\n options.inReplyTo\r\n );\r\n\r\n if (httpResult.ok) {\r\n return httpResult;\r\n }\r\n\r\n this.logger?.debug(`HTTP send to ${options.peerName} failed: ${httpResult.error}`);\r\n\r\n // --direct flag: do not fall back to relay\r\n if (options.direct) {\r\n return {\r\n ok: false,\r\n status: httpResult.status,\r\n error: `Direct send to ${options.peerName} failed: ${httpResult.error}`,\r\n };\r\n }\r\n } else if (options.direct && !peer.url) {\r\n // --direct requested but peer has no URL configured\r\n return {\r\n ok: false,\r\n status: 0,\r\n error: `Direct send failed: peer '${options.peerName}' has no URL configured`,\r\n };\r\n }\r\n\r\n // Fall back to relay\r\n if (this.relayClient?.connected() && this.config.relay) {\r\n const relayResult = await sendViaRelay(\r\n {\r\n identity: this.config.identity,\r\n relayUrl: this.config.relay.url,\r\n relayClient: this.relayClient,\r\n },\r\n peer.publicKey,\r\n options.type,\r\n options.payload,\r\n options.inReplyTo\r\n );\r\n\r\n return {\r\n ok: relayResult.ok,\r\n status: 0,\r\n error: relayResult.error,\r\n };\r\n }\r\n\r\n // Both failed\r\n return {\r\n ok: false,\r\n status: 0,\r\n error: peer.url\r\n ? `HTTP send failed and relay not available for peer: ${options.peerName}`\r\n : `No webhook URL and relay not available for peer: ${options.peerName}`,\r\n };\r\n }\r\n\r\n /**\r\n * Reply to an envelope from any sender via relay.\r\n * Unlike sendMessage(), this does NOT require the target to be a configured peer.\r\n * Uses the target's public key directly — relay-only (no HTTP, since unknown peers have no URL).\r\n */\r\n async replyToEnvelope(options: ReplyToEnvelopeOptions): Promise<SendMessageResult> {\r\n if (!this.relayClient?.connected() || !this.config.relay) {\r\n return {\r\n ok: false,\r\n status: 0,\r\n error: 'Relay not connected — cannot reply to envelope without relay',\r\n };\r\n }\r\n\r\n this.logger?.debug(\r\n `Replying to envelope via relay: target=${shortKey(options.targetPubkey)} type=${options.type} inReplyTo=${options.inReplyTo}`\r\n );\r\n\r\n const relayResult = await sendViaRelay(\r\n {\r\n identity: this.config.identity,\r\n relayUrl: this.config.relay.url,\r\n relayClient: this.relayClient,\r\n },\r\n options.targetPubkey,\r\n options.type,\r\n options.payload,\r\n options.inReplyTo\r\n );\r\n\r\n return {\r\n ok: relayResult.ok,\r\n status: 0,\r\n error: relayResult.error,\r\n };\r\n }\r\n\r\n /**\r\n * Decode and verify an inbound envelope from a webhook message.\r\n */\r\n async decodeInbound(message: string): Promise<DecodeInboundResult> {\r\n const peersByPubKey = new Map<string, PeerConfig>();\r\n for (const peer of this.config.peers.values()) {\r\n peersByPubKey.set(peer.publicKey, peer);\r\n }\r\n const result = decodeInboundEnvelope(message, peersByPubKey);\r\n if (result.ok) {\r\n return { ok: true, envelope: result.envelope };\r\n }\r\n return { ok: false, reason: result.reason };\r\n }\r\n\r\n getPeers(): string[] {\r\n return Array.from(this.config.peers.keys());\r\n }\r\n\r\n getPeerConfig(name: string): PeerConfig | undefined {\r\n return this.resolvePeer(name);\r\n }\r\n\r\n /**\r\n * Connect to the relay server.\r\n */\r\n async connectRelay(url: string): Promise<void> {\r\n if (this.relayClient) {\r\n return;\r\n }\r\n\r\n const maxReconnectDelay = this.config.relay?.reconnectMaxMs ?? 300000;\r\n let name = this.config.identity.name ?? this.config.relay?.name;\r\n // Never use the short key (id) as the relay display name; treat it as no name\r\n if (name && name === shortKey(this.config.identity.publicKey)) {\r\n name = undefined;\r\n }\r\n const opts = {\r\n relayUrl: url,\r\n publicKey: this.config.identity.publicKey,\r\n privateKey: this.config.identity.privateKey,\r\n name,\r\n pingInterval: 30000,\r\n maxReconnectDelay,\r\n };\r\n\r\n if (this.relayClientFactory) {\r\n this.relayClient = this.relayClientFactory(opts);\r\n } else {\r\n this.relayClient = new RelayClient(opts);\r\n }\r\n\r\n this.relayClient.on('error', (error: Error) => {\r\n this.logger?.debug(`Agora relay error: ${error.message}`);\r\n });\r\n\r\n this.relayClient.on('message', (envelope: Envelope, from: string, fromName?: string) => {\r\n this.onRelayMessage(envelope, from, fromName);\r\n });\r\n\r\n try {\r\n await this.relayClient.connect();\r\n } catch (error) {\r\n const message = error instanceof Error ? error.message : String(error);\r\n this.logger?.debug(`Agora relay connect failed (${url}): ${message}`);\r\n this.relayClient = null;\r\n }\r\n }\r\n\r\n async disconnectRelay(): Promise<void> {\r\n if (this.relayClient) {\r\n this.relayClient.disconnect();\r\n this.relayClient = null;\r\n }\r\n }\r\n\r\n isRelayConnected(): boolean {\r\n return this.relayClient?.connected() ?? false;\r\n }\r\n\r\n /**\r\n * Load Agora configuration and return service config (peers as Map).\r\n */\r\n static async loadConfig(path?: string): Promise<AgoraServiceConfig> {\r\n const configPath = path ?? getDefaultConfigPath();\r\n const loaded = await loadAgoraConfigAsync(configPath);\r\n\r\n const peers = new Map<string, PeerConfig>();\r\n for (const p of Object.values(loaded.peers)) {\r\n peers.set(p.publicKey, {\r\n publicKey: p.publicKey,\r\n url: p.url,\r\n token: p.token,\r\n name: p.name,\r\n } satisfies PeerConfig);\r\n }\r\n\r\n return {\r\n identity: loaded.identity,\r\n peers,\r\n relay: loaded.relay,\r\n };\r\n }\r\n}\r\n","/**\r\n * Network reputation query handler.\r\n * Handles incoming reputation_query messages and returns reputation_response.\r\n */\r\n\r\nimport type { ReputationQuery, ReputationResponse, TrustScore } from './types.js';\r\nimport { computeTrustScore, computeTrustScores } from './scoring.js';\r\nimport type { ReputationStore } from './store.js';\r\n\r\n/** Maximum number of verification records to include in a response */\r\nconst MAX_VERIFICATIONS_IN_RESPONSE = 50;\r\n\r\n/**\r\n * Handle an incoming reputation query by reading from the local store,\r\n * computing trust scores, and returning a response.\r\n *\r\n * @param query - The reputation query (agent, optional domain, optional after timestamp)\r\n * @param store - Local reputation store to read from\r\n * @param currentTime - Current timestamp (ms) for score computation\r\n * @returns Reputation response with computed scores and verification records\r\n */\r\nexport async function handleReputationQuery(\r\n query: ReputationQuery,\r\n store: ReputationStore,\r\n currentTime: number\r\n): Promise<ReputationResponse> {\r\n // Get all verifications from the store for score computation\r\n const allVerifications = await store.getVerifications();\r\n\r\n // Filter to verifications targeting the queried agent\r\n let relevantVerifications = allVerifications.filter(v => v.target === query.agent);\r\n\r\n // Apply domain filter if specified\r\n if (query.domain !== undefined) {\r\n relevantVerifications = relevantVerifications.filter(v => v.domain === query.domain);\r\n }\r\n\r\n // Apply after-timestamp filter if specified\r\n if (query.after !== undefined) {\r\n const after = query.after;\r\n relevantVerifications = relevantVerifications.filter(v => v.timestamp > after);\r\n }\r\n\r\n // Compute trust scores\r\n let scores: Record<string, TrustScore>;\r\n\r\n if (query.domain !== undefined) {\r\n const score = computeTrustScore(query.agent, query.domain, allVerifications, currentTime);\r\n scores = { [query.domain]: score };\r\n } else {\r\n const scoreMap = computeTrustScores(query.agent, allVerifications, currentTime);\r\n scores = {};\r\n for (const [domain, score] of scoreMap.entries()) {\r\n scores[domain] = score;\r\n }\r\n }\r\n\r\n // Size-limit verifications to most recent MAX_VERIFICATIONS_IN_RESPONSE\r\n const limitedVerifications = relevantVerifications\r\n .slice()\r\n .sort((a, b) => b.timestamp - a.timestamp)\r\n .slice(0, MAX_VERIFICATIONS_IN_RESPONSE);\r\n\r\n const response: ReputationResponse = {\r\n agent: query.agent,\r\n verifications: limitedVerifications,\r\n scores,\r\n };\r\n\r\n if (query.domain !== undefined) {\r\n response.domain = query.domain;\r\n }\r\n\r\n return response;\r\n}\r\n","/**\r\n * Cross-peer reputation synchronization.\r\n * Pull and merge verification records from trusted peers.\r\n */\r\n\r\nimport type { ReputationQuery, ReputationResponse } from './types.js';\r\nimport { verifyVerificationSignature } from './verification.js';\r\nimport type { ReputationStore } from './store.js';\r\n\r\n/**\r\n * Pull reputation data from a peer and merge it into the local store.\r\n *\r\n * Flow:\r\n * 1. Send `reputation_query` to peer via sendMessage\r\n * 2. Receive `reputation_response` with verification records\r\n * 3. For each record: verify signature, check domain matches, check not duplicate\r\n * 4. Append new records to local store\r\n * 5. Return count of added/skipped\r\n *\r\n * @param agentPublicKey - Public key of the agent whose reputation to sync\r\n * @param domain - Domain to sync reputation for\r\n * @param store - Local reputation store to merge records into\r\n * @param sendMessage - Function that sends a reputation_query and returns the response\r\n * @returns Counts of records added and skipped\r\n */\r\nexport async function syncReputationFromPeer(\r\n agentPublicKey: string,\r\n domain: string,\r\n store: ReputationStore,\r\n sendMessage: (type: string, payload: ReputationQuery) => Promise<ReputationResponse>\r\n): Promise<{ added: number; skipped: number }> {\r\n // Build and send the query\r\n const query: ReputationQuery = {\r\n agent: agentPublicKey,\r\n domain,\r\n };\r\n\r\n const response = await sendMessage('reputation_query', query);\r\n\r\n // Build set of existing record IDs for fast deduplication\r\n const existing = await store.getVerifications();\r\n const existingIds = new Set(existing.map(v => v.id));\r\n\r\n let added = 0;\r\n let skipped = 0;\r\n\r\n for (const record of response.verifications) {\r\n // Skip duplicate records (content-addressed by ID)\r\n if (existingIds.has(record.id)) {\r\n skipped++;\r\n continue;\r\n }\r\n\r\n // Verify cryptographic signature before accepting\r\n const sigResult = verifyVerificationSignature(record);\r\n if (!sigResult.valid) {\r\n skipped++;\r\n continue;\r\n }\r\n\r\n // Ensure the record's domain matches what we requested\r\n if (record.domain !== domain) {\r\n skipped++;\r\n continue;\r\n }\r\n\r\n // Add to local store and track for deduplication within this batch\r\n await store.addVerification(record);\r\n existingIds.add(record.id);\r\n added++;\r\n }\r\n\r\n return { added, skipped };\r\n}\r\n"],"mappings":"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","names":["readFileSync","existsSync","existsSync","readFileSync","createHash"]}
|
package/package.json
CHANGED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/transport/peer-config.ts","../src/transport/http.ts","../src/transport/relay.ts","../src/relay/client.ts","../src/discovery/peer-discovery.ts","../src/discovery/bootstrap.ts","../src/utils.ts","../src/reputation/types.ts","../src/reputation/store.ts","../src/reputation/verification.ts","../src/reputation/commit-reveal.ts","../src/reputation/scoring.ts"],"sourcesContent":["import { readFileSync, writeFileSync, existsSync } from 'node:fs';\nimport { generateKeyPair } from '../identity/keypair';\n\nexport interface PeerConfigFile {\n identity: {\n publicKey: string;\n privateKey: string;\n name?: string;\n };\n relay?: string | {\n url: string;\n name?: string;\n };\n peers: Record<string, {\n url?: string;\n token?: string;\n publicKey: string;\n name?: string;\n }>;\n}\n\n/**\n * Load peer configuration from a JSON file.\n * @param path - Path to the config file\n * @returns The parsed configuration\n * @throws Error if file doesn't exist or contains invalid JSON\n */\nexport function loadPeerConfig(path: string): PeerConfigFile {\n const content = readFileSync(path, 'utf-8');\n return JSON.parse(content) as PeerConfigFile;\n}\n\n/**\n * Save peer configuration to a JSON file.\n * @param path - Path to the config file\n * @param config - The configuration to save\n */\nexport function savePeerConfig(path: string, config: PeerConfigFile): void {\n const content = JSON.stringify(config, null, 2);\n writeFileSync(path, content, 'utf-8');\n}\n\n/**\n * Initialize peer configuration, generating a new keypair if the file doesn't exist.\n * If the file exists, loads and returns it.\n * @param path - Path to the config file\n * @returns The configuration (loaded or newly created)\n */\nexport function initPeerConfig(path: string): PeerConfigFile {\n if (existsSync(path)) {\n return loadPeerConfig(path);\n }\n\n // Generate new keypair and create initial config\n const identity = generateKeyPair();\n const config: PeerConfigFile = {\n identity,\n peers: {},\n };\n\n savePeerConfig(path, config);\n return config;\n}\n","import { createEnvelope, verifyEnvelope, type Envelope, type MessageType } from '../message/envelope';\r\n\r\nexport interface PeerConfig {\r\n /** Peer's webhook URL, e.g. http://localhost:18790/hooks (undefined for relay-only peers) */\r\n url?: string;\r\n /** Peer's webhook auth token (undefined for relay-only peers) */\r\n token?: string;\r\n /** Peer's public key (hex) for verifying responses */\r\n publicKey: string;\r\n /** Optional convenience alias only (not identity) */\r\n name?: string;\r\n}\r\n\r\nexport interface TransportConfig {\r\n /** This agent's keypair */\r\n identity: { publicKey: string; privateKey: string };\r\n /** Known peers */\r\n peers: Map<string, PeerConfig>;\r\n}\r\n\r\n/**\r\n * Send a signed envelope to a peer via HTTP webhook.\r\n * Creates the envelope, signs it, and POSTs to the peer's /hooks/agent endpoint.\r\n * Returns the HTTP status code.\r\n */\r\nexport async function sendToPeer(\r\n config: TransportConfig,\r\n peerPublicKey: string,\r\n type: MessageType,\r\n payload: unknown,\r\n inReplyTo?: string\r\n): Promise<{ ok: boolean; status: number; error?: string }> {\r\n // Look up peer config\r\n const peer = config.peers.get(peerPublicKey);\r\n if (!peer) {\r\n return { ok: false, status: 0, error: 'Unknown peer' };\r\n }\r\n\r\n // Relay-only peer — no webhook URL configured\r\n if (!peer.url) {\r\n return { ok: false, status: 0, error: 'No webhook URL configured' };\r\n }\r\n\r\n // Create and sign the envelope\r\n const envelope = createEnvelope(\r\n type,\r\n config.identity.publicKey,\r\n config.identity.privateKey,\r\n payload,\r\n Date.now(),\r\n inReplyTo,\r\n [peerPublicKey]\r\n );\r\n\r\n // Encode envelope as base64url\r\n const envelopeJson = JSON.stringify(envelope);\r\n const envelopeBase64 = Buffer.from(envelopeJson).toString('base64url');\r\n\r\n // Construct webhook payload\r\n const webhookPayload = {\r\n message: `[AGORA_ENVELOPE]${envelopeBase64}`,\r\n name: 'Agora',\r\n sessionKey: `agora:${envelope.from.substring(0, 16)}`,\r\n deliver: false,\r\n };\r\n\r\n // Build headers — only include Authorization when a token is configured\r\n const headers: Record<string, string> = {\r\n 'Content-Type': 'application/json',\r\n };\r\n if (peer.token) {\r\n headers['Authorization'] = `Bearer ${peer.token}`;\r\n }\r\n\r\n const requestBody = JSON.stringify(webhookPayload);\r\n\r\n // Send HTTP POST (retry once on network error, not on 4xx/5xx)\r\n for (let attempt = 0; attempt < 2; attempt++) {\r\n try {\r\n const response = await fetch(`${peer.url}/agent`, {\r\n method: 'POST',\r\n headers,\r\n body: requestBody,\r\n });\r\n\r\n return {\r\n ok: response.ok,\r\n status: response.status,\r\n error: response.ok ? undefined : await response.text(),\r\n };\r\n } catch (err) {\r\n if (attempt === 1) {\r\n return {\r\n ok: false,\r\n status: 0,\r\n error: err instanceof Error ? err.message : String(err),\r\n };\r\n }\r\n // First attempt failed with network error — retry once\r\n }\r\n }\r\n\r\n // Unreachable, but satisfies TypeScript\r\n return { ok: false, status: 0, error: 'Unexpected send failure' };\r\n}\r\n\r\n/**\r\n * Decode and verify an inbound Agora envelope from a webhook message.\r\n * Expects the message to start with [AGORA_ENVELOPE] followed by base64.\r\n * Returns the verified envelope or an error.\r\n */\r\nexport function decodeInboundEnvelope(\r\n message: string,\r\n knownPeers: Map<string, PeerConfig>\r\n): { ok: true; envelope: Envelope } | { ok: false; reason: string } {\r\n // Check for AGORA_ENVELOPE prefix\r\n const prefix = '[AGORA_ENVELOPE]';\r\n if (!message.startsWith(prefix)) {\r\n return { ok: false, reason: 'not_agora_message' };\r\n }\r\n\r\n // Extract base64 payload\r\n const base64Payload = message.substring(prefix.length);\r\n \r\n // Check for empty payload\r\n if (!base64Payload) {\r\n return { ok: false, reason: 'invalid_base64' };\r\n }\r\n \r\n // Decode base64\r\n let envelopeJson: string;\r\n try {\r\n const decoded = Buffer.from(base64Payload, 'base64url');\r\n // Check if decoded buffer is empty or contains invalid data\r\n if (decoded.length === 0) {\r\n return { ok: false, reason: 'invalid_base64' };\r\n }\r\n envelopeJson = decoded.toString('utf-8');\r\n } catch {\r\n return { ok: false, reason: 'invalid_base64' };\r\n }\r\n\r\n // Parse JSON\r\n let envelope: Envelope;\r\n try {\r\n envelope = JSON.parse(envelopeJson);\r\n } catch {\r\n return { ok: false, reason: 'invalid_json' };\r\n }\r\n\r\n // Verify envelope integrity\r\n const verification = verifyEnvelope(envelope);\r\n if (!verification.valid) {\r\n return { ok: false, reason: verification.reason || 'verification_failed' };\r\n }\r\n\r\n // Check if sender is a known peer\r\n const senderKnown = knownPeers.has(envelope.from);\r\n if (!senderKnown) {\r\n return { ok: false, reason: 'unknown_sender' };\r\n }\r\n\r\n return { ok: true, envelope };\r\n}\r\n","import WebSocket from 'ws';\nimport { createEnvelope, type Envelope, type MessageType } from '../message/envelope';\n\n/** Minimal interface for a connected relay client (avoids importing full RelayClient) */\nexport interface RelayClientSender {\n connected(): boolean;\n send(to: string, envelope: Envelope): Promise<{ ok: boolean; error?: string }>;\n}\n\nexport interface RelayTransportConfig {\n /** This agent's keypair */\n identity: { publicKey: string; privateKey: string };\n /** Relay server WebSocket URL (e.g., wss://agora-relay.lbsa71.net) */\n relayUrl: string;\n /** Optional persistent relay client (if provided, will use it instead of connect-per-message) */\n relayClient?: RelayClientSender;\n}\n\n/**\n * Send a signed envelope to a peer via relay server.\n * If a persistent relayClient is provided in the config, uses that.\n * Otherwise, connects to relay, registers, sends message, and disconnects.\n */\nexport async function sendViaRelay(\n config: RelayTransportConfig,\n peerPublicKey: string,\n type: MessageType,\n payload: unknown,\n inReplyTo?: string\n): Promise<{ ok: boolean; error?: string }> {\n // If a persistent relay client is available, use it\n if (config.relayClient && config.relayClient.connected()) {\n const envelope = createEnvelope(\n type,\n config.identity.publicKey,\n config.identity.privateKey,\n payload,\n Date.now(),\n inReplyTo,\n [peerPublicKey]\n );\n return config.relayClient.send(peerPublicKey, envelope);\n }\n\n // Otherwise, fall back to connect-per-message\n return new Promise((resolve) => {\n const ws = new WebSocket(config.relayUrl);\n let registered = false;\n let messageSent = false;\n let resolved = false;\n\n // Helper to resolve once\n const resolveOnce = (result: { ok: boolean; error?: string }): void => {\n if (!resolved) {\n resolved = true;\n clearTimeout(timeout);\n resolve(result);\n }\n };\n\n // Set timeout for the entire operation\n const timeout = setTimeout(() => {\n if (!messageSent) {\n ws.close();\n resolveOnce({ ok: false, error: 'Relay connection timeout' });\n }\n }, 10000); // 10 second timeout\n\n ws.on('open', () => {\n // Send register message\n const registerMsg = {\n type: 'register',\n publicKey: config.identity.publicKey,\n };\n ws.send(JSON.stringify(registerMsg));\n });\n\n ws.on('message', (data: WebSocket.Data) => {\n try {\n const msg = JSON.parse(data.toString());\n\n if (msg.type === 'registered' && !registered) {\n registered = true;\n\n // Create and sign the envelope\n const envelope: Envelope = createEnvelope(\n type,\n config.identity.publicKey,\n config.identity.privateKey,\n payload,\n Date.now(),\n inReplyTo,\n [peerPublicKey]\n );\n\n // Send message via relay\n const relayMsg = {\n type: 'message',\n to: peerPublicKey,\n envelope,\n };\n ws.send(JSON.stringify(relayMsg));\n messageSent = true;\n\n // Close connection after sending\n setTimeout(() => {\n ws.close();\n resolveOnce({ ok: true });\n }, 100); // Small delay to ensure message is sent\n } else if (msg.type === 'error') {\n ws.close();\n resolveOnce({ ok: false, error: msg.message || 'Relay server error' });\n }\n } catch (err) {\n ws.close();\n resolveOnce({ ok: false, error: err instanceof Error ? err.message : String(err) });\n }\n });\n\n ws.on('error', (err) => {\n ws.close();\n resolveOnce({ ok: false, error: err.message });\n });\n\n ws.on('close', () => {\n if (!messageSent) {\n resolveOnce({ ok: false, error: 'Connection closed before message sent' });\n }\n });\n });\n}\n","import { EventEmitter } from 'node:events';\nimport WebSocket from 'ws';\nimport { verifyEnvelope, type Envelope } from '../message/envelope';\nimport type { RelayClientMessage, RelayServerMessage, RelayPeer } from './types';\n\n/**\n * Configuration for RelayClient\n */\nexport interface RelayClientConfig {\n /** WebSocket URL of the relay server */\n relayUrl: string;\n /** Agent's public key */\n publicKey: string;\n /** Agent's private key (for signing) */\n privateKey: string;\n /** Optional name for this agent */\n name?: string;\n /** Keepalive ping interval in milliseconds (default: 30000) */\n pingInterval?: number;\n /** Maximum reconnection delay in milliseconds (default: 60000) */\n maxReconnectDelay?: number;\n}\n\n/**\n * Events emitted by RelayClient\n */\nexport interface RelayClientEvents {\n /** Emitted when successfully connected and registered */\n 'connected': () => void;\n /** Emitted when disconnected from relay */\n 'disconnected': () => void;\n /** Emitted when a verified message is received */\n 'message': (envelope: Envelope, from: string, fromName?: string) => void;\n /** Emitted when a peer comes online */\n 'peer_online': (peer: RelayPeer) => void;\n /** Emitted when a peer goes offline */\n 'peer_offline': (peer: RelayPeer) => void;\n /** Emitted on errors */\n 'error': (error: Error) => void;\n}\n\n/**\n * Persistent WebSocket client for the Agora relay server.\n * Maintains a long-lived connection, handles reconnection, and routes messages.\n */\nexport class RelayClient extends EventEmitter {\n private ws: WebSocket | null = null;\n private config: RelayClientConfig;\n private reconnectAttempts = 0;\n private reconnectTimeout: NodeJS.Timeout | null = null;\n private pingInterval: NodeJS.Timeout | null = null;\n private isConnected = false;\n private isRegistered = false;\n private shouldReconnect = true;\n private onlinePeers = new Map<string, RelayPeer>();\n\n constructor(config: RelayClientConfig) {\n super();\n this.config = {\n pingInterval: 30000,\n maxReconnectDelay: 60000,\n ...config,\n };\n }\n\n /**\n * Connect to the relay server\n */\n async connect(): Promise<void> {\n if (this.ws && (this.ws.readyState === WebSocket.CONNECTING || this.ws.readyState === WebSocket.OPEN)) {\n return;\n }\n\n this.shouldReconnect = true;\n return this.doConnect();\n }\n\n /**\n * Disconnect from the relay server\n */\n disconnect(): void {\n this.shouldReconnect = false;\n this.cleanup();\n if (this.ws) {\n this.ws.close();\n this.ws = null;\n }\n }\n\n /**\n * Check if currently connected and registered\n */\n connected(): boolean {\n return this.isConnected && this.isRegistered;\n }\n\n /**\n * Send a message to a specific peer\n */\n async send(to: string, envelope: Envelope): Promise<{ ok: boolean; error?: string }> {\n if (!this.connected()) {\n return { ok: false, error: 'Not connected to relay' };\n }\n\n const message: RelayClientMessage = {\n type: 'message',\n to,\n envelope,\n };\n\n try {\n this.ws!.send(JSON.stringify(message));\n return { ok: true };\n } catch (err) {\n return { ok: false, error: err instanceof Error ? err.message : String(err) };\n }\n }\n\n /**\n * Get list of currently online peers\n */\n getOnlinePeers(): RelayPeer[] {\n return Array.from(this.onlinePeers.values());\n }\n\n /**\n * Check if a specific peer is online\n */\n isPeerOnline(publicKey: string): boolean {\n return this.onlinePeers.has(publicKey);\n }\n\n /**\n * Internal: Perform connection\n */\n private async doConnect(): Promise<void> {\n return new Promise((resolve, reject) => {\n try {\n this.ws = new WebSocket(this.config.relayUrl);\n let resolved = false;\n\n const resolveOnce = (callback: () => void): void => {\n if (!resolved) {\n resolved = true;\n callback();\n }\n };\n\n this.ws.on('open', () => {\n this.isConnected = true;\n this.reconnectAttempts = 0;\n this.startPingInterval();\n\n // Send registration message\n const registerMsg: RelayClientMessage = {\n type: 'register',\n publicKey: this.config.publicKey,\n name: this.config.name,\n };\n this.ws!.send(JSON.stringify(registerMsg));\n });\n\n this.ws.on('message', (data: WebSocket.Data) => {\n try {\n const msg = JSON.parse(data.toString()) as RelayServerMessage;\n this.handleMessage(msg);\n\n // Resolve promise on successful registration\n if (msg.type === 'registered' && !resolved) {\n resolveOnce(() => resolve());\n }\n } catch (err) {\n this.emit('error', new Error(`Failed to parse message: ${err instanceof Error ? err.message : String(err)}`));\n }\n });\n\n this.ws.on('close', () => {\n this.isConnected = false;\n this.isRegistered = false;\n this.cleanup();\n this.emit('disconnected');\n\n if (this.shouldReconnect) {\n this.scheduleReconnect();\n }\n\n if (!resolved) {\n resolveOnce(() => reject(new Error('Connection closed before registration')));\n }\n });\n\n this.ws.on('error', (err) => {\n this.emit('error', err);\n if (!resolved) {\n resolveOnce(() => reject(err));\n }\n });\n } catch (err) {\n reject(err);\n }\n });\n }\n\n /**\n * Handle incoming message from relay\n */\n private handleMessage(msg: RelayServerMessage): void {\n switch (msg.type) {\n case 'registered':\n this.isRegistered = true;\n if (msg.peers) {\n // Populate initial peer list\n for (const peer of msg.peers) {\n this.onlinePeers.set(peer.publicKey, peer);\n }\n }\n this.emit('connected');\n break;\n\n case 'message':\n if (msg.envelope && msg.from) {\n // Verify envelope signature\n const verification = verifyEnvelope(msg.envelope);\n if (!verification.valid) {\n this.emit('error', new Error(`Invalid envelope signature: ${verification.reason}`));\n return;\n }\n\n // Verify sender matches 'from' field\n const envelopeFrom = msg.envelope.from;\n if (envelopeFrom !== msg.from) {\n this.emit('error', new Error('Envelope sender does not match relay from field'));\n return;\n }\n\n // Emit verified message\n this.emit('message', msg.envelope, msg.from, msg.name);\n }\n break;\n\n case 'peer_online':\n if (msg.publicKey) {\n const peer: RelayPeer = {\n publicKey: msg.publicKey,\n name: msg.name,\n };\n this.onlinePeers.set(msg.publicKey, peer);\n this.emit('peer_online', peer);\n }\n break;\n\n case 'peer_offline':\n if (msg.publicKey) {\n const peer = this.onlinePeers.get(msg.publicKey);\n if (peer) {\n this.onlinePeers.delete(msg.publicKey);\n this.emit('peer_offline', peer);\n }\n }\n break;\n\n case 'error':\n this.emit('error', new Error(`Relay error: ${msg.message || 'Unknown error'}`));\n break;\n\n case 'pong':\n // Keepalive response, no action needed\n break;\n\n default:\n // Unknown message type, ignore\n break;\n }\n }\n\n /**\n * Schedule reconnection with exponential backoff\n */\n private scheduleReconnect(): void {\n if (this.reconnectTimeout) {\n return;\n }\n\n // Exponential backoff: 1s, 2s, 4s, 8s, 16s, 32s, 60s (max)\n const delay = Math.min(\n 1000 * Math.pow(2, this.reconnectAttempts),\n this.config.maxReconnectDelay!\n );\n\n this.reconnectAttempts++;\n\n this.reconnectTimeout = setTimeout(() => {\n this.reconnectTimeout = null;\n if (this.shouldReconnect) {\n this.doConnect().catch((err) => {\n this.emit('error', err);\n });\n }\n }, delay);\n }\n\n /**\n * Start periodic ping messages\n */\n private startPingInterval(): void {\n this.stopPingInterval();\n this.pingInterval = setInterval(() => {\n if (this.ws && this.ws.readyState === WebSocket.OPEN) {\n const ping: RelayClientMessage = { type: 'ping' };\n this.ws.send(JSON.stringify(ping));\n }\n }, this.config.pingInterval!);\n }\n\n /**\n * Stop ping interval\n */\n private stopPingInterval(): void {\n if (this.pingInterval) {\n clearInterval(this.pingInterval);\n this.pingInterval = null;\n }\n }\n\n /**\n * Cleanup resources\n */\n private cleanup(): void {\n this.stopPingInterval();\n if (this.reconnectTimeout) {\n clearTimeout(this.reconnectTimeout);\n this.reconnectTimeout = null;\n }\n this.onlinePeers.clear();\n }\n}\n","import { EventEmitter } from 'node:events';\nimport { createEnvelope, verifyEnvelope, type Envelope } from '../message/envelope';\nimport type { RelayClient } from '../relay/client';\nimport type { PeerListRequestPayload, PeerListResponsePayload, PeerReferralPayload } from '../message/types/peer-discovery';\n\n/**\n * Configuration for PeerDiscoveryService\n */\nexport interface PeerDiscoveryConfig {\n /** Agent's public key */\n publicKey: string;\n /** Agent's private key for signing */\n privateKey: string;\n /** RelayClient instance for communication */\n relayClient: RelayClient;\n /** Public key of the relay server (for sending peer list requests) */\n relayPublicKey?: string;\n}\n\n/**\n * Events emitted by PeerDiscoveryService\n */\nexport interface PeerDiscoveryEvents {\n /** Emitted when peers are discovered */\n 'peers-discovered': (peers: PeerListResponsePayload['peers']) => void;\n /** Emitted when a peer referral is received */\n 'peer-referral': (referral: PeerReferralPayload, from: string) => void;\n /** Emitted on errors */\n 'error': (error: Error) => void;\n}\n\n/**\n * Service for discovering peers on the Agora network\n */\nexport class PeerDiscoveryService extends EventEmitter {\n private config: PeerDiscoveryConfig;\n\n constructor(config: PeerDiscoveryConfig) {\n super();\n this.config = config;\n\n // Listen for peer list responses and referrals\n this.config.relayClient.on('message', (envelope: Envelope, from: string) => {\n if (envelope.type === 'peer_list_response') {\n this.handlePeerList(envelope as Envelope<PeerListResponsePayload>);\n } else if (envelope.type === 'peer_referral') {\n this.handleReferral(envelope as Envelope<PeerReferralPayload>, from);\n }\n });\n }\n\n /**\n * Request peer list from relay\n */\n async discoverViaRelay(filters?: PeerListRequestPayload['filters']): Promise<PeerListResponsePayload | null> {\n if (!this.config.relayPublicKey) {\n throw new Error('Relay public key not configured');\n }\n\n if (!this.config.relayClient.connected()) {\n throw new Error('Not connected to relay');\n }\n\n const payload: PeerListRequestPayload = filters ? { filters } : {};\n\n const envelope = createEnvelope(\n 'peer_list_request',\n this.config.publicKey,\n this.config.privateKey,\n payload,\n Date.now(),\n undefined,\n [this.config.relayPublicKey]\n );\n\n // Send request to relay\n const result = await this.config.relayClient.send(this.config.relayPublicKey, envelope);\n if (!result.ok) {\n throw new Error(`Failed to send peer list request: ${result.error}`);\n }\n\n // Wait for response (with timeout)\n return new Promise((resolve, reject) => {\n const timeout = setTimeout(() => {\n cleanup();\n reject(new Error('Peer list request timed out'));\n }, 10000); // 10 second timeout\n\n const messageHandler = (responseEnvelope: Envelope, from: string): void => {\n if (responseEnvelope.type === 'peer_list_response' && \n responseEnvelope.inReplyTo === envelope.id &&\n from === this.config.relayPublicKey) {\n cleanup();\n resolve(responseEnvelope.payload as PeerListResponsePayload);\n }\n };\n\n const cleanup = (): void => {\n clearTimeout(timeout);\n this.config.relayClient.off('message', messageHandler);\n };\n\n this.config.relayClient.on('message', messageHandler);\n });\n }\n\n /**\n * Send peer referral to another agent\n */\n async referPeer(\n recipientPublicKey: string,\n referredPublicKey: string,\n metadata?: { name?: string; endpoint?: string; comment?: string; trustScore?: number }\n ): Promise<{ ok: boolean; error?: string }> {\n if (!this.config.relayClient.connected()) {\n return { ok: false, error: 'Not connected to relay' };\n }\n\n const payload: PeerReferralPayload = {\n publicKey: referredPublicKey,\n endpoint: metadata?.endpoint,\n metadata: metadata?.name ? { name: metadata.name } : undefined,\n comment: metadata?.comment,\n trustScore: metadata?.trustScore,\n };\n\n const envelope = createEnvelope(\n 'peer_referral',\n this.config.publicKey,\n this.config.privateKey,\n payload,\n Date.now(),\n undefined,\n [recipientPublicKey]\n );\n\n return this.config.relayClient.send(recipientPublicKey, envelope);\n }\n\n /**\n * Handle incoming peer referral\n */\n private handleReferral(envelope: Envelope<PeerReferralPayload>, from: string): void {\n // Verify envelope\n const verification = verifyEnvelope(envelope);\n if (!verification.valid) {\n this.emit('error', new Error(`Invalid peer referral: ${verification.reason}`));\n return;\n }\n\n // Emit event for application to handle\n this.emit('peer-referral', envelope.payload, from);\n }\n\n /**\n * Handle incoming peer list from relay\n */\n private handlePeerList(envelope: Envelope<PeerListResponsePayload>): void {\n // Verify envelope\n const verification = verifyEnvelope(envelope);\n if (!verification.valid) {\n this.emit('error', new Error(`Invalid peer list response: ${verification.reason}`));\n return;\n }\n\n // Verify sender is the relay\n if (envelope.from !== this.config.relayPublicKey) {\n this.emit('error', new Error('Peer list response not from configured relay'));\n return;\n }\n\n // Emit event\n this.emit('peers-discovered', envelope.payload.peers);\n }\n}\n","/**\r\n * Bootstrap configuration for peer discovery on the Agora network.\r\n * Provides default bootstrap relays for initial network entry.\r\n */\r\n\r\n/**\r\n * Default bootstrap relay servers\r\n * These are well-known relays that serve as initial entry points to the network\r\n */\r\nexport const DEFAULT_BOOTSTRAP_RELAYS = [\r\n {\r\n url: 'wss://agora-relay.lbsa71.net',\r\n name: 'Primary Bootstrap Relay',\r\n // Note: Public key would need to be set when the relay is actually deployed\r\n // For now, this is a placeholder that would be configured when the relay is running\r\n },\r\n];\r\n\r\n/**\r\n * Configuration for bootstrap connection\r\n */\r\nexport interface BootstrapConfig {\r\n /** Bootstrap relay URL */\r\n relayUrl: string;\r\n /** Optional relay public key (for verification) */\r\n relayPublicKey?: string;\r\n /** Connection timeout in ms (default: 10000) */\r\n timeout?: number;\r\n}\r\n\r\n/**\r\n * Get default bootstrap relay configuration\r\n */\r\nexport function getDefaultBootstrapRelay(): BootstrapConfig {\r\n return {\r\n relayUrl: DEFAULT_BOOTSTRAP_RELAYS[0].url,\r\n timeout: 10000,\r\n };\r\n}\r\n\r\n/**\r\n * Parse bootstrap relay URL and optional public key\r\n */\r\nexport function parseBootstrapRelay(url: string, publicKey?: string): BootstrapConfig {\r\n return {\r\n relayUrl: url,\r\n relayPublicKey: publicKey,\r\n timeout: 10000,\r\n };\r\n}\r\n","/**\r\n * Get a short display version of a public key using the last 8 characters.\r\n * Ed25519 public keys all share the same OID prefix, so the last 8 characters\r\n * are more distinguishable than the first 8.\r\n *\r\n * @param publicKey - The full public key hex string\r\n * @returns \"...\" followed by the last 8 characters of the key\r\n */\r\nexport function shortKey(publicKey: string): string {\r\n return \"...\" + publicKey.slice(-8);\r\n}\r\n\r\nexport interface PeerReferenceEntry {\r\n publicKey: string;\r\n name?: string;\r\n}\r\n\r\nexport type PeerReferenceDirectory =\r\n | Record<string, PeerReferenceEntry>\r\n | Map<string, PeerReferenceEntry>\r\n | PeerReferenceEntry[];\r\n\r\nfunction toDirectoryEntries(directory?: PeerReferenceDirectory): PeerReferenceEntry[] {\r\n if (!directory) {\r\n return [];\r\n }\r\n if (Array.isArray(directory)) {\r\n return directory.filter((p) => typeof p.publicKey === 'string' && p.publicKey.length > 0);\r\n }\r\n if (directory instanceof Map) {\r\n return Array.from(directory.values()).filter((p) => typeof p.publicKey === 'string' && p.publicKey.length > 0);\r\n }\r\n return Object.values(directory).filter((p) => typeof p.publicKey === 'string' && p.publicKey.length > 0);\r\n}\r\n\r\nfunction findById(id: string, directory?: PeerReferenceDirectory): PeerReferenceEntry | undefined {\r\n return toDirectoryEntries(directory).find((entry) => entry.publicKey === id);\r\n}\r\n\r\n/**\r\n * Shorten a full peer ID for display/reference.\r\n * Canonical form:\r\n * - Configured name => \"name...<last8>\"\r\n * - Unknown/no-name => \"...<last8>\"\r\n */\r\nexport function shorten(id: string, directory?: PeerReferenceDirectory): string {\r\n const suffix = id.slice(-8);\r\n const entry = findById(id, directory);\r\n if (!entry?.name) {\r\n return `...${suffix}`;\r\n }\r\n return `${entry.name}...${suffix}`;\r\n}\r\n\r\n/**\r\n * Expand a short peer reference to a full ID.\r\n * Supports: full ID, unique name, ...last8, and name...last8.\r\n */\r\nexport function expand(shortId: string, directory: PeerReferenceDirectory): string | undefined {\r\n const entries = toDirectoryEntries(directory);\r\n if (entries.length === 0) {\r\n return undefined;\r\n }\r\n\r\n const token = shortId.trim();\r\n const direct = entries.find((entry) => entry.publicKey === token);\r\n if (direct) {\r\n return direct.publicKey;\r\n }\r\n\r\n const namedWithSuffix = token.match(/^(.+)\\.\\.\\.([0-9a-fA-F]{8})$/);\r\n if (namedWithSuffix) {\r\n const [, name, suffix] = namedWithSuffix;\r\n const matches = entries.filter((entry) => entry.name === name && entry.publicKey.toLowerCase().endsWith(suffix.toLowerCase()));\r\n if (matches.length === 1) {\r\n return matches[0].publicKey;\r\n }\r\n return undefined;\r\n }\r\n\r\n const suffixOnly = token.match(/^\\.\\.\\.([0-9a-fA-F]{8})$/);\r\n if (suffixOnly) {\r\n const [, suffix] = suffixOnly;\r\n const matches = entries.filter((entry) => entry.publicKey.toLowerCase().endsWith(suffix.toLowerCase()));\r\n if (matches.length === 1) {\r\n return matches[0].publicKey;\r\n }\r\n return undefined;\r\n }\r\n\r\n const byName = entries.filter((entry) => entry.name === token);\r\n if (byName.length === 1) {\r\n return byName[0].publicKey;\r\n }\r\n\r\n return undefined;\r\n}\r\n\r\n/**\r\n * Expand inline @references in text to full IDs using configured peers.\r\n */\r\nexport function expandInlineReferences(text: string, directory: PeerReferenceDirectory): string {\r\n return text.replace(/@([^\\s]+)/g, (_full, token: string) => {\r\n const resolved = expand(token, directory);\r\n return resolved ? `@${resolved}` : `@${token}`;\r\n });\r\n}\r\n\r\n/**\r\n * Compact inline @<full-id> references for rendering.\r\n */\r\nexport function compactInlineReferences(text: string, directory: PeerReferenceDirectory): string {\r\n return text.replace(/@([0-9a-fA-F]{16,})/g, (_full, id: string) => `@${shorten(id, directory)}`);\r\n}\r\n\r\n/**\r\n * Strip characters that can crash downstream width/segmenter logic in UIs.\r\n * Removes control chars (except newline/tab) and replaces lone surrogates.\r\n */\r\nexport function sanitizeText(text: string): string {\r\n return text\r\n .replace(/[\\u0000-\\u0008\\u000B\\u000C\\u000E-\\u001F\\u007F-\\u009F]/g, '')\r\n .replace(/[\\uD800-\\uDBFF](?![\\uDC00-\\uDFFF])/g, '\\uFFFD')\r\n .replace(/(?<![\\uD800-\\uDBFF])[\\uDC00-\\uDFFF]/g, '\\uFFFD');\r\n}\r\n\r\n/**\r\n * Resolve a display name for a peer.\r\n * Priority order:\r\n * 1. configured name in directory for the exact public key\r\n * 2. relay-broadcast name (if not already a short-id token)\r\n * 3. undefined\r\n */\r\nexport function resolveDisplayName(\r\n publicKey: string,\r\n peerName: string | undefined,\r\n directory?: PeerReferenceDirectory,\r\n): string | undefined {\r\n const entry = findById(publicKey, directory);\r\n if (entry?.name) {\r\n return entry.name;\r\n }\r\n\r\n if (peerName && !peerName.startsWith('...')) {\r\n return sanitizeText(peerName);\r\n }\r\n\r\n return undefined;\r\n}\r\n\r\n/**\r\n * Resolves the name to broadcast when connecting to a relay.\r\n * Priority order:\r\n * 1. CLI --name flag\r\n * 2. config.relay.name (if relay is an object with name property)\r\n * 3. config.identity.name\r\n * 4. undefined (no name broadcast)\r\n *\r\n * @param config - The Agora configuration (or compatible config with identity and optional relay)\r\n * @param cliName - Optional name from CLI --name flag\r\n * @returns The resolved name to broadcast, or undefined if none available\r\n */\r\nexport function resolveBroadcastName(\r\n config: { identity: { name?: string }; relay?: { name?: string } | string },\r\n cliName?: string\r\n): string | undefined {\r\n // Priority 1: CLI --name flag\r\n if (cliName) {\r\n return cliName;\r\n }\r\n\r\n // Priority 2: config.relay.name (if relay is an object with name property)\r\n if (config.relay) {\r\n if (typeof config.relay === 'object' && config.relay.name) {\r\n return config.relay.name;\r\n }\r\n }\r\n\r\n // Priority 3: config.identity.name\r\n if (config.identity.name) {\r\n return config.identity.name;\r\n }\r\n\r\n // Priority 4: No name available\r\n return undefined;\r\n}\r\n\r\n/**\r\n * Formats a display name with short ID postfix.\r\n * If name exists: \"name (...3f8c2247)\"\r\n * If no name: \"...3f8c2247\" (short ID only)\r\n *\r\n * @param name - Optional name to display (should not be a short ID)\r\n * @param publicKey - The public key to use for short ID\r\n * @returns Formatted display string\r\n */\r\nexport function formatDisplayName(name: string | undefined, publicKey: string): string {\r\n const shortId = shortKey(publicKey);\r\n // If name is undefined, empty, or is already a short ID, return only short ID\r\n if (!name || name.trim() === '' || name.startsWith('...')) {\r\n return shortId;\r\n }\r\n return `${name} (${shortId})`;\r\n}\r\n","/**\r\n * Core data structures for the Agora reputation layer.\r\n * Phase 1: Verification records, commit-reveal patterns, and trust scoring.\r\n */\r\n\r\n/**\r\n * A cryptographically signed verification of another agent's output or claim.\r\n * Core primitive for building computational reputation.\r\n */\r\nexport interface VerificationRecord {\r\n /** Content-addressed ID (hash of canonical JSON) */\r\n id: string;\r\n \r\n /** Public key of verifying agent */\r\n verifier: string;\r\n \r\n /** ID of message/output being verified */\r\n target: string;\r\n \r\n /** Capability domain (e.g., 'ocr', 'summarization', 'code_review') */\r\n domain: string;\r\n \r\n /** Verification verdict */\r\n verdict: 'correct' | 'incorrect' | 'disputed';\r\n \r\n /** Verifier's confidence in their check (0-1) */\r\n confidence: number;\r\n \r\n /** Optional link to independent verification data */\r\n evidence?: string;\r\n \r\n /** Unix timestamp (ms) */\r\n timestamp: number;\r\n \r\n /** Ed25519 signature over canonical JSON */\r\n signature: string;\r\n}\r\n\r\n/**\r\n * A commitment to a prediction before outcome is known.\r\n * Prevents post-hoc editing of predictions.\r\n */\r\nexport interface CommitRecord {\r\n /** Content-addressed ID */\r\n id: string;\r\n \r\n /** Public key of committing agent */\r\n agent: string;\r\n \r\n /** Domain of prediction */\r\n domain: string;\r\n \r\n /** SHA-256 hash of prediction string */\r\n commitment: string;\r\n \r\n /** Unix timestamp (ms) */\r\n timestamp: number;\r\n \r\n /** Expiry time (ms) - commitment invalid after this */\r\n expiry: number;\r\n \r\n /** Ed25519 signature */\r\n signature: string;\r\n}\r\n\r\n/**\r\n * Reveals the prediction and outcome after commitment expiry.\r\n * Enables verification of prediction accuracy.\r\n */\r\nexport interface RevealRecord {\r\n /** Content-addressed ID */\r\n id: string;\r\n \r\n /** Public key of revealing agent */\r\n agent: string;\r\n \r\n /** ID of original commit message */\r\n commitmentId: string;\r\n \r\n /** Original prediction (plaintext) */\r\n prediction: string;\r\n \r\n /** Observed outcome */\r\n outcome: string;\r\n \r\n /** Evidence for outcome (optional) */\r\n evidence?: string;\r\n \r\n /** Unix timestamp (ms) */\r\n timestamp: number;\r\n \r\n /** Ed25519 signature */\r\n signature: string;\r\n}\r\n\r\n/**\r\n * Computed reputation score for an agent in a specific domain.\r\n * Derived from verification history, not stored directly.\r\n */\r\nexport interface TrustScore {\r\n /** Public key of agent being scored */\r\n agent: string;\r\n \r\n /** Domain of reputation */\r\n domain: string;\r\n \r\n /** Computed score (0-1, where 1 = highest trust) */\r\n score: number;\r\n \r\n /** Number of verifications considered */\r\n verificationCount: number;\r\n \r\n /** Timestamp of most recent verification (ms) */\r\n lastVerified: number;\r\n \r\n /** Public keys of top verifiers (by weight) */\r\n topVerifiers: string[];\r\n}\r\n\r\n/**\r\n * Request for reputation data about a specific agent.\r\n */\r\nexport interface ReputationQuery {\r\n /** Public key of agent being queried */\r\n agent: string;\r\n \r\n /** Optional: filter by capability domain */\r\n domain?: string;\r\n \r\n /** Optional: only include verifications after this timestamp */\r\n after?: number;\r\n}\r\n\r\n/**\r\n * Response containing reputation data for a queried agent.\r\n */\r\nexport interface ReputationResponse {\r\n /** Public key of agent being reported on */\r\n agent: string;\r\n \r\n /** Domain filter (if requested) */\r\n domain?: string;\r\n \r\n /** Verification records matching the query */\r\n verifications: VerificationRecord[];\r\n \r\n /** Computed trust scores by domain */\r\n scores: Record<string, TrustScore>;\r\n}\r\n\r\n/**\r\n * Revocation of a previously issued verification.\r\n * Used when a verifier discovers their verification was incorrect.\r\n */\r\nexport interface RevocationRecord {\r\n /** Content-addressed ID of this revocation */\r\n id: string;\r\n \r\n /** Public key of agent revoking (must match original verifier) */\r\n verifier: string;\r\n \r\n /** ID of verification being revoked */\r\n verificationId: string;\r\n \r\n /** Reason for revocation */\r\n reason: string;\r\n \r\n /** Unix timestamp (ms) */\r\n timestamp: number;\r\n \r\n /** Ed25519 signature */\r\n signature: string;\r\n}\r\n\r\n/**\r\n * Validation result structure\r\n */\r\nexport interface ValidationResult {\r\n valid: boolean;\r\n errors: string[];\r\n}\r\n\r\n/**\r\n * Validate a verification record structure\r\n */\r\nexport function validateVerificationRecord(record: unknown): ValidationResult {\r\n const errors: string[] = [];\r\n \r\n if (typeof record !== 'object' || record === null) {\r\n return { valid: false, errors: ['Record must be an object'] };\r\n }\r\n \r\n const r = record as Record<string, unknown>;\r\n \r\n if (typeof r.id !== 'string' || r.id.length === 0) {\r\n errors.push('id must be a non-empty string');\r\n }\r\n \r\n if (typeof r.verifier !== 'string' || r.verifier.length === 0) {\r\n errors.push('verifier must be a non-empty string');\r\n }\r\n \r\n if (typeof r.target !== 'string' || r.target.length === 0) {\r\n errors.push('target must be a non-empty string');\r\n }\r\n \r\n if (typeof r.domain !== 'string' || r.domain.length === 0) {\r\n errors.push('domain must be a non-empty string');\r\n }\r\n \r\n if (!['correct', 'incorrect', 'disputed'].includes(r.verdict as string)) {\r\n errors.push('verdict must be one of: correct, incorrect, disputed');\r\n }\r\n \r\n if (typeof r.confidence !== 'number' || r.confidence < 0 || r.confidence > 1) {\r\n errors.push('confidence must be a number between 0 and 1');\r\n }\r\n \r\n if (r.evidence !== undefined && typeof r.evidence !== 'string') {\r\n errors.push('evidence must be a string if provided');\r\n }\r\n \r\n if (typeof r.timestamp !== 'number' || r.timestamp <= 0) {\r\n errors.push('timestamp must be a positive number');\r\n }\r\n \r\n if (typeof r.signature !== 'string' || r.signature.length === 0) {\r\n errors.push('signature must be a non-empty string');\r\n }\r\n \r\n return { valid: errors.length === 0, errors };\r\n}\r\n\r\n/**\r\n * Validate a commit record structure\r\n */\r\nexport function validateCommitRecord(record: unknown): ValidationResult {\r\n const errors: string[] = [];\r\n \r\n if (typeof record !== 'object' || record === null) {\r\n return { valid: false, errors: ['Record must be an object'] };\r\n }\r\n \r\n const r = record as Record<string, unknown>;\r\n \r\n if (typeof r.id !== 'string' || r.id.length === 0) {\r\n errors.push('id must be a non-empty string');\r\n }\r\n \r\n if (typeof r.agent !== 'string' || r.agent.length === 0) {\r\n errors.push('agent must be a non-empty string');\r\n }\r\n \r\n if (typeof r.domain !== 'string' || r.domain.length === 0) {\r\n errors.push('domain must be a non-empty string');\r\n }\r\n \r\n if (typeof r.commitment !== 'string' || r.commitment.length !== 64) {\r\n errors.push('commitment must be a 64-character hex string (SHA-256 hash)');\r\n }\r\n \r\n if (typeof r.timestamp !== 'number' || r.timestamp <= 0) {\r\n errors.push('timestamp must be a positive number');\r\n }\r\n \r\n if (typeof r.expiry !== 'number' || r.expiry <= 0) {\r\n errors.push('expiry must be a positive number');\r\n }\r\n \r\n if (typeof r.expiry === 'number' && typeof r.timestamp === 'number' && r.expiry <= r.timestamp) {\r\n errors.push('expiry must be after timestamp');\r\n }\r\n \r\n if (typeof r.signature !== 'string' || r.signature.length === 0) {\r\n errors.push('signature must be a non-empty string');\r\n }\r\n \r\n return { valid: errors.length === 0, errors };\r\n}\r\n\r\n/**\r\n * Validate a reveal record structure\r\n */\r\nexport function validateRevealRecord(record: unknown): ValidationResult {\r\n const errors: string[] = [];\r\n \r\n if (typeof record !== 'object' || record === null) {\r\n return { valid: false, errors: ['Record must be an object'] };\r\n }\r\n \r\n const r = record as Record<string, unknown>;\r\n \r\n if (typeof r.id !== 'string' || r.id.length === 0) {\r\n errors.push('id must be a non-empty string');\r\n }\r\n \r\n if (typeof r.agent !== 'string' || r.agent.length === 0) {\r\n errors.push('agent must be a non-empty string');\r\n }\r\n \r\n if (typeof r.commitmentId !== 'string' || r.commitmentId.length === 0) {\r\n errors.push('commitmentId must be a non-empty string');\r\n }\r\n \r\n if (typeof r.prediction !== 'string' || r.prediction.length === 0) {\r\n errors.push('prediction must be a non-empty string');\r\n }\r\n \r\n if (typeof r.outcome !== 'string' || r.outcome.length === 0) {\r\n errors.push('outcome must be a non-empty string');\r\n }\r\n \r\n if (r.evidence !== undefined && typeof r.evidence !== 'string') {\r\n errors.push('evidence must be a string if provided');\r\n }\r\n \r\n if (typeof r.timestamp !== 'number' || r.timestamp <= 0) {\r\n errors.push('timestamp must be a positive number');\r\n }\r\n \r\n if (typeof r.signature !== 'string' || r.signature.length === 0) {\r\n errors.push('signature must be a non-empty string');\r\n }\r\n \r\n return { valid: errors.length === 0, errors };\r\n}\r\n","/**\n * Local reputation store using JSONL append-only log.\n * Stores verification records, commits, and reveals.\n */\n\nimport { promises as fs } from 'node:fs';\nimport { dirname } from 'node:path';\nimport type { VerificationRecord, CommitRecord, RevealRecord } from './types';\nimport { validateVerificationRecord, validateCommitRecord, validateRevealRecord } from './types';\n\n/**\n * Record type discriminator for JSONL storage\n */\ntype StoredRecord = \n | ({ type: 'verification' } & VerificationRecord)\n | ({ type: 'commit' } & CommitRecord)\n | ({ type: 'reveal' } & RevealRecord);\n\n/**\n * Reputation store with JSONL persistence\n */\nexport class ReputationStore {\n private filePath: string;\n private verifications: Map<string, VerificationRecord> = new Map();\n private commits: Map<string, CommitRecord> = new Map();\n private reveals: Map<string, RevealRecord> = new Map();\n private loaded = false;\n\n constructor(filePath: string) {\n this.filePath = filePath;\n }\n\n /**\n * Load records from JSONL file\n */\n async load(): Promise<void> {\n try {\n const content = await fs.readFile(this.filePath, 'utf-8');\n const lines = content.trim().split('\\n').filter(line => line.length > 0);\n \n for (const line of lines) {\n try {\n const record = JSON.parse(line) as StoredRecord;\n \n switch (record.type) {\n case 'verification': {\n // eslint-disable-next-line @typescript-eslint/no-unused-vars\n const { type: _type, ...verification } = record;\n const validation = validateVerificationRecord(verification);\n if (validation.valid) {\n this.verifications.set(verification.id, verification as VerificationRecord);\n }\n break;\n }\n case 'commit': {\n // eslint-disable-next-line @typescript-eslint/no-unused-vars\n const { type: _type, ...commit } = record;\n const validation = validateCommitRecord(commit);\n if (validation.valid) {\n this.commits.set(commit.id, commit as CommitRecord);\n }\n break;\n }\n case 'reveal': {\n // eslint-disable-next-line @typescript-eslint/no-unused-vars\n const { type: _type, ...reveal } = record;\n const validation = validateRevealRecord(reveal);\n if (validation.valid) {\n this.reveals.set(reveal.id, reveal as RevealRecord);\n }\n break;\n }\n }\n } catch {\n // Skip invalid lines\n continue;\n }\n }\n \n this.loaded = true;\n } catch (error) {\n // File doesn't exist yet - that's okay for first run\n if ((error as NodeJS.ErrnoException).code === 'ENOENT') {\n this.loaded = true;\n return;\n }\n throw error;\n }\n }\n\n /**\n * Ensure the store is loaded\n */\n private async ensureLoaded(): Promise<void> {\n if (!this.loaded) {\n await this.load();\n }\n }\n\n /**\n * Append a record to the JSONL file\n */\n private async appendToFile(record: StoredRecord): Promise<void> {\n // Ensure directory exists\n await fs.mkdir(dirname(this.filePath), { recursive: true });\n \n // Append JSONL line\n const line = JSON.stringify(record) + '\\n';\n await fs.appendFile(this.filePath, line, 'utf-8');\n }\n\n /**\n * Add a verification record\n */\n async addVerification(verification: VerificationRecord): Promise<void> {\n await this.ensureLoaded();\n \n const validation = validateVerificationRecord(verification);\n if (!validation.valid) {\n throw new Error(`Invalid verification: ${validation.errors.join(', ')}`);\n }\n \n this.verifications.set(verification.id, verification);\n await this.appendToFile({ type: 'verification', ...verification });\n }\n\n /**\n * Add a commit record\n */\n async addCommit(commit: CommitRecord): Promise<void> {\n await this.ensureLoaded();\n \n const validation = validateCommitRecord(commit);\n if (!validation.valid) {\n throw new Error(`Invalid commit: ${validation.errors.join(', ')}`);\n }\n \n this.commits.set(commit.id, commit);\n await this.appendToFile({ type: 'commit', ...commit });\n }\n\n /**\n * Add a reveal record\n */\n async addReveal(reveal: RevealRecord): Promise<void> {\n await this.ensureLoaded();\n \n const validation = validateRevealRecord(reveal);\n if (!validation.valid) {\n throw new Error(`Invalid reveal: ${validation.errors.join(', ')}`);\n }\n \n this.reveals.set(reveal.id, reveal);\n await this.appendToFile({ type: 'reveal', ...reveal });\n }\n\n /**\n * Get all verifications\n */\n async getVerifications(): Promise<VerificationRecord[]> {\n await this.ensureLoaded();\n return Array.from(this.verifications.values());\n }\n\n /**\n * Get verifications for a specific target\n */\n async getVerificationsByTarget(target: string): Promise<VerificationRecord[]> {\n await this.ensureLoaded();\n return Array.from(this.verifications.values()).filter(v => v.target === target);\n }\n\n /**\n * Get verifications by domain\n */\n async getVerificationsByDomain(domain: string): Promise<VerificationRecord[]> {\n await this.ensureLoaded();\n return Array.from(this.verifications.values()).filter(v => v.domain === domain);\n }\n\n /**\n * Get verifications for an agent (where they are the target of verification)\n * This requires looking up the target message to find the agent\n * For now, we'll return all verifications and let the caller filter\n */\n async getVerificationsByDomainForAgent(domain: string): Promise<VerificationRecord[]> {\n return this.getVerificationsByDomain(domain);\n }\n\n /**\n * Get all commits\n */\n async getCommits(): Promise<CommitRecord[]> {\n await this.ensureLoaded();\n return Array.from(this.commits.values());\n }\n\n /**\n * Get commit by ID\n */\n async getCommit(id: string): Promise<CommitRecord | null> {\n await this.ensureLoaded();\n return this.commits.get(id) || null;\n }\n\n /**\n * Get commits by agent\n */\n async getCommitsByAgent(agent: string): Promise<CommitRecord[]> {\n await this.ensureLoaded();\n return Array.from(this.commits.values()).filter(c => c.agent === agent);\n }\n\n /**\n * Get all reveals\n */\n async getReveals(): Promise<RevealRecord[]> {\n await this.ensureLoaded();\n return Array.from(this.reveals.values());\n }\n\n /**\n * Get reveal by commitment ID\n */\n async getRevealByCommitment(commitmentId: string): Promise<RevealRecord | null> {\n await this.ensureLoaded();\n return Array.from(this.reveals.values()).find(r => r.commitmentId === commitmentId) || null;\n }\n\n /**\n * Get reveals by agent\n */\n async getRevealsByAgent(agent: string): Promise<RevealRecord[]> {\n await this.ensureLoaded();\n return Array.from(this.reveals.values()).filter(r => r.agent === agent);\n }\n}\n","/**\n * Verification record creation and validation.\n * Core primitive for computational reputation.\n */\n\nimport { createEnvelope, verifyEnvelope } from '../message/envelope';\nimport type { VerificationRecord } from './types';\nimport { validateVerificationRecord } from './types';\n\n/**\n * Create a signed verification record\n * @param verifier - Public key of the verifying agent\n * @param privateKey - Private key for signing\n * @param target - ID of the message/output being verified\n * @param domain - Capability domain\n * @param verdict - Verification verdict\n * @param confidence - Verifier's confidence (0-1)\n * @param timestamp - Timestamp for the verification (ms)\n * @param evidence - Optional link to verification evidence\n * @returns Signed VerificationRecord\n */\nexport function createVerification(\n verifier: string,\n privateKey: string,\n target: string,\n domain: string,\n verdict: 'correct' | 'incorrect' | 'disputed',\n confidence: number,\n timestamp: number,\n evidence?: string\n): VerificationRecord {\n // Validate confidence range\n if (confidence < 0 || confidence > 1) {\n throw new Error('confidence must be between 0 and 1');\n }\n \n // Create the payload for signing\n const payload: Record<string, unknown> = {\n verifier,\n target,\n domain,\n verdict,\n confidence,\n timestamp,\n };\n \n if (evidence !== undefined) {\n payload.evidence = evidence;\n }\n \n // Create signed envelope with type 'verification'\n const envelope = createEnvelope('verification', verifier, privateKey, payload, timestamp, undefined, [verifier]);\n \n // Return verification record\n const record: VerificationRecord = {\n id: envelope.id,\n verifier,\n target,\n domain,\n verdict,\n confidence,\n timestamp,\n signature: envelope.signature,\n };\n \n if (evidence !== undefined) {\n record.evidence = evidence;\n }\n \n return record;\n}\n\n/**\n * Verify the cryptographic signature of a verification record\n * @param record - The verification record to verify\n * @returns Object with valid flag and optional reason for failure\n */\nexport function verifyVerificationSignature(\n record: VerificationRecord\n): { valid: boolean; reason?: string } {\n // First validate the structure\n const structureValidation = validateVerificationRecord(record);\n if (!structureValidation.valid) {\n return { \n valid: false, \n reason: `Invalid structure: ${structureValidation.errors.join(', ')}` \n };\n }\n \n // Reconstruct the envelope for signature verification\n const payload: Record<string, unknown> = {\n verifier: record.verifier,\n target: record.target,\n domain: record.domain,\n verdict: record.verdict,\n confidence: record.confidence,\n timestamp: record.timestamp,\n };\n \n if (record.evidence !== undefined) {\n payload.evidence = record.evidence;\n }\n \n const envelope = {\n id: record.id,\n type: 'verification' as const,\n from: record.verifier,\n to: [record.verifier],\n timestamp: record.timestamp,\n payload,\n signature: record.signature,\n };\n \n // Verify the envelope signature\n return verifyEnvelope(envelope);\n}\n","/**\n * Commit-reveal pattern implementation for tamper-proof predictions.\n * Agents commit to predictions before outcomes are known, then reveal after.\n */\n\nimport { createHash } from 'node:crypto';\nimport { createEnvelope } from '../message/envelope';\nimport type { CommitRecord, RevealRecord } from './types';\nimport { validateCommitRecord, validateRevealRecord } from './types';\n\n/**\n * Create a commitment hash for a prediction\n * @param prediction - The prediction string\n * @returns SHA-256 hash of the prediction (hex string)\n */\nexport function hashPrediction(prediction: string): string {\n return createHash('sha256').update(prediction).digest('hex');\n}\n\n/**\n * Create a signed commit record\n * @param agent - Public key of the committing agent\n * @param privateKey - Private key for signing\n * @param domain - Domain of the prediction\n * @param prediction - The prediction to commit to\n * @param timestamp - Timestamp for the commit (ms)\n * @param expiryMs - Expiry time in milliseconds from timestamp\n * @returns Signed CommitRecord\n */\nexport function createCommit(\n agent: string,\n privateKey: string,\n domain: string,\n prediction: string,\n timestamp: number,\n expiryMs: number\n): CommitRecord {\n const commitment = hashPrediction(prediction);\n const expiry = timestamp + expiryMs;\n \n // Create the payload for signing\n const payload = {\n agent,\n domain,\n commitment,\n timestamp,\n expiry,\n };\n \n // Create signed envelope with type 'commit'\n const envelope = createEnvelope('commit', agent, privateKey, payload, timestamp, undefined, [agent]);\n \n // Return commit record\n return {\n id: envelope.id,\n agent,\n domain,\n commitment,\n timestamp,\n expiry,\n signature: envelope.signature,\n };\n}\n\n/**\n * Create a signed reveal record\n * @param agent - Public key of the revealing agent\n * @param privateKey - Private key for signing\n * @param commitmentId - ID of the original commit record\n * @param prediction - The original prediction (plaintext)\n * @param outcome - The observed outcome\n * @param timestamp - Timestamp for the reveal (ms)\n * @param evidence - Optional evidence for the outcome\n * @returns Signed RevealRecord\n */\nexport function createReveal(\n agent: string,\n privateKey: string,\n commitmentId: string,\n prediction: string,\n outcome: string,\n timestamp: number,\n evidence?: string\n): RevealRecord {\n \n // Create the payload for signing\n const payload: Record<string, unknown> = {\n agent,\n commitmentId,\n prediction,\n outcome,\n timestamp,\n };\n \n if (evidence !== undefined) {\n payload.evidence = evidence;\n }\n \n // Create signed envelope with type 'reveal'\n const envelope = createEnvelope('reveal', agent, privateKey, payload, timestamp, undefined, [agent]);\n \n // Return reveal record\n const record: RevealRecord = {\n id: envelope.id,\n agent,\n commitmentId,\n prediction,\n outcome,\n timestamp,\n signature: envelope.signature,\n };\n \n if (evidence !== undefined) {\n record.evidence = evidence;\n }\n \n return record;\n}\n\n/**\n * Verify a reveal against its commitment\n * @param commit - The original commit record\n * @param reveal - The reveal record\n * @returns Object with valid flag and optional reason for failure\n */\nexport function verifyReveal(\n commit: CommitRecord,\n reveal: RevealRecord\n): { valid: boolean; reason?: string } {\n // Validate structures\n const commitValidation = validateCommitRecord(commit);\n if (!commitValidation.valid) {\n return { valid: false, reason: `Invalid commit: ${commitValidation.errors.join(', ')}` };\n }\n \n const revealValidation = validateRevealRecord(reveal);\n if (!revealValidation.valid) {\n return { valid: false, reason: `Invalid reveal: ${revealValidation.errors.join(', ')}` };\n }\n \n // Check that reveal references the correct commit\n if (reveal.commitmentId !== commit.id) {\n return { valid: false, reason: 'Reveal does not reference this commit' };\n }\n \n // Check that agents match\n if (reveal.agent !== commit.agent) {\n return { valid: false, reason: 'Reveal agent does not match commit agent' };\n }\n \n // Check that reveal is after commit expiry\n if (reveal.timestamp < commit.expiry) {\n return { valid: false, reason: 'Reveal timestamp is before commit expiry' };\n }\n \n // Verify that the prediction hash matches the commitment\n const predictedHash = hashPrediction(reveal.prediction);\n if (predictedHash !== commit.commitment) {\n return { valid: false, reason: 'Prediction hash does not match commitment' };\n }\n \n return { valid: true };\n}\n","/**\r\n * Trust score computation with exponential time decay.\r\n * Domain-specific reputation scoring from verification history.\r\n */\r\n\r\nimport type { VerificationRecord, TrustScore } from './types';\r\n\r\n/**\r\n * Exponential decay function for time-based reputation degradation.\r\n * @param deltaTimeMs - Time since verification (milliseconds)\r\n * @param lambda - Decay rate (default: ln(2)/70 ≈ 0.0099, giving 70-day half-life)\r\n * @returns Weight multiplier (0-1)\r\n */\r\nexport function decay(deltaTimeMs: number, lambda = Math.log(2) / 70): number {\r\n const deltaDays = deltaTimeMs / (1000 * 60 * 60 * 24);\r\n return Math.exp(-lambda * deltaDays);\r\n}\r\n\r\n/**\r\n * Compute verdict weight\r\n * @param verdict - Verification verdict\r\n * @returns Weight value (+1 for correct, -1 for incorrect, 0 for disputed)\r\n */\r\nfunction verdictWeight(verdict: 'correct' | 'incorrect' | 'disputed'): number {\r\n switch (verdict) {\r\n case 'correct':\r\n return 1;\r\n case 'incorrect':\r\n return -1;\r\n case 'disputed':\r\n return 0;\r\n }\r\n}\r\n\r\n/**\r\n * Options for recursive trust score computation.\r\n */\r\nexport interface TrustScoreOptions {\r\n /**\r\n * Optional function to get verifier's trust score for recursive weighting.\r\n * When provided, each verification is weighted by the verifier's own trust score.\r\n * Defaults to returning 1.0 (flat weighting, backward compatible).\r\n * New agents with no score should return 0.5 (neutral bootstrapping weight).\r\n */\r\n getVerifierScore?: (verifier: string, domain: string) => number;\r\n /**\r\n * Maximum recursion depth for recursive scoring. Default: 3.\r\n * At depth 0, flat weighting (1.0) is used instead of calling getVerifierScore.\r\n */\r\n maxDepth?: number;\r\n /**\r\n * Internal: set of agents currently being scored, used for cycle detection.\r\n * Pass a shared mutable Set when making recursive calls to enable cycle detection.\r\n * When a verifier is found in this set, neutral weight (0.5) is used instead of recursing.\r\n */\r\n visitedAgents?: Set<string>;\r\n}\r\n\r\n/**\r\n * Compute trust score for an agent in a specific domain\r\n * @param agent - Public key of the agent being scored\r\n * @param domain - Capability domain\r\n * @param verifications - All verification records (will be filtered by target and domain)\r\n * @param currentTime - Current timestamp (ms)\r\n * @param options - Optional settings for recursive scoring and cycle detection\r\n * @returns TrustScore object with computed reputation\r\n */\r\nexport function computeTrustScore(\r\n agent: string,\r\n domain: string,\r\n verifications: VerificationRecord[],\r\n currentTime: number,\r\n options?: TrustScoreOptions\r\n): TrustScore {\r\n // Filter verifications for this agent and domain\r\n const relevantVerifications = verifications.filter(\r\n v => v.target === agent && v.domain === domain\r\n );\r\n \r\n if (relevantVerifications.length === 0) {\r\n return {\r\n agent,\r\n domain,\r\n score: 0,\r\n verificationCount: 0,\r\n lastVerified: 0,\r\n topVerifiers: [],\r\n };\r\n }\r\n \r\n const maxDepth = options?.maxDepth ?? 3;\r\n const visitedAgents = options?.visitedAgents;\r\n const getVerifierScore = options?.getVerifierScore;\r\n\r\n // Mark current agent as being scored (cycle detection)\r\n if (visitedAgents) {\r\n visitedAgents.add(agent);\r\n }\r\n\r\n // Compute weighted score with time decay\r\n let totalWeight = 0;\r\n const verifierWeights = new Map<string, number>();\r\n \r\n for (const verification of relevantVerifications) {\r\n const deltaTime = currentTime - verification.timestamp;\r\n const decayFactor = decay(deltaTime);\r\n const verdict = verdictWeight(verification.verdict);\r\n\r\n // Determine verifier trust weight for recursive scoring\r\n let verifierTrustWeight: number;\r\n if (!getVerifierScore || maxDepth <= 0) {\r\n // No recursive scoring or depth limit reached — use flat weight\r\n verifierTrustWeight = 1.0;\r\n } else if (visitedAgents?.has(verification.verifier)) {\r\n // Cycle detected — use neutral weight (0.5) instead of recursing\r\n verifierTrustWeight = 0.5;\r\n } else {\r\n verifierTrustWeight = getVerifierScore(verification.verifier, domain);\r\n }\r\n\r\n const weight = verdict * verification.confidence * decayFactor * verifierTrustWeight;\r\n \r\n totalWeight += weight;\r\n \r\n // Track verifier contributions\r\n const currentVerifierWeight = verifierWeights.get(verification.verifier) ?? 0;\r\n verifierWeights.set(verification.verifier, currentVerifierWeight + Math.abs(weight));\r\n }\r\n\r\n // Backtrack: remove current agent from visited set for correct DFS traversal\r\n if (visitedAgents) {\r\n visitedAgents.delete(agent);\r\n }\r\n \r\n // Normalize score to 0-1 range\r\n // Positive verifications push toward 1, negative push toward 0\r\n const rawScore = totalWeight / Math.max(relevantVerifications.length, 1);\r\n const normalizedScore = Math.max(0, Math.min(1, (rawScore + 1) / 2));\r\n \r\n // Find most recent verification\r\n const lastVerified = Math.max(...relevantVerifications.map(v => v.timestamp));\r\n \r\n // Get top verifiers by absolute weight\r\n const topVerifiers = Array.from(verifierWeights.entries())\r\n .sort((a, b) => b[1] - a[1])\r\n .slice(0, 5)\r\n .map(([verifier]) => verifier);\r\n \r\n return {\r\n agent,\r\n domain,\r\n score: normalizedScore,\r\n verificationCount: relevantVerifications.length,\r\n lastVerified,\r\n topVerifiers,\r\n };\r\n}\r\n\r\n/**\r\n * Compute trust scores for an agent across multiple domains\r\n * @param agent - Public key of the agent being scored\r\n * @param verifications - All verification records\r\n * @param currentTime - Current timestamp (ms)\r\n * @returns Map of domain to TrustScore\r\n */\r\nexport function computeTrustScores(\r\n agent: string,\r\n verifications: VerificationRecord[],\r\n currentTime: number\r\n): Map<string, TrustScore> {\r\n // Get unique domains for this agent\r\n const domains = new Set(\r\n verifications\r\n .filter(v => v.target === agent)\r\n .map(v => v.domain)\r\n );\r\n \r\n const scores = new Map<string, TrustScore>();\r\n for (const domain of domains) {\r\n const score = computeTrustScore(agent, domain, verifications, currentTime);\r\n scores.set(domain, score);\r\n }\r\n \r\n return scores;\r\n}\r\n\r\n// Alias for backward compatibility\r\nexport const computeAllTrustScores = computeTrustScores;\r\n"],"mappings":"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","names":["WebSocket","WebSocket","EventEmitter","EventEmitter"]}
|