{"version":3,"sources":["../src/plugins.ts","../src/relay-feedback.ts"],"sourcesContent":["// src/plugins.ts\n// RelAI Plugin System - extensible middleware hooks for Relai.protect()\n\nimport crypto from 'crypto';\nimport { ethers } from 'ethers';\nimport type { RelaiNetwork } from './types';\nimport type { SettleResult } from './server';\n\nconst RELAI_API_BASE = 'https://api.relai.fi';\n\n// ============================================================================\n// Plugin Interface\n// ============================================================================\n\nexport interface PluginContext {\n  /** Network for this endpoint */\n  network: RelaiNetwork;\n  /** Price in USD */\n  price: number;\n  /** Request path */\n  path: string;\n  /** HTTP method */\n  method: string;\n}\n\nexport interface PluginResult {\n  /** If true, skip payment and serve content for free */\n  skip?: boolean;\n  /** If true, reject the request entirely (e.g. service unhealthy) */\n  reject?: boolean;\n  /** HTTP status code when rejecting (default: 503) */\n  rejectStatus?: number;\n  /** Error message when rejecting */\n  rejectMessage?: string;\n  /** Extra response headers to set */\n  headers?: Record<string, string>;\n  /** Metadata attached to req.pluginMeta */\n  meta?: Record<string, unknown>;\n}\n\nexport interface RelaiPlugin {\n  /** Unique plugin name */\n  name: string;\n\n  /**\n   * Called before the 402 payment check.\n   * Return { skip: true } to bypass payment entirely.\n   */\n  beforePaymentCheck?(req: any, ctx: PluginContext): Promise<PluginResult>;\n\n  /**\n   * Called after a successful payment settlement.\n   * Use for analytics, logging, webhooks, etc.\n   */\n  afterSettled?(req: any, result: SettleResult, ctx: PluginContext): Promise<void>;\n\n  /**\n   * Called once when the Relai instance initializes (server start).\n   * Use to sync config to RelAI backend or validate credentials.\n   */\n  onInit?(): Promise<void>;\n\n  /**\n   * Called before sending the 402 response. Allows plugins to add\n   * extensions or modify the response body (e.g. bridge info).\n   */\n  enrich402Response?(response: any, ctx: PluginContext): any;\n}\n\n// ============================================================================\n// Free Tier Plugin\n// ============================================================================\n\nexport interface FreeTierPluginConfig {\n  /** Service key (sk_live_...) for syncing with RelAI backend. If omitted, runs in local in-memory mode. */\n  serviceKey?: string;\n  /** Max free calls per buyer per period */\n  perBuyerLimit: number;\n  /** Reset period for per-buyer counters */\n  resetPeriod?: 'none' | 'daily' | 'monthly';\n  /** Optional global cap across all buyers */\n  globalCap?: number;\n  /** Specific paths to apply free tier to (default: '*' = all) */\n  paths?: string[];\n  /** Override RelAI API base URL (default: https://api.relai.fi) */\n  baseUrl?: string;\n  /** Cache TTL in ms for check results (default: 5000) */\n  cacheTtlMs?: number;\n}\n\ninterface FreeTierCheckResponse {\n  free: boolean;\n  remaining?: number;\n  total?: number;\n  reason?: string;\n  globalRemaining?: number;\n}\n\n/**\n * Free Tier plugin - gives buyers a number of free API calls\n * before requiring payment.\n *\n * State is stored in the RelAI backend, keyed by your service key.\n * Config can be set here (SDK-side) or overridden in the relai.fi dashboard.\n *\n * @example\n * ```typescript\n * import Relai from '@relai-fi/x402/server';\n * import { freeTier } from '@relai-fi/x402/plugins';\n *\n * const relai = new Relai({\n *   network: 'base',\n *   plugins: [\n *     freeTier({\n *       serviceKey: process.env.RELAI_SERVICE_KEY!,\n *       perBuyerLimit: 10,\n *       resetPeriod: 'daily',\n *     }),\n *   ],\n * });\n *\n * app.get('/api/data', relai.protect({\n *   payTo: '0xYourWallet',\n *   price: 0.01,\n * }), (req, res) => {\n *   res.json({ data: 'paid content' });\n * });\n * ```\n */\n// ============================================================================\n// Bridge Plugin\n// ============================================================================\n\nexport interface BridgePluginConfig {\n  /** Service key (sk_live_...) for tracking bridge usage per API owner */\n  serviceKey?: string;\n  /** RelAI API base URL (default: https://api.relai.fi) */\n  baseUrl?: string;\n  /** Override settle endpoint (auto-discovered from /bridge/info if not set) */\n  settleEndpoint?: string;\n  /** Override supported source chains (auto-discovered if not set) */\n  supportedSourceChains?: string[];\n  /** Override supported source assets (auto-discovered if not set) */\n  supportedSourceAssets?: string[];\n  /** Override bridge payTo map: { [caip2]: address } */\n  payTo?: Record<string, string>;\n  /** Override Solana fee payer address (auto-discovered if not set) */\n  feePayerSvm?: string;\n  /** Override payment facilitator URL */\n  paymentFacilitator?: string;\n  /** Bridge fee in basis points (default: auto-discovered) */\n  feeBps?: number;\n}\n\ninterface BridgeInfo {\n  settleEndpoint: string;\n  supportedSourceChains: string[];\n  supportedSourceAssets: string[];\n  payTo: Record<string, string>;\n  feePayerSvm: string | null;\n  feeBps: number;\n  paymentFacilitator: string;\n}\n\n/**\n * Bridge plugin - enables cross-chain payments via the RelAI bridge.\n *\n * When a buyer's wallet is on a different chain than the merchant accepts,\n * the client SDK can automatically route the payment through the bridge.\n * This plugin adds `extensions.bridge` to the 402 response with all the\n * info the client needs to execute a cross-chain payment.\n *\n * @example\n * ```typescript\n * import Relai from '@relai-fi/x402/server';\n * import { bridge } from '@relai-fi/x402/plugins';\n *\n * const relai = new Relai({\n *   network: 'skale-base',\n *   plugins: [\n *     bridge(), // auto-discovers from https://api.relai.fi\n *   ],\n * });\n *\n * // Buyer on Solana can now pay for a SKALE endpoint\n * app.get('/api/data', relai.protect({\n *   payTo: '0xYourWallet',\n *   price: 0.05,\n * }), (req, res) => {\n *   res.json({ data: 'paid content' });\n * });\n * ```\n */\nexport function bridge(config?: BridgePluginConfig): RelaiPlugin {\n  const base = (config?.baseUrl ?? RELAI_API_BASE).replace(/\\/$/, '');\n  let bridgeInfo: BridgeInfo | null = null;\n\n  // Hash service key for tracking (never expose raw key to clients)\n  // Must match backend: crypto.createHash(\"sha256\").update(key).digest(\"hex\").slice(0, 16)\n  let serviceKeyHash: string | null = null;\n  if (config?.serviceKey) {\n    serviceKeyHash = crypto.createHash('sha256').update(config.serviceKey).digest('hex').slice(0, 16);\n  }\n\n  async function fetchBridgeInfo(): Promise<BridgeInfo | null> {\n    try {\n      const res = await fetch(`${base}/bridge/info`);\n      if (!res.ok) {\n        console.warn(`[relai:bridge] Failed to fetch /bridge/info: ${res.status}`);\n        return null;\n      }\n      const data = await res.json() as any;\n      return {\n        settleEndpoint: config?.settleEndpoint || data.settleEndpoint,\n        supportedSourceChains: config?.supportedSourceChains || data.supportedSourceChains || [],\n        supportedSourceAssets: config?.supportedSourceAssets || data.supportedSourceAssets || [],\n        payTo: config?.payTo || data.payTo || {},\n        feePayerSvm: config?.feePayerSvm ?? data.feePayerSvm ?? null,\n        feeBps: config?.feeBps ?? data.feeBps ?? 100,\n        paymentFacilitator: config?.paymentFacilitator || data.paymentFacilitator || 'https://facilitator.x402.fi',\n      };\n    } catch (err) {\n      console.warn(`[relai:bridge] Failed to fetch bridge info: ${err}`);\n      return null;\n    }\n  }\n\n  return {\n    name: 'bridge',\n\n    async onInit() {\n      bridgeInfo = await fetchBridgeInfo();\n      if (bridgeInfo) {\n        console.log(`[relai:bridge] Initialized — ${bridgeInfo.supportedSourceChains.length} source chains, settle: ${bridgeInfo.settleEndpoint}`);\n      } else {\n        console.warn('[relai:bridge] Bridge info not available — cross-chain payments disabled');\n      }\n    },\n\n    enrich402Response(response: any, ctx: PluginContext) {\n      if (!bridgeInfo || bridgeInfo.supportedSourceChains.length === 0) {\n        return response;\n      }\n\n      // Don't add bridge extension if merchant's network is already a source chain\n      // and there's only that one chain — no bridging needed\n      const merchantCaip2 = response?.accepts?.[0]?.network;\n\n      // Filter out the merchant's own chain from source chains\n      // (buyer on same chain should pay directly, not bridge)\n      const otherSourceChains = bridgeInfo.supportedSourceChains.filter(\n        (c: string) => c !== merchantCaip2,\n      );\n\n      if (otherSourceChains.length === 0) {\n        return response;\n      }\n\n      // Find the payTo for the first available source chain (Solana-first for UX)\n      const solanaChain = otherSourceChains.find((c: string) => c.startsWith('solana:'));\n      const primaryPayTo = solanaChain\n        ? bridgeInfo.payTo[solanaChain]\n        : bridgeInfo.payTo[otherSourceChains[0]];\n\n      response.extensions = response.extensions || {};\n      response.extensions.bridge = {\n        info: {\n          settleEndpoint: bridgeInfo.settleEndpoint,\n          supportedSourceChains: otherSourceChains,\n          supportedSourceAssets: bridgeInfo.supportedSourceAssets,\n          payTo: primaryPayTo || null,\n          payToMap: bridgeInfo.payTo,\n          feePayerSvm: bridgeInfo.feePayerSvm,\n          feeBps: bridgeInfo.feeBps,\n          paymentFacilitator: bridgeInfo.paymentFacilitator,\n          ...(serviceKeyHash ? { serviceKeyHash } : {}),\n        },\n      };\n\n      return response;\n    },\n  };\n}\n\n// ============================================================================\n// Free Tier Plugin\n// ============================================================================\n\n/**\n * Extended plugin interface with data export for free tier.\n */\nexport interface FreeTierPlugin extends RelaiPlugin {\n  /** Export all in-memory usage data (local mode only). Returns null when using cloud mode. */\n  getUsageData(): FreeTierUsageExport | null;\n  /** Whether plugin is running in local (in-memory) or cloud (RelAI backend) mode. */\n  readonly mode: 'local' | 'cloud';\n}\n\nexport interface FreeTierUsageExport {\n  mode: 'local';\n  config: {\n    perBuyerLimit: number;\n    resetPeriod: string;\n    globalCap: number | null;\n    paths: string[];\n  };\n  /** Per-buyer usage entries */\n  usage: Array<{\n    buyerId: string;\n    path: string;\n    count: number;\n    periodStart: string;\n    lastCall: string;\n  }>;\n  globalCount: number;\n  exportedAt: string;\n}\n\nexport function freeTier(config: FreeTierPluginConfig): FreeTierPlugin {\n  const base = (config.baseUrl ?? RELAI_API_BASE).replace(/\\/$/, '');\n  const cacheTtl = config.cacheTtlMs ?? 5000;\n  const isLocal = !config.serviceKey;\n  const resetPeriod = config.resetPeriod ?? 'none';\n  const limit = config.perBuyerLimit;\n  const globalCap = config.globalCap ?? null;\n  const pluginPaths = config.paths ?? ['*'];\n\n  /**\n   * Resolve buyer identity from the request.\n   * Priority: JWT sub > x-wallet-address > IP fallback\n   */\n  function resolveBuyerId(req: any): string {\n    // 1. JWT Bearer token\n    try {\n      const auth = req.headers?.authorization || '';\n      if (auth.startsWith('Bearer ')) {\n        const token = auth.slice(7).trim();\n        const payload = JSON.parse(Buffer.from(token.split('.')[1], 'base64').toString());\n        if (payload?.sub) return `user:${payload.sub}`;\n      }\n    } catch { /* ignore */ }\n\n    // 2. Explicit wallet header\n    const wallet = req.headers?.['x-wallet-address'] || req.headers?.['x-buyer-address'];\n    if (wallet) return `wallet:${wallet}`;\n\n    // 3. IP fallback\n    const ip =\n      (req.headers?.['x-forwarded-for'] || '').split(',')[0].trim() ||\n      req.socket?.remoteAddress ||\n      req.ip ||\n      'unknown';\n    return `ip:${ip}`;\n  }\n\n  function pathMatches(requestPath: string): boolean {\n    return pluginPaths.includes('*') || pluginPaths.some((p) => {\n      const normalized = p.toLowerCase().replace(/\\/+$/, '') || '/';\n      const reqNormalized = requestPath.toLowerCase().replace(/\\/+$/, '') || '/';\n      return normalized === reqNormalized || normalized === '*';\n    });\n  }\n\n  // ---------------------------------------------------------------------------\n  // LOCAL IN-MEMORY MODE\n  // ---------------------------------------------------------------------------\n\n  interface LocalUsageEntry {\n    count: number;\n    periodStart: string;\n    lastCall: string;\n  }\n\n  // Map key: \"path:buyerId\"\n  const localUsage = new Map<string, LocalUsageEntry>();\n  let localGlobalCount = 0;\n\n  function currentPeriodStart(): string {\n    const now = new Date();\n    if (resetPeriod === 'daily') {\n      return new Date(now.getFullYear(), now.getMonth(), now.getDate()).toISOString();\n    }\n    if (resetPeriod === 'monthly') {\n      return new Date(now.getFullYear(), now.getMonth(), 1).toISOString();\n    }\n    return 'none'; // permanent\n  }\n\n  function isCurrentPeriod(entry: LocalUsageEntry): boolean {\n    if (resetPeriod === 'none') return true;\n    return entry.periodStart === currentPeriodStart();\n  }\n\n  function localKey(path: string, buyerId: string): string {\n    return `${path}:${buyerId}`;\n  }\n\n  function localCheck(path: string, buyerId: string): FreeTierCheckResponse {\n    const key = localKey(path, buyerId);\n    const entry = localUsage.get(key);\n\n    // If entry is from a previous period, it's stale — treat as 0\n    const count = (entry && isCurrentPeriod(entry)) ? entry.count : 0;\n    const remaining = Math.max(0, limit - count);\n\n    // Global cap check\n    if (globalCap != null && localGlobalCount >= globalCap) {\n      return { free: false, remaining: 0, total: limit, reason: 'global_cap_reached', globalRemaining: 0 };\n    }\n\n    if (count >= limit) {\n      return { free: false, remaining: 0, total: limit, reason: 'limit_reached' };\n    }\n\n    return {\n      free: true,\n      remaining: remaining - 1, // after this call\n      total: limit,\n      ...(globalCap != null ? { globalRemaining: globalCap - localGlobalCount - 1 } : {}),\n    };\n  }\n\n  function localRecord(path: string, buyerId: string): void {\n    const key = localKey(path, buyerId);\n    const period = currentPeriodStart();\n    const entry = localUsage.get(key);\n    const now = new Date().toISOString();\n\n    if (entry && isCurrentPeriod(entry)) {\n      entry.count++;\n      entry.lastCall = now;\n    } else {\n      localUsage.set(key, { count: 1, periodStart: period, lastCall: now });\n    }\n    localGlobalCount++;\n  }\n\n  function exportLocalData(): FreeTierUsageExport {\n    const usage: FreeTierUsageExport['usage'] = [];\n    for (const [key, entry] of localUsage.entries()) {\n      // key format is \"path:buyerId\" but buyerId itself can contain ':'\n      // We stored it as `${path}:${buyerId}` where path starts with '/'\n      // So we find the first ':' after the path portion\n      const firstSlash = key.indexOf('/');\n      const colonAfterPath = key.indexOf(':', firstSlash > -1 ? firstSlash : 0);\n      const path = colonAfterPath > -1 ? key.slice(0, colonAfterPath) : key;\n      const buyerId = colonAfterPath > -1 ? key.slice(colonAfterPath + 1) : 'unknown';\n      usage.push({\n        buyerId,\n        path,\n        count: entry.count,\n        periodStart: entry.periodStart,\n        lastCall: entry.lastCall,\n      });\n    }\n    return {\n      mode: 'local',\n      config: { perBuyerLimit: limit, resetPeriod, globalCap, paths: pluginPaths },\n      usage,\n      globalCount: localGlobalCount,\n      exportedAt: new Date().toISOString(),\n    };\n  }\n\n  // ---------------------------------------------------------------------------\n  // CLOUD MODE (original — requires serviceKey)\n  // ---------------------------------------------------------------------------\n\n  // Simple in-memory cache: \"serviceKey:path:buyerId\" -> { result, expiresAt }\n  const cache = new Map<string, { result: FreeTierCheckResponse; expiresAt: number }>();\n\n  function resolveHeaders(): Record<string, string> {\n    return {\n      'X-Service-Key': config.serviceKey!,\n      'Content-Type': 'application/json',\n    };\n  }\n\n  function cloudCacheKey(path: string, buyerId: string): string {\n    return `${config.serviceKey}:${path}:${buyerId}`;\n  }\n\n  async function checkFreeTier(path: string, buyerId: string): Promise<FreeTierCheckResponse> {\n    const key = cloudCacheKey(path, buyerId);\n    const now = Date.now();\n    const cached = cache.get(key);\n    if (cached && cached.expiresAt > now) {\n      return cached.result;\n    }\n\n    try {\n      const res = await fetch(`${base}/v1/plugins/free-tier/check`, {\n        method: 'POST',\n        headers: resolveHeaders(),\n        body: JSON.stringify({ path, buyerId }),\n      });\n\n      if (!res.ok) {\n        // Non-blocking: if API unreachable, default to not-free\n        return { free: false, reason: `api_error_${res.status}` };\n      }\n\n      const result = await res.json() as FreeTierCheckResponse;\n      cache.set(key, { result, expiresAt: now + cacheTtl });\n      return result;\n    } catch (err) {\n      // Network error - non-blocking, default to paid\n      return { free: false, reason: 'network_error' };\n    }\n  }\n\n  async function recordCall(path: string, buyerId: string): Promise<void> {\n    try {\n      await fetch(`${base}/v1/plugins/free-tier/record`, {\n        method: 'POST',\n        headers: resolveHeaders(),\n        body: JSON.stringify({ path, buyerId }),\n      });\n      // Invalidate cache for this buyer+path after recording\n      cache.delete(cloudCacheKey(path, buyerId));\n    } catch {\n      // Fire-and-forget\n    }\n  }\n\n  async function syncConfig(): Promise<void> {\n    try {\n      // Check if config already exists on backend — don't overwrite dashboard-managed configs\n      const existing = await fetch(`${base}/v1/plugins/free-tier/config`, {\n        method: 'GET',\n        headers: resolveHeaders(),\n      });\n      if (existing.ok) {\n        const data = await existing.json();\n        if (data.configs && data.configs.length > 0) {\n          return; // Config managed via dashboard or previous sync — skip\n        }\n      }\n\n      await fetch(`${base}/v1/plugins/free-tier/config`, {\n        method: 'PUT',\n        headers: resolveHeaders(),\n        body: JSON.stringify({\n          perBuyerLimit: config.perBuyerLimit,\n          resetPeriod: config.resetPeriod ?? 'none',\n          globalCap: config.globalCap ?? null,\n          paths: pluginPaths,\n        }),\n      });\n    } catch (err) {\n      console.warn(`[relai:freeTier] Failed to sync config to RelAI: ${err}`);\n    }\n  }\n\n  // ---------------------------------------------------------------------------\n  // Plugin instance\n  // ---------------------------------------------------------------------------\n\n  return {\n    name: 'free-tier',\n    mode: isLocal ? 'local' : 'cloud',\n\n    getUsageData(): FreeTierUsageExport | null {\n      if (!isLocal) return null;\n      return exportLocalData();\n    },\n\n    async onInit() {\n      if (isLocal) {\n        console.log(`[relai:freeTier] Running in local mode (in-memory). perBuyerLimit=${limit}, resetPeriod=${resetPeriod}`);\n        return;\n      }\n      await syncConfig();\n    },\n\n    async beforePaymentCheck(req, ctx) {\n      const requestPath = ctx.path || '/';\n      if (!pathMatches(requestPath)) return {};\n\n      const buyerId = resolveBuyerId(req);\n\n      // ── Local mode ──\n      if (isLocal) {\n        const result = localCheck(requestPath, buyerId);\n        if (result.free) {\n          localRecord(requestPath, buyerId);\n          return {\n            skip: true,\n            headers: {\n              'X-Free-Calls-Remaining': String(result.remaining ?? 0),\n              'X-Free-Calls-Total': String(result.total ?? limit),\n              ...(result.globalRemaining != null\n                ? { 'X-Free-Calls-Global-Remaining': String(result.globalRemaining) }\n                : {}),\n              'X-Free-Tier-Mode': 'local',\n            },\n            meta: { freeTier: true, buyerId, remaining: result.remaining, total: result.total ?? limit, mode: 'local' },\n          };\n        }\n        return {};\n      }\n\n      // ── Cloud mode ──\n      const result = await checkFreeTier(requestPath, buyerId);\n\n      if (result.free) {\n        // Record the call (fire-and-forget)\n        recordCall(requestPath, buyerId).catch(() => {});\n\n        return {\n          skip: true,\n          headers: {\n            'X-Free-Calls-Remaining': String(result.remaining ?? 0),\n            'X-Free-Calls-Total': String(result.total ?? config.perBuyerLimit),\n            ...(result.globalRemaining != null\n              ? { 'X-Free-Calls-Global-Remaining': String(result.globalRemaining) }\n              : {}),\n          },\n          meta: {\n            freeTier: true,\n            buyerId,\n            remaining: result.remaining,\n            total: result.total ?? config.perBuyerLimit,\n          },\n        };\n      }\n\n      return {};\n    },\n  };\n}\n\n// ============================================================================\n// Shield Plugin\n// ============================================================================\n\nexport interface ShieldPluginConfig {\n  /**\n   * Custom health check function. Return true if the service is healthy.\n   * Takes priority over `healthUrl` if both are provided.\n   */\n  healthCheck?: () => Promise<boolean> | boolean;\n  /**\n   * URL to probe. A 2xx response means healthy.\n   * Ignored if `healthCheck` is provided.\n   */\n  healthUrl?: string;\n  /** Timeout in ms for the health probe (default: 5000) */\n  timeoutMs?: number;\n  /** Cache the health result for this many ms (default: 10000) */\n  cacheTtlMs?: number;\n  /** Message returned to the caller when unhealthy (default: 'Service temporarily unavailable. Please try again later.') */\n  unhealthyMessage?: string;\n  /** HTTP status code when unhealthy (default: 503) */\n  unhealthyStatus?: number;\n}\n\n/**\n * Shield plugin — protects buyers from paying for unhealthy endpoints.\n *\n * Before the server returns a 402, Shield runs a health check (custom\n * function or URL probe). If the service is down, the request is rejected\n * with 503 instead of asking for payment.\n *\n * @example\n * ```typescript\n * import Relai from '@relai-fi/x402/server';\n * import { shield } from '@relai-fi/x402/plugins';\n *\n * const relai = new Relai({\n *   network: 'base',\n *   plugins: [\n *     shield({\n *       healthUrl: 'https://my-api.com/health',\n *       timeoutMs: 3000,\n *     }),\n *   ],\n * });\n *\n * // If /health is down, buyers get 503 instead of 402\n * app.get('/api/data', relai.protect({\n *   payTo: '0xYourWallet',\n *   price: 0.01,\n * }), (req, res) => {\n *   res.json({ data: 'premium content' });\n * });\n * ```\n */\nexport function shield(config?: ShieldPluginConfig): RelaiPlugin {\n  const timeoutMs = config?.timeoutMs ?? 5000;\n  const cacheTtl = config?.cacheTtlMs ?? 10000;\n  const unhealthyMessage = config?.unhealthyMessage ?? 'Service temporarily unavailable. Please try again later.';\n  const unhealthyStatus = config?.unhealthyStatus ?? 503;\n\n  let cached: { healthy: boolean; expiresAt: number } | null = null;\n\n  async function checkHealth(): Promise<boolean> {\n    const now = Date.now();\n    if (cached && cached.expiresAt > now) {\n      return cached.healthy;\n    }\n\n    let healthy = true;\n\n    try {\n      if (config?.healthCheck) {\n        healthy = await Promise.resolve(config.healthCheck());\n      } else if (config?.healthUrl) {\n        const controller = new AbortController();\n        const timer = setTimeout(() => controller.abort(), timeoutMs);\n        try {\n          const res = await fetch(config.healthUrl, {\n            method: 'GET',\n            signal: controller.signal,\n          });\n          healthy = res.ok;\n        } finally {\n          clearTimeout(timer);\n        }\n      }\n      // No healthCheck and no healthUrl → always healthy (no-op mode)\n    } catch {\n      healthy = false;\n    }\n\n    cached = { healthy, expiresAt: now + cacheTtl };\n    return healthy;\n  }\n\n  return {\n    name: 'shield',\n\n    async onInit() {\n      const healthy = await checkHealth();\n      console.log(`[relai:shield] Initial health check: ${healthy ? 'healthy ✓' : 'UNHEALTHY ✗'}`);\n    },\n\n    async beforePaymentCheck(_req, _ctx): Promise<PluginResult> {\n      const healthy = await checkHealth();\n\n      if (!healthy) {\n        return {\n          reject: true,\n          rejectStatus: unhealthyStatus,\n          rejectMessage: unhealthyMessage,\n          headers: {\n            'X-Shield-Status': 'unhealthy',\n            'Retry-After': String(Math.ceil(cacheTtl / 1000)),\n          },\n        };\n      }\n\n      return {\n        headers: { 'X-Shield-Status': 'healthy' },\n      };\n    },\n  };\n}\n\n// ============================================================================\n// Preflight Plugin\n// ============================================================================\n\nexport interface PreflightPluginConfig {\n  /**\n   * Base URL of the API server (e.g. 'https://my-api.com').\n   * The plugin appends the request path to form the probe URL.\n   * **Required** — the plugin needs to know where to send the probe.\n   */\n  baseUrl: string;\n  /** Timeout in ms for the preflight probe (default: 3000) */\n  timeoutMs?: number;\n  /** Cache the probe result for this many ms (default: 5000) */\n  cacheTtlMs?: number;\n  /** Custom unhealthy message (default: 'Endpoint not responding. Please try again later.') */\n  unhealthyMessage?: string;\n  /** HTTP status code when endpoint unreachable (default: 503) */\n  unhealthyStatus?: number;\n}\n\n/**\n * Preflight plugin — verifies the specific endpoint responds before payment.\n *\n * Unlike Shield (global health check), Preflight probes the **actual endpoint**\n * the buyer is requesting. It sends a HEAD request with `X-Preflight: true` —\n * the Relai middleware responds 200 instantly without triggering payment.\n * If the endpoint doesn't respond, the buyer gets 503 instead of 402.\n *\n * @example\n * ```typescript\n * import Relai from '@relai-fi/x402/server';\n * import { preflight } from '@relai-fi/x402/plugins';\n *\n * const relai = new Relai({\n *   network: 'base',\n *   plugins: [\n *     preflight({ baseUrl: 'https://my-api.com' }),\n *   ],\n * });\n *\n * app.get('/api/data', relai.protect({\n *   payTo: '0xYourWallet',\n *   price: 0.01,\n * }), (req, res) => {\n *   res.json({ data: 'premium content' });\n * });\n * ```\n */\nexport function preflight(config: PreflightPluginConfig): RelaiPlugin {\n  const baseUrl = config.baseUrl.replace(/\\/+$/, '');\n  const timeoutMs = config.timeoutMs ?? 3000;\n  const cacheTtl = config.cacheTtlMs ?? 5000;\n  const unhealthyMessage = config.unhealthyMessage ?? 'Endpoint not responding. Please try again later.';\n  const unhealthyStatus = config.unhealthyStatus ?? 503;\n\n  const cache = new Map<string, { ok: boolean; expiresAt: number }>();\n\n  async function probeEndpoint(path: string): Promise<boolean> {\n    const now = Date.now();\n    const cached = cache.get(path);\n    if (cached && cached.expiresAt > now) {\n      return cached.ok;\n    }\n\n    let ok = false;\n    try {\n      const controller = new AbortController();\n      const timer = setTimeout(() => controller.abort(), timeoutMs);\n      try {\n        const url = `${baseUrl}${path.startsWith('/') ? path : `/${path}`}`;\n        const res = await fetch(url, {\n          method: 'HEAD',\n          headers: { 'X-Preflight': 'true' },\n          signal: controller.signal,\n        });\n        ok = res.ok;\n      } finally {\n        clearTimeout(timer);\n      }\n    } catch {\n      ok = false;\n    }\n\n    cache.set(path, { ok, expiresAt: now + cacheTtl });\n    return ok;\n  }\n\n  return {\n    name: 'preflight',\n\n    async onInit() {\n      // Probe the base URL on startup\n      const ok = await probeEndpoint('/');\n      console.log(`[relai:preflight] Initial probe ${baseUrl}: ${ok ? 'reachable ✓' : 'UNREACHABLE ✗'}`);\n    },\n\n    async beforePaymentCheck(_req, ctx): Promise<PluginResult> {\n      const path = ctx.path || '/';\n      const ok = await probeEndpoint(path);\n\n      if (!ok) {\n        return {\n          reject: true,\n          rejectStatus: unhealthyStatus,\n          rejectMessage: unhealthyMessage,\n          headers: {\n            'X-Preflight-Status': 'unreachable',\n            'Retry-After': String(Math.ceil(cacheTtl / 1000)),\n          },\n        };\n      }\n\n      return {\n        headers: { 'X-Preflight-Status': 'ok' },\n      };\n    },\n  };\n}\n\n\n// ============================================================================\n// Circuit Breaker Plugin\n// ============================================================================\n\nexport type CircuitState = 'closed' | 'open' | 'half-open';\n\nexport interface CircuitBreakerPluginConfig {\n  /**\n   * Number of consecutive failures before the circuit opens.\n   * Default: 5\n   */\n  failureThreshold?: number;\n  /**\n   * Time in ms the circuit stays open before moving to half-open.\n   * Default: 30000 (30 seconds)\n   */\n  resetTimeMs?: number;\n  /**\n   * Number of successful requests in half-open state before closing the circuit.\n   * Default: 2\n   */\n  halfOpenSuccesses?: number;\n  /** HTTP status code when circuit is open (default: 503) */\n  openStatus?: number;\n  /** Error message when circuit is open */\n  openMessage?: string;\n  /**\n   * HTTP status codes considered as failures.\n   * Default: [500, 502, 503, 504]\n   */\n  failureCodes?: number[];\n  /**\n   * Track failures globally or per-path.\n   * Default: 'per-path'\n   */\n  scope?: 'global' | 'per-path';\n}\n\ninterface CircuitEntry {\n  state: CircuitState;\n  failures: number;\n  successes: number;\n  lastFailureAt: number;\n}\n\n/**\n * Circuit Breaker plugin — tracks failure history and opens the circuit\n * after repeated failures, preventing buyers from paying for broken endpoints.\n *\n * Unlike Shield/Preflight (real-time probes), Circuit Breaker is **zero-latency** —\n * it makes no additional HTTP requests. Instead, it tracks `afterSettled` outcomes\n * and rejects requests when the failure rate exceeds the threshold.\n *\n * States:\n * - **closed** — normal operation, requests flow through\n * - **open** — too many failures, requests are rejected with 503\n * - **half-open** — after resetTime, allows a few test requests through\n *\n * @example\n * ```typescript\n * import Relai from '@relai-fi/x402/server';\n * import { circuitBreaker } from '@relai-fi/x402/plugins';\n *\n * const relai = new Relai({\n *   network: 'base',\n *   plugins: [\n *     circuitBreaker({ failureThreshold: 5, resetTimeMs: 30000 }),\n *   ],\n * });\n * ```\n */\nexport function circuitBreaker(config?: CircuitBreakerPluginConfig): RelaiPlugin {\n  const failureThreshold = config?.failureThreshold ?? 5;\n  const resetTimeMs = config?.resetTimeMs ?? 30000;\n  const halfOpenSuccesses = config?.halfOpenSuccesses ?? 2;\n  const openStatus = config?.openStatus ?? 503;\n  const openMessage = config?.openMessage ?? 'Service temporarily unavailable (circuit open). Please try again later.';\n  const failureCodes = new Set(config?.failureCodes ?? [500, 502, 503, 504]);\n  const scope = config?.scope ?? 'per-path';\n\n  const circuits = new Map<string, CircuitEntry>();\n\n  function getKey(path: string): string {\n    return scope === 'global' ? '__global__' : path;\n  }\n\n  function getCircuit(key: string): CircuitEntry {\n    let entry = circuits.get(key);\n    if (!entry) {\n      entry = { state: 'closed', failures: 0, successes: 0, lastFailureAt: 0 };\n      circuits.set(key, entry);\n    }\n    return entry;\n  }\n\n  function recordFailure(key: string): void {\n    const circuit = getCircuit(key);\n    circuit.failures++;\n    circuit.successes = 0;\n    circuit.lastFailureAt = Date.now();\n\n    if (circuit.failures >= failureThreshold) {\n      circuit.state = 'open';\n    }\n  }\n\n  function recordSuccess(key: string): void {\n    const circuit = getCircuit(key);\n    if (circuit.state === 'half-open') {\n      circuit.successes++;\n      if (circuit.successes >= halfOpenSuccesses) {\n        // Enough successes in half-open → close the circuit\n        circuit.state = 'closed';\n        circuit.failures = 0;\n        circuit.successes = 0;\n      }\n    } else {\n      // Reset failures on success in closed state\n      circuit.failures = 0;\n      circuit.successes = 0;\n    }\n  }\n\n  return {\n    name: 'circuit-breaker',\n\n    async beforePaymentCheck(_req, ctx): Promise<PluginResult> {\n      const key = getKey(ctx.path || '/');\n      const circuit = getCircuit(key);\n      const now = Date.now();\n\n      // Check if open circuit should transition to half-open\n      if (circuit.state === 'open' && (now - circuit.lastFailureAt) >= resetTimeMs) {\n        circuit.state = 'half-open';\n        circuit.successes = 0;\n      }\n\n      if (circuit.state === 'open') {\n        const retryAfter = Math.max(1, Math.ceil((resetTimeMs - (now - circuit.lastFailureAt)) / 1000));\n        return {\n          reject: true,\n          rejectStatus: openStatus,\n          rejectMessage: openMessage,\n          headers: {\n            'X-Circuit-State': 'open',\n            'X-Circuit-Failures': String(circuit.failures),\n            'Retry-After': String(retryAfter),\n          },\n        };\n      }\n\n      return {\n        headers: {\n          'X-Circuit-State': circuit.state,\n          ...(circuit.state === 'half-open' ? { 'X-Circuit-Successes': String(circuit.successes) } : {}),\n        },\n      };\n    },\n\n    async afterSettled(_req, result, ctx): Promise<void> {\n      const key = getKey(ctx.path || '/');\n\n      // Check if the settlement itself failed or returned error codes\n      if (!result.success) {\n        recordFailure(key);\n        return;\n      }\n\n      // Check response status if available\n      const status = (result as any).statusCode || (result as any).status;\n      if (status && failureCodes.has(status)) {\n        recordFailure(key);\n      } else {\n        recordSuccess(key);\n      }\n    },\n  };\n}\n\n\n// ============================================================================\n// Refund Plugin\n// ============================================================================\n\nexport interface RefundPluginConfig {\n  /**\n   * HTTP status codes that trigger a refund/credit after payment.\n   * Default: [500, 502, 503, 504]\n   */\n  triggerCodes?: number[];\n  /**\n   * What to do when a refund is triggered.\n   * - 'credit': record a credit that the free-tier plugin can consume later\n   * - 'log': just log the event (useful with a custom onRefund callback)\n   * Default: 'credit'\n   */\n  mode?: 'credit' | 'log';\n  /**\n   * Called whenever a refund event is triggered.\n   * Use for external refund processing, logging, or notifications.\n   */\n  onRefund?: (event: RefundEvent) => void | Promise<void>;\n  /**\n   * Also trigger refund when settlement itself fails (result.success === false).\n   * Default: true\n   */\n  refundOnSettlementFailure?: boolean;\n}\n\nexport interface RefundEvent {\n  /** Buyer wallet address */\n  payer: string;\n  /** Transaction ID of the original payment */\n  transactionId: string;\n  /** Network used */\n  network: string;\n  /** Amount paid in USD */\n  amount: number;\n  /** Request path */\n  path: string;\n  /** Reason for the refund */\n  reason: 'endpoint_error' | 'settlement_failure' | 'timeout';\n  /** HTTP status code that triggered the refund (if applicable) */\n  statusCode?: number;\n  /** Timestamp */\n  timestamp: number;\n}\n\n/**\n * Refund plugin — automatically handles refund/credit when a paid request fails.\n *\n * After payment settles, if the endpoint returns an error (e.g. 500),\n * the plugin records a credit or calls your custom `onRefund` handler.\n * Credits can be consumed by the free-tier plugin on the next request.\n *\n * @example\n * ```typescript\n * import Relai from '@relai-fi/x402/server';\n * import { refund } from '@relai-fi/x402/plugins';\n *\n * const relai = new Relai({\n *   network: 'base',\n *   plugins: [\n *     refund({\n *       triggerCodes: [500, 502, 503],\n *       onRefund: (event) => {\n *         console.log(`Refund for ${event.payer}: $${event.amount} on ${event.path}`);\n *         // Notify buyer, record in DB, etc.\n *       },\n *     }),\n *   ],\n * });\n * ```\n */\nexport function refund(config?: RefundPluginConfig): RelaiPlugin {\n  const triggerCodes = new Set(config?.triggerCodes ?? [500, 502, 503, 504]);\n  const mode = config?.mode ?? 'credit';\n  const onRefund = config?.onRefund;\n  const refundOnSettlementFailure = config?.refundOnSettlementFailure ?? true;\n\n  // In-memory credit ledger: payer → number of credits\n  const credits = new Map<string, number>();\n\n  return {\n    name: 'refund',\n\n    async beforePaymentCheck(req, ctx): Promise<PluginResult> {\n      // If buyer has credits from previous refunds, skip payment\n      if (mode === 'credit') {\n        const buyerId =\n          req.headers?.['x-buyer-id'] ||\n          req.headers?.['authorization']?.replace(/^Bearer\\s+/i, '') ||\n          req.ip ||\n          req.socket?.remoteAddress ||\n          'unknown';\n\n        const buyerCredits = credits.get(buyerId) || 0;\n        if (buyerCredits > 0) {\n          credits.set(buyerId, buyerCredits - 1);\n          return {\n            skip: true,\n            headers: {\n              'X-Refund-Credit': 'applied',\n              'X-Refund-Credits-Remaining': String(buyerCredits - 1),\n            },\n            meta: {\n              refundCredit: true,\n              creditsRemaining: buyerCredits - 1,\n            },\n          };\n        }\n      }\n\n      return {};\n    },\n\n    async afterSettled(req, result, ctx): Promise<void> {\n      const payer = result.payer || 'unknown';\n      const transactionId = result.transaction || '';\n\n      // Settlement failure\n      if (!result.success && refundOnSettlementFailure) {\n        const event: RefundEvent = {\n          payer,\n          transactionId,\n          network: ctx.network,\n          amount: ctx.price,\n          path: ctx.path,\n          reason: 'settlement_failure',\n          timestamp: Date.now(),\n        };\n\n        if (mode === 'credit') {\n          const buyerId =\n            req.headers?.['x-buyer-id'] ||\n            req.ip ||\n            req.socket?.remoteAddress ||\n            'unknown';\n          credits.set(buyerId, (credits.get(buyerId) || 0) + 1);\n        }\n\n        try {\n          await onRefund?.(event);\n        } catch (err) {\n          console.warn('[relai:refund] onRefund callback error:', err);\n        }\n        return;\n      }\n\n      // Check for endpoint error codes in settlement result\n      const status = (result as any).statusCode || (result as any).status;\n      if (status && triggerCodes.has(status)) {\n        const event: RefundEvent = {\n          payer,\n          transactionId,\n          network: ctx.network,\n          amount: ctx.price,\n          path: ctx.path,\n          reason: 'endpoint_error',\n          statusCode: status,\n          timestamp: Date.now(),\n        };\n\n        if (mode === 'credit') {\n          const buyerId =\n            req.headers?.['x-buyer-id'] ||\n            req.ip ||\n            req.socket?.remoteAddress ||\n            'unknown';\n          credits.set(buyerId, (credits.get(buyerId) || 0) + 1);\n        }\n\n        try {\n          await onRefund?.(event);\n        } catch (err) {\n          console.warn('[relai:refund] onRefund callback error:', err);\n        }\n      }\n    },\n  };\n}\n\n// Re-export for backwards-compatible import path '@relai-fi/x402/plugins'\nexport { submitRelayFeedback, type RelayFeedbackConfig } from './relay-feedback';\n\n// ============================================================================\n// Score Plugin\n// ============================================================================\n\nexport interface ScorePluginConfig {\n  /**\n   * ERC-8004 agentId (NFT tokenId) for this API.\n   * Find yours in the RelAI dashboard under \"On-chain Identity\".\n   */\n  agentId: string | number;\n  /**\n   * SKALE Base Sepolia RPC URL.\n   * Default: https://base-sepolia-testnet.skalenodes.com/v1/jubilant-horrible-ancha\n   */\n  rpcUrl?: string;\n  /**\n   * ERC-8004 IdentityRegistry contract address.\n   * Default: process.env.ERC8004_IDENTITY_REGISTRY\n   */\n  identityRegistryAddress?: string;\n  /**\n   * ERC-8004 ReputationRegistry contract address.\n   * Default: process.env.ERC8004_REPUTATION_REGISTRY\n   */\n  reputationRegistryAddress?: string;\n  /**\n   * Cache TTL in ms (default: 5 minutes).\n   */\n  cacheTtlMs?: number;\n}\n\ninterface CachedScore {\n  score: Record<string, unknown>;\n  expiresAt: number;\n}\n\nconst SCORE_IDENTITY_ABI = [\n  'function ownerOf(uint256 tokenId) external view returns (address)',\n];\n\nconst SCORE_FEEDBACK_TOPIC = ethers.id(\n  'NewFeedback(uint256,address,uint64,int128,uint8,string,string,string,string,string,bytes32)'\n);\n\nconst SCORE_FEEDBACK_IFACE = new ethers.Interface([\n  'event NewFeedback(uint256 indexed agentId, address indexed giver, uint64 index, int128 value, uint8 valueDecimals, string tag1, string tag2, string endpoint, string feedbackURI, string responseURI, bytes32 feedbackHash)',\n]);\n\nconst SCORE_BATCH = 1900;\nconst SCORE_HISTORY = 10000;\n\n/**\n * Score plugin — fetches ERC-8004 on-chain reputation for your API\n * directly via RPC and injects it into the 402 response `extensions.score`.\n *\n * Agents can read the score **before paying**, enabling trust-based routing:\n * - `feedbackCount` — number of on-chain feedback entries\n * - `successRate` — 0–100 (% of successful calls)\n * - `avgResponseMs` — average response time in milliseconds\n * - `verified` — true if agentId is registered on-chain\n *\n * @example\n * ```typescript\n * import Relai from '@relai-fi/x402/server';\n * import { score } from '@relai-fi/x402/plugins';\n *\n * const relai = new Relai({\n *   network: 'base',\n *   plugins: [\n *     score({ agentId: '5' }),\n *   ],\n * });\n * ```\n */\nexport function score(config: ScorePluginConfig): RelaiPlugin {\n  const rawAgentId = config.agentId\n    ?? (typeof process !== 'undefined' ? process.env?.ERC8004_AGENT_ID : undefined);\n\n  if (!rawAgentId || String(rawAgentId) === 'undefined') {\n    console.warn('[relai:score] ERC8004_AGENT_ID not set — score plugin is a no-op');\n    return { name: 'score', async onInit() {} };\n  }\n\n  const agentId = String(rawAgentId);\n  const cacheTtlMs = config.cacheTtlMs ?? 5 * 60 * 1000;\n  const rpcUrl = config.rpcUrl\n    ?? (typeof process !== 'undefined' ? process.env?.ERC8004_RPC_URL : undefined)\n    ?? 'https://base-sepolia-testnet.skalenodes.com/v1/jubilant-horrible-ancha';\n\n  let cached: CachedScore | null = null;\n\n  function getContracts() {\n    const identityAddress = config.identityRegistryAddress\n      ?? (typeof process !== 'undefined' ? process.env?.ERC8004_IDENTITY_REGISTRY : undefined);\n    const reputationAddress = config.reputationRegistryAddress\n      ?? (typeof process !== 'undefined' ? process.env?.ERC8004_REPUTATION_REGISTRY : undefined);\n    if (!identityAddress || !reputationAddress) return null;\n    const provider = new ethers.JsonRpcProvider(rpcUrl);\n    const identity = new ethers.Contract(identityAddress, SCORE_IDENTITY_ABI, provider);\n    return { provider, identity, reputationAddress };\n  }\n\n  async function fetchScore(): Promise<Record<string, unknown> | null> {\n    if (cached && cached.expiresAt > Date.now()) {\n      return cached.score;\n    }\n\n    const contracts = getContracts();\n    if (!contracts) {\n      console.warn(`[relai:score] ERC8004_IDENTITY_REGISTRY or ERC8004_REPUTATION_REGISTRY not set — score disabled`);\n      return null;\n    }\n\n    const { provider, identity, reputationAddress } = contracts;\n    const bigId = BigInt(agentId);\n\n    try {\n      let verified = false;\n      try {\n        const owner = await identity.ownerOf(bigId);\n        verified = !!owner;\n      } catch {\n        // Not registered\n      }\n\n      if (!verified) {\n        const scoreObj = { feedbackCount: 0, successRate: null, avgResponseMs: null, verified: false, agentId, source: 'erc8004-skale' };\n        cached = { score: scoreObj, expiresAt: Date.now() + cacheTtlMs };\n        return scoreObj;\n      }\n\n      const latest = await provider.getBlockNumber();\n      const agentIdTopic = ethers.zeroPadValue(ethers.toBeHex(bigId), 32);\n      const logs: any[] = [];\n\n      for (let from = Math.max(0, latest - SCORE_HISTORY); from <= latest; from += SCORE_BATCH) {\n        const to = Math.min(from + SCORE_BATCH - 1, latest);\n        const chunk = await provider.getLogs({\n          address: reputationAddress,\n          topics: [SCORE_FEEDBACK_TOPIC, agentIdTopic],\n          fromBlock: from,\n          toBlock: to,\n        });\n        logs.push(...chunk);\n      }\n\n      const srValues: number[] = [];\n      const rtValues: number[] = [];\n      const endpointMap: Record<string, { sr: number[]; rt: number[] }> = {};\n\n      for (const log of logs) {\n        try {\n          const p = SCORE_FEEDBACK_IFACE.parseLog({ topics: log.topics as string[], data: log.data });\n          if (!p) continue;\n          const val = Number(p.args.value) / Math.pow(10, Number(p.args.valueDecimals));\n          const ep: string = p.args.endpoint || '';\n          if (!endpointMap[ep]) endpointMap[ep] = { sr: [], rt: [] };\n          if (p.args.tag1 === 'successRate') { srValues.push(val); endpointMap[ep].sr.push(val); }\n          else if (p.args.tag1 === 'responseTime') { rtValues.push(val); endpointMap[ep].rt.push(val); }\n        } catch { /* skip */ }\n      }\n\n      const feedbackCount = srValues.length + rtValues.length;\n      const successRate = srValues.length\n        ? Math.round(srValues.reduce((a, b) => a + b, 0) / srValues.length)\n        : null;\n      const avgResponseMs = rtValues.length\n        ? Math.round(rtValues.reduce((a, b) => a + b, 0) / rtValues.length)\n        : null;\n\n      const endpoints: Record<string, { feedbackCount: number; successRate: number | null; avgResponseMs: number | null }> = {};\n      for (const [ep, { sr, rt }] of Object.entries(endpointMap)) {\n        endpoints[ep] = {\n          feedbackCount: sr.length + rt.length,\n          successRate: sr.length ? Math.round(sr.reduce((a, b) => a + b, 0) / sr.length) : null,\n          avgResponseMs: rt.length ? Math.round(rt.reduce((a, b) => a + b, 0) / rt.length) : null,\n        };\n      }\n\n      const scoreObj = { feedbackCount, successRate, avgResponseMs, verified: true, agentId, source: 'erc8004-skale', endpoints };\n      cached = { score: scoreObj, expiresAt: Date.now() + cacheTtlMs };\n      return scoreObj;\n    } catch (err) {\n      console.warn(`[relai:score] fetchScore RPC error for agentId=${agentId}:`, err);\n      return null;\n    }\n  }\n\n  return {\n    name: 'score',\n\n    async onInit() {\n      const s = await fetchScore();\n      if (s) {\n        console.log(`[relai:score] Initialized — agentId=${agentId}, feedback=${s.feedbackCount}, successRate=${s.successRate}%, avgResp=${s.avgResponseMs}ms`);\n      } else {\n        console.warn(`[relai:score] Could not load score for agentId=${agentId} — score will be omitted from 402 responses`);\n      }\n    },\n\n    enrich402Response(response: any, _ctx: PluginContext) {\n      const scoreData = cached?.score ?? null;\n      if (!scoreData) return response;\n\n      return {\n        ...response,\n        extensions: {\n          ...(response.extensions ?? {}),\n          score: scoreData,\n        },\n      };\n    },\n  };\n}\n\n// ============================================================================\n// Feedback Plugin\n// ============================================================================\n\nexport interface FeedbackPluginConfig {\n  /**\n   * ERC-8004 agentId (NFT tokenId) for this API.\n   */\n  agentId: string | number;\n  /**\n   * Private key of the wallet that signs feedback transactions.\n   * The wallet must hold CREDIT tokens on SKALE Base to pay gas.\n   * Default: process.env.BACKEND_WALLET_PRIVATE_KEY\n   */\n  walletPrivateKey?: string;\n  /**\n   * SKALE Base Sepolia RPC URL.\n   * Default: process.env.ERC8004_RPC_URL or SKALE Base Sepolia public RPC.\n   */\n  rpcUrl?: string;\n  /**\n   * ERC-8004 ReputationRegistry contract address.\n   * Default: process.env.ERC8004_REPUTATION_REGISTRY\n   */\n  reputationRegistryAddress?: string;\n  /**\n   * Tag2 label for all feedback entries (default: 'x402').\n   */\n  tag2?: string;\n  /**\n   * Whether to submit successRate feedback (default: true).\n   * Value: 1 (success) or 0 (failure).\n   */\n  submitSuccessRate?: boolean;\n  /**\n   * Whether to submit responseTime feedback (default: true).\n   * Value: milliseconds elapsed since request start.\n   * Requires req.x402StartTime (set automatically if using Relai.protect()).\n   */\n  submitResponseTime?: boolean;\n}\n\nconst FEEDBACK_REPUTATION_ABI = [\n  'function giveFeedback(uint256 agentId, int128 value, uint8 valueDecimals, string tag1, string tag2, string endpoint, string feedbackURI, bytes32 feedbackHash) external',\n];\n\n/**\n * Feedback plugin — submits ERC-8004 on-chain reputation after every successful x402 settlement.\n *\n * Records `successRate` and `responseTime` signals to the ReputationRegistry on SKALE Base.\n * This is the mechanism that **builds** the score that `score()` plugin later reads.\n *\n * Runs fire-and-forget — never blocks the response.\n *\n * @example\n * ```typescript\n * import Relai from '@relai-fi/x402/server';\n * import { score, feedback } from '@relai-fi/x402/plugins';\n *\n * const relai = new Relai({\n *   network: 'base',\n *   plugins: [\n *     score({ agentId: '5' }),\n *     feedback({ agentId: '5' }), // needs BACKEND_WALLET_PRIVATE_KEY in env\n *   ],\n * });\n * ```\n */\nexport function feedback(config: FeedbackPluginConfig): RelaiPlugin {\n  const rawAgentId = config.agentId\n    ?? (typeof process !== 'undefined' ? process.env?.ERC8004_AGENT_ID : undefined);\n\n  if (!rawAgentId || String(rawAgentId) === 'undefined') {\n    console.warn('[relai:feedback] ERC8004_AGENT_ID not set — feedback plugin is a no-op');\n    return { name: 'feedback', async onInit() {} };\n  }\n\n  const agentId = String(rawAgentId);\n  const tag2 = config.tag2 ?? 'x402';\n  const submitSuccessRate = config.submitSuccessRate ?? true;\n  const submitResponseTime = config.submitResponseTime ?? true;\n\n  let _reputation: any = null;\n\n  function getReputation() {\n    if (_reputation) return _reputation;\n\n    const privateKey = config.walletPrivateKey\n      ?? (typeof process !== 'undefined' ? process.env?.BACKEND_WALLET_PRIVATE_KEY : undefined);\n    const reputationAddress = config.reputationRegistryAddress\n      ?? (typeof process !== 'undefined' ? process.env?.ERC8004_REPUTATION_REGISTRY : undefined);\n    const rpcUrl = config.rpcUrl\n      ?? (typeof process !== 'undefined' ? process.env?.ERC8004_RPC_URL : undefined)\n      ?? 'https://base-sepolia-testnet.skalenodes.com/v1/jubilant-horrible-ancha';\n\n    if (!privateKey || !reputationAddress) return null;\n\n    const provider = new ethers.JsonRpcProvider(rpcUrl);\n    const signer = new ethers.Wallet(privateKey, provider);\n    _reputation = new ethers.Contract(reputationAddress, FEEDBACK_REPUTATION_ABI, signer);\n    return _reputation;\n  }\n\n  function submitAsync(tag1: string, value: number, valueDecimals: number, endpoint: string) {\n    const reputation = getReputation();\n    if (!reputation) return;\n\n    (async () => {\n      try {\n        const tx = await reputation.giveFeedback(\n          BigInt(agentId),\n          BigInt(Math.round(value)),\n          valueDecimals,\n          tag1,\n          tag2,\n          endpoint,\n          '',\n          ethers.ZeroHash,\n        );\n        console.log(`[relai:feedback] Submitted agentId=${agentId} tag=${tag1} value=${value} tx=${tx.hash}`);\n      } catch (err: any) {\n        console.warn(`[relai:feedback] giveFeedback failed (non-fatal): ${err?.message}`);\n      }\n    })();\n  }\n\n  return {\n    name: 'feedback',\n\n    async onInit() {\n      const reputation = getReputation();\n      if (reputation) {\n        console.log(`[relai:feedback] Initialized — agentId=${agentId}, submitSuccessRate=${submitSuccessRate}, submitResponseTime=${submitResponseTime}`);\n      } else {\n        console.warn(`[relai:feedback] BACKEND_WALLET_PRIVATE_KEY or ERC8004_REPUTATION_REGISTRY not set — feedback disabled`);\n      }\n    },\n\n    async afterSettled(req: any, result: SettleResult, ctx: PluginContext) {\n      const endpoint = ctx.path ?? '';\n\n      // successRate: 1 = success, 0 = failure\n      if (submitSuccessRate) {\n        submitAsync('successRate', result.success ? 1 : 0, 0, endpoint);\n      }\n\n      // responseTime in ms — use req.x402StartTime if available\n      if (submitResponseTime) {\n        const startTime = req?.x402StartTime ?? req?.x402StartedAt;\n        const responseTimeMs = startTime ? Date.now() - Number(startTime) : 0;\n        if (responseTimeMs > 0) {\n          submitAsync('responseTime', responseTimeMs, 0, endpoint);\n        }\n      }\n    },\n  };\n}\n\n// ============================================================================\n// Solana Feedback Plugin (8004-solana)\n// ============================================================================\n\nexport interface SolanaFeedbackPluginConfig {\n  /**\n   * Solana MPL Core NFT public key (base58) of the registered agent.\n   * This is the `solanaAgentAsset` stored after calling /api/solana8004/register.\n   */\n  assetPubkey: string;\n  /**\n   * Private key of the feedback wallet — base58 or JSON array format.\n   * Should differ from the owner's registration wallet (avoids self-feedback restriction).\n   * Default: process.env.SOLANA_8004_FEEDBACK_KEY ?? process.env.SOLANA_8004_PRIVATE_KEY\n   */\n  feedbackWalletPrivateKey?: string;\n  /**\n   * Solana cluster to use.\n   * Default: process.env.SOLANA_8004_CLUSTER ?? 'mainnet-beta'\n   */\n  cluster?: 'mainnet-beta' | 'devnet';\n  /**\n   * Custom Solana RPC URL (Helius / QuickNode recommended).\n   * Default: process.env.SOLANA_8004_RPC_URL\n   */\n  rpcUrl?: string;\n  /**\n   * Whether to submit successRate feedback (default: true).\n   */\n  submitSuccessRate?: boolean;\n  /**\n   * Whether to submit responseTime feedback (default: true).\n   */\n  submitResponseTime?: boolean;\n}\n\n/**\n * Solana Feedback plugin — submits 8004-solana on-chain reputation after x402 settlement.\n *\n * Uses the native Solana `8004-solana` program (MPL Core NFT registry),\n * separate from the SKALE EVM ReputationRegistry used by `feedback()`.\n *\n * Requires `8004-solana` package to be installed:\n * ```\n * npm install 8004-solana\n * ```\n *\n * @example\n * ```typescript\n * import Relai from '@relai-fi/x402/server';\n * import { solanaFeedback } from '@relai-fi/x402/plugins';\n *\n * const relai = new Relai({\n *   network: 'solana',\n *   plugins: [\n *     solanaFeedback({\n *       assetPubkey: process.env.SOLANA_AGENT_ASSET!, // e.g. 'GH93tGR8...'\n *     }),\n *   ],\n * });\n * ```\n */\nexport function solanaFeedback(config: SolanaFeedbackPluginConfig): RelaiPlugin {\n  const assetPubkey = config.assetPubkey;\n  const submitSuccessRate = config.submitSuccessRate ?? true;\n  const submitResponseTime = config.submitResponseTime ?? true;\n\n  function getPrivateKey(): string | null {\n    return config.feedbackWalletPrivateKey\n      ?? (typeof process !== 'undefined'\n        ? process.env?.SOLANA_8004_FEEDBACK_KEY ?? process.env?.SOLANA_8004_PRIVATE_KEY\n        : undefined)\n      ?? null;\n  }\n\n  function getCluster(): string {\n    return config.cluster\n      ?? (typeof process !== 'undefined' ? process.env?.SOLANA_8004_CLUSTER : undefined)\n      ?? 'mainnet-beta';\n  }\n\n  function getRpcUrl(): string | undefined {\n    return config.rpcUrl\n      ?? (typeof process !== 'undefined' ? process.env?.SOLANA_8004_RPC_URL : undefined);\n  }\n\n  async function buildSDK(): Promise<any> {\n    let SolanaSDK: any;\n    try {\n      const mod = await import('8004-solana' as any);\n      SolanaSDK = mod.SolanaSDK;\n    } catch {\n      console.warn('[relai:solanaFeedback] 8004-solana package not installed — run: npm install 8004-solana');\n      return null;\n    }\n\n    const privateKeyRaw = getPrivateKey();\n    if (!privateKeyRaw) return null;\n\n    let secretKey: Uint8Array;\n    try {\n      const parsed = JSON.parse(privateKeyRaw);\n      if (Array.isArray(parsed)) {\n        secretKey = Uint8Array.from(parsed);\n      } else {\n        throw new Error('not array');\n      }\n    } catch {\n      // base58\n      try {\n        const bs58Mod = await import('bs58' as any);\n        const decode = bs58Mod.default?.decode ?? bs58Mod.decode;\n        secretKey = decode(privateKeyRaw);\n      } catch {\n        console.warn('[relai:solanaFeedback] Could not parse feedbackWalletPrivateKey');\n        return null;\n      }\n    }\n\n    const { Keypair } = await import('@solana/web3.js');\n    const signer = Keypair.fromSecretKey(secretKey);\n    const cluster = getCluster();\n    const rpcUrl = getRpcUrl();\n\n    return new SolanaSDK({ cluster, signer, ...(rpcUrl ? { rpcUrl } : {}) });\n  }\n\n  function submitAsync(isSuccess: boolean, responseTimeMs: number, endpoint: string) {\n    (async () => {\n      try {\n        const sdk = await buildSDK();\n        if (!sdk) return;\n\n        const { PublicKey } = await import('@solana/web3.js');\n        const pubkey = new PublicKey(assetPubkey);\n\n        if (submitSuccessRate) {\n          try {\n            const result = await sdk.giveFeedback(pubkey, {\n              value: String(isSuccess ? 10000 : 0),\n              tag1: 'successRate',\n              tag2: 'x402',\n              ...(endpoint ? { endpoint } : {}),\n            });\n            console.log(`[relai:solanaFeedback] successRate submitted asset=${assetPubkey.slice(0, 8)}... tx=${result.signature}`);\n          } catch (err: any) {\n            console.warn(`[relai:solanaFeedback] successRate failed (non-fatal): ${err?.message}`);\n          }\n        }\n\n        if (submitResponseTime && responseTimeMs > 0) {\n          try {\n            const result = await sdk.giveFeedback(pubkey, {\n              value: String(Math.round(responseTimeMs)),\n              tag1: 'responseTime',\n              tag2: 'x402',\n              ...(endpoint ? { endpoint } : {}),\n            });\n            console.log(`[relai:solanaFeedback] responseTime submitted asset=${assetPubkey.slice(0, 8)}... ms=${responseTimeMs} tx=${result.signature}`);\n          } catch (err: any) {\n            console.warn(`[relai:solanaFeedback] responseTime failed (non-fatal): ${err?.message}`);\n          }\n        }\n      } catch (err: any) {\n        console.warn(`[relai:solanaFeedback] feedback error (non-fatal): ${err?.message}`);\n      }\n    })();\n  }\n\n  return {\n    name: 'solanaFeedback',\n\n    async onInit() {\n      const privateKey = getPrivateKey();\n      if (!privateKey) {\n        console.warn('[relai:solanaFeedback] SOLANA_8004_FEEDBACK_KEY not set — Solana feedback disabled');\n        return;\n      }\n      try {\n        await import('8004-solana' as any);\n        console.log(`[relai:solanaFeedback] Initialized — asset=${assetPubkey.slice(0, 8)}... cluster=${getCluster()}`);\n      } catch {\n        console.warn('[relai:solanaFeedback] 8004-solana package not installed — run: npm install 8004-solana');\n      }\n    },\n\n    async afterSettled(req: any, result: SettleResult, ctx: PluginContext) {\n      const endpoint = ctx.path ?? '';\n      const startTime = req?.x402StartTime ?? req?.x402StartedAt;\n      const responseTimeMs = startTime ? Date.now() - Number(startTime) : 0;\n\n      submitAsync(result.success, responseTimeMs, endpoint);\n    },\n  };\n}\n\n// ============================================================================\n// Solana Score Plugin (8004-solana)\n// ============================================================================\n\nexport interface SolanaScorePluginConfig {\n  /**\n   * Solana MPL Core NFT public key (base58) of the registered agent.\n   * This is the `solanaAgentAsset` stored after calling /api/solana8004/register.\n   */\n  assetPubkey: string;\n  /**\n   * Solana cluster to use.\n   * Default: process.env.SOLANA_8004_CLUSTER ?? 'mainnet-beta'\n   */\n  cluster?: 'mainnet-beta' | 'devnet';\n  /**\n   * Custom Solana RPC URL (Helius / QuickNode recommended).\n   * Default: process.env.SOLANA_8004_RPC_URL\n   */\n  rpcUrl?: string;\n  /**\n   * Cache TTL in ms (default: 5 minutes).\n   */\n  cacheTtlMs?: number;\n}\n\n/**\n * Solana Score plugin — fetches 8004-solana on-chain reputation and injects it\n * into the 402 response `extensions.score` before the client pays.\n *\n * Mirrors the EVM `score()` plugin but reads from the native Solana program\n * instead of the SKALE EVM ReputationRegistry.\n *\n * @example\n * ```typescript\n * import Relai from '@relai-fi/x402/server';\n * import { solanaScore } from '@relai-fi/x402/plugins';\n *\n * const relai = new Relai({\n *   network: 'solana',\n *   plugins: [\n *     solanaScore({ assetPubkey: process.env.SOLANA_AGENT_ASSET! }),\n *   ],\n * });\n * ```\n */\nexport function solanaScore(config: SolanaScorePluginConfig): RelaiPlugin {\n  const assetPubkey = config.assetPubkey;\n  const cacheTtlMs = config.cacheTtlMs ?? 5 * 60 * 1000;\n\n  if (!assetPubkey || String(assetPubkey) === 'undefined') {\n    console.warn('[relai:solanaScore] assetPubkey not set — solanaScore plugin is a no-op');\n    return { name: 'solanaScore', async onInit() {} };\n  }\n\n  let cached: CachedScore | null = null;\n\n  function getCluster(): string {\n    return config.cluster\n      ?? (typeof process !== 'undefined' ? process.env?.SOLANA_8004_CLUSTER : undefined)\n      ?? 'mainnet-beta';\n  }\n\n  function getRpcUrl(): string | undefined {\n    return config.rpcUrl\n      ?? (typeof process !== 'undefined' ? process.env?.SOLANA_8004_RPC_URL : undefined);\n  }\n\n  async function fetchScore(): Promise<Record<string, unknown> | null> {\n    if (cached && Date.now() < cached.expiresAt) return cached.score;\n\n    let SolanaSDK: any;\n    try {\n      const mod = await import('8004-solana' as any);\n      SolanaSDK = mod.SolanaSDK;\n    } catch {\n      console.warn('[relai:solanaScore] 8004-solana package not installed — run: npm install 8004-solana');\n      return null;\n    }\n\n    try {\n      const { PublicKey } = await import('@solana/web3.js');\n      const cluster = getCluster();\n      const rpcUrl = getRpcUrl();\n      const sdk = new SolanaSDK({ cluster, ...(rpcUrl ? { rpcUrl } : {}) });\n      const pubkey = new PublicKey(assetPubkey);\n\n      // Try getReputationSummary first (lightweight)\n      let feedbackCount = 0;\n      let successRate: number | null = null;\n      let avgResponseMs: number | null = null;\n      const endpoints: Record<string, { feedbackCount: number; successRate: number | null; avgResponseMs: number | null }> = {};\n\n      // Try to read all feedback for per-endpoint breakdown\n      let allFeedbacks: any[] = [];\n      try {\n        allFeedbacks = await sdk.readAllFeedback(pubkey);\n      } catch {\n        // fallback to summary only\n      }\n\n      if (allFeedbacks.length > 0) {\n        // Group by endpoint\n        const byEndpoint: Record<string, { successValues: number[]; responseTimes: number[] }> = {};\n        let globalSuccessValues: number[] = [];\n        let globalResponseTimes: number[] = [];\n\n        for (const entry of allFeedbacks) {\n          const tag1: string = entry.tag1 ?? '';\n          const rawValue = entry.value ?? entry.score ?? 0;\n          const value = typeof rawValue === 'bigint' ? Number(rawValue) : Number(rawValue);\n          const ep: string = entry.endpoint ?? '';\n\n          if (!byEndpoint[ep]) byEndpoint[ep] = { successValues: [], responseTimes: [] };\n\n          if (tag1 === 'successRate') {\n            globalSuccessValues.push(value);\n            byEndpoint[ep].successValues.push(value);\n          } else if (tag1 === 'responseTime') {\n            globalResponseTimes.push(value);\n            byEndpoint[ep].responseTimes.push(value);\n          }\n        }\n\n        feedbackCount = globalSuccessValues.length;\n        if (feedbackCount > 0) {\n          const avgSuccess = globalSuccessValues.reduce((a, b) => a + b, 0) / feedbackCount;\n          // value=10000 means success (100%), value=0 means failure\n          successRate = Math.round((avgSuccess / 10000) * 100);\n        }\n        if (globalResponseTimes.length > 0) {\n          avgResponseMs = Math.round(globalResponseTimes.reduce((a, b) => a + b, 0) / globalResponseTimes.length);\n        }\n\n        for (const [ep, data] of Object.entries(byEndpoint)) {\n          const epCount = data.successValues.length;\n          const epAvgSuccess = epCount > 0\n            ? data.successValues.reduce((a, b) => a + b, 0) / epCount\n            : null;\n          const epAvgMs = data.responseTimes.length > 0\n            ? Math.round(data.responseTimes.reduce((a, b) => a + b, 0) / data.responseTimes.length)\n            : null;\n          endpoints[ep || '/'] = {\n            feedbackCount: epCount,\n            successRate: epAvgSuccess !== null ? Math.round((epAvgSuccess / 10000) * 100) : null,\n            avgResponseMs: epAvgMs,\n          };\n        }\n      } else {\n        // Fallback to summary\n        try {\n          const summary = await sdk.getReputationSummary(pubkey);\n          feedbackCount = summary.count ?? 0;\n          if (feedbackCount > 0) {\n            const avg = summary.averageScore ?? 0;\n            successRate = Math.round((avg / 10000) * 100);\n          }\n        } catch {\n          // no data\n        }\n      }\n\n      const scoreObj = {\n        feedbackCount,\n        successRate,\n        avgResponseMs,\n        assetPubkey,\n        source: '8004-solana',\n        cluster,\n        ...(Object.keys(endpoints).length > 0 ? { endpoints } : {}),\n      };\n\n      cached = { score: scoreObj, expiresAt: Date.now() + cacheTtlMs };\n      return scoreObj;\n    } catch (err: any) {\n      console.warn(`[relai:solanaScore] fetch error (non-fatal): ${err?.message}`);\n      return null;\n    }\n  }\n\n  return {\n    name: 'solanaScore',\n\n    async onInit() {\n      const s = await fetchScore();\n      if (s) {\n        console.log(`[relai:solanaScore] Initialized — asset=${assetPubkey.slice(0, 8)}... feedbackCount=${(s as any).feedbackCount} cluster=${getCluster()}`);\n      } else {\n        console.warn(`[relai:solanaScore] Could not load score for asset=${assetPubkey.slice(0, 8)}... — score will be omitted from 402 responses`);\n      }\n    },\n\n    enrich402Response(response: any) {\n      const scoreData = cached?.score ?? null;\n      if (!scoreData) return response;\n      return {\n        ...response,\n        extensions: {\n          ...(response.extensions ?? {}),\n          solanaScore: scoreData,\n        },\n      };\n    },\n  };\n}\n","// src/relay-feedback.ts\n// Standalone utility for submitting ERC-8004 feedback about third-party APIs.\n// Not a plugin — call directly from your relay/aggregator application code.\n\nimport { ethers } from 'ethers';\n\nexport interface RelayFeedbackConfig {\n  /**\n   * ERC-8004 agentId (NFT tokenId) of the **target API** you are calling.\n   */\n  agentId: string | number;\n  /**\n   * Whether the API call succeeded.\n   */\n  success: boolean;\n  /**\n   * Elapsed milliseconds for the API call.\n   */\n  responseTimeMs?: number;\n  /**\n   * Endpoint path of the called API (e.g. '/v1/data').\n   */\n  endpoint?: string;\n  /**\n   * Private key of the **relay/third-party** wallet that signs feedback.\n   * MUST be different from the API owner's wallet — ReputationRegistry\n   * may restrict self-feedback. Wallet needs CREDIT tokens on SKALE Base.\n   * Default: process.env.FEEDBACK_WALLET_PRIVATE_KEY\n   */\n  feedbackWalletPrivateKey?: string;\n  /**\n   * SKALE Base Sepolia RPC URL.\n   * Default: process.env.ERC8004_RPC_URL or SKALE Base Sepolia public RPC.\n   */\n  rpcUrl?: string;\n  /**\n   * ERC-8004 ReputationRegistry contract address.\n   * Default: process.env.ERC8004_REPUTATION_REGISTRY\n   */\n  reputationRegistryAddress?: string;\n}\n\nconst RELAY_FEEDBACK_REPUTATION_ABI = [\n  'function giveFeedback(uint256 agentId, int128 value, uint8 valueDecimals, string tag1, string tag2, string endpoint, string feedbackURI, bytes32 feedbackHash) external',\n];\n\n/**\n * Submit ERC-8004 on-chain feedback about a **third-party API** you called.\n *\n * Call this fire-and-forget from your relay/aggregator after every external API call.\n * Uses a separate relay wallet (not the API owner's key) to avoid self-feedback restrictions.\n *\n * Records:\n * - `successRate`: 10000 (= 100%) on success, 0 on failure — 2 decimal places\n * - `responseTime`: elapsed milliseconds\n *\n * @example\n * ```typescript\n * import { submitRelayFeedback } from '@relai-fi/x402/relay-feedback';\n *\n * // after calling an external API:\n * const start = Date.now();\n * const result = await fetch('https://other-api.com/data');\n * submitRelayFeedback({\n *   agentId: '5',\n *   success: result.ok,\n *   responseTimeMs: Date.now() - start,\n *   endpoint: '/data',\n * });\n * ```\n */\nexport function submitRelayFeedback(config: RelayFeedbackConfig): void {\n  const agentId = String(config.agentId);\n  const endpoint = config.endpoint ?? '';\n  const responseTimeMs = config.responseTimeMs ?? 0;\n\n  const privateKey = config.feedbackWalletPrivateKey\n    ?? (typeof process !== 'undefined'\n      ? process.env?.FEEDBACK_WALLET_PRIVATE_KEY ?? process.env?.ERC8004_FEEDBACK_WALLET_PRIVATE_KEY\n      : undefined);\n  const reputationAddress = config.reputationRegistryAddress\n    ?? (typeof process !== 'undefined' ? process.env?.ERC8004_REPUTATION_REGISTRY : undefined);\n  const rpcUrl = config.rpcUrl\n    ?? (typeof process !== 'undefined' ? process.env?.ERC8004_RPC_URL : undefined)\n    ?? 'https://base-sepolia-testnet.skalenodes.com/v1/jubilant-horrible-ancha';\n\n  if (!privateKey || !reputationAddress) {\n    console.warn('[relai:submitRelayFeedback] FEEDBACK_WALLET_PRIVATE_KEY or ERC8004_REPUTATION_REGISTRY not set — skipping');\n    return;\n  }\n\n  const provider = new ethers.JsonRpcProvider(rpcUrl);\n  const signer = new ethers.Wallet(privateKey, provider);\n  const reputation = new ethers.Contract(reputationAddress, RELAY_FEEDBACK_REPUTATION_ABI, signer);\n  const id = BigInt(agentId);\n\n  (async () => {\n    const successValue = config.success ? 10000n : 0n;\n    try {\n      const srTx = await reputation.giveFeedback(\n        id, successValue, 2, 'successRate', '', endpoint, '', ethers.ZeroHash,\n      );\n      await srTx.wait();\n      console.log(`[relai:submitRelayFeedback] successRate confirmed agentId=${agentId} success=${config.success}`);\n    } catch (err: any) {\n      console.warn(`[relai:submitRelayFeedback] successRate failed (non-fatal): ${err?.message}`);\n    }\n\n    if (responseTimeMs > 0) {\n      try {\n        const rtTx = await reputation.giveFeedback(\n          id, BigInt(Math.max(0, Math.round(responseTimeMs))), 0, 'responseTime', '', endpoint, '', ethers.ZeroHash,\n        );\n        console.log(`[relai:submitRelayFeedback] responseTime sent agentId=${agentId} ms=${responseTimeMs} tx=${rtTx.hash}`);\n      } catch (err: any) {\n        console.warn(`[relai:submitRelayFeedback] responseTime failed (non-fatal): ${err?.message}`);\n      }\n    }\n  })();\n}\n"],"mappings":"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","names":["import_ethers","crypto","result","scoreObj"]}