@tangle-network/agent-gateway 0.3.5 → 0.5.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -30,7 +30,7 @@ async function verifyX402(spendAuthHeader, config, nonceStore) {
30
30
  const verified = await config.verifySigner(raw);
31
31
  if (!verified) return null;
32
32
  } else if (!config.demoMode) {
33
- console.warn("[agent-gateway] x402 verification running without verifySigner \u2014 set demoMode: true to suppress this warning");
33
+ return null;
34
34
  }
35
35
  return raw.commitment;
36
36
  } catch {
@@ -316,6 +316,11 @@ function generateRequestId() {
316
316
 
317
317
  // src/middleware.ts
318
318
  function createAgentGateway(config) {
319
+ if (!config.x402.verifySigner && !config.x402.demoMode) {
320
+ throw new Error(
321
+ "createAgentGateway: x402.verifySigner is required in production. For tests, set x402.demoMode: true explicitly."
322
+ );
323
+ }
319
324
  const gw = new Hono();
320
325
  const maxLen = config.maxMessageLength ?? 8e3;
321
326
  const rateLimitStore = config.rateLimitStore ?? new MemoryRateLimitStore();
@@ -495,6 +500,26 @@ function createAgentGateway(config) {
495
500
  if (!userMessage) {
496
501
  return c.json({ error: { message: "No user message provided", type: "invalid_request" } }, 400);
497
502
  }
503
+ if (config.authorizeConsumer) {
504
+ const authz = await config.authorizeConsumer(agent, {
505
+ method: paymentMethod,
506
+ consumerId,
507
+ keyId: keyInfo?.keyId,
508
+ requestId
509
+ });
510
+ if (!authz.allow) {
511
+ return c.json(
512
+ {
513
+ error: {
514
+ message: authz.reason,
515
+ type: "authorization_denied",
516
+ code: authz.code
517
+ }
518
+ },
519
+ { status: 403, headers: { "X-Request-Id": requestId } }
520
+ );
521
+ }
522
+ }
498
523
  const inputTokens = Math.ceil(userMessage.length / 4);
499
524
  let outputTokens = 0;
500
525
  const stream = new ReadableStream({
@@ -540,6 +565,7 @@ function createAgentGateway(config) {
540
565
  const ownerEarned = totalCost * (1 - agent.platformFeePercent);
541
566
  const platformFee = totalCost * agent.platformFeePercent;
542
567
  const usageEvent = {
568
+ requestId: ctx.requestId,
543
569
  agentId: agent.id,
544
570
  agentSlug: agent.slug,
545
571
  consumerId,
@@ -554,7 +580,10 @@ function createAgentGateway(config) {
554
580
  await config.recordUsage(usageEvent);
555
581
  await obs?.onRequestComplete?.(ctx, usageEvent);
556
582
  if (config.settlePayment) {
557
- await config.settlePayment({ method: paymentMethod, consumerId }, totalCost).catch(async (err) => {
583
+ await config.settlePayment(
584
+ { method: paymentMethod, consumerId, requestId: ctx.requestId },
585
+ totalCost
586
+ ).catch(async (err) => {
558
587
  const msg = err instanceof Error ? err.message : String(err);
559
588
  console.error(`[agent-gateway] settlement failed for ${consumerId}: ${msg}`);
560
589
  await obs?.onSettlementError?.(ctx, { consumerId, method: paymentMethod, errorMessage: msg });
@@ -603,4 +632,4 @@ export {
603
632
  generateRequestId,
604
633
  createAgentGateway
605
634
  };
606
- //# sourceMappingURL=chunk-E4HSWMEY.js.map
635
+ //# sourceMappingURL=chunk-373QHRKV.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/middleware.ts","../src/verify.ts","../src/filter.ts","../src/observer.ts"],"sourcesContent":["import { Hono } from 'hono'\nimport type {\n GatewayConfig,\n ChatCompletionRequest,\n ChatCompletionChunk,\n PaymentMethod,\n ApiKeyInfo,\n} from './types'\nimport { verifyX402, verifyMpp, defaultVerifyApiKey } from './verify'\nimport { filterConsumerMessagesStrict, redactSystemPromptFromOutput } from './filter'\nimport { checkRateLimit, MemoryRateLimitStore, type RateLimitStore } from './rate-limit'\nimport { MemoryNonceStore } from './nonce-store'\nimport { generateRequestId, type GatewayObserver, type RequestContext } from './observer'\n\n/**\n * Create a Hono router that serves the agent gateway.\n *\n * Mount at any path:\n * app.route('/v1/agents', createAgentGateway(config))\n *\n * Exposes:\n * GET /:slug/chat/completions — agent discovery metadata\n * POST /:slug/chat/completions — OpenAI-compatible chat endpoint (paid)\n */\nexport function createAgentGateway(config: GatewayConfig) {\n // Production gateways must verify x402 signatures. Tests and local\n // dev can opt into the explicit demo path.\n if (!config.x402.verifySigner && !config.x402.demoMode) {\n throw new Error(\n 'createAgentGateway: x402.verifySigner is required in production. ' +\n 'For tests, set x402.demoMode: true explicitly.',\n )\n }\n const gw = new Hono()\n const maxLen = config.maxMessageLength ?? 8000\n const rateLimitStore: RateLimitStore = config.rateLimitStore ?? new MemoryRateLimitStore()\n const globalRateLimit = config.rateLimit ?? { limit: 60, windowSeconds: 60 }\n const nonceStore = config.nonceStore ?? new MemoryNonceStore()\n const requiredScope = config.requiredScope ?? 'chat'\n const obs: GatewayObserver | undefined = config.observer\n\n // --- Discovery endpoint (no auth) ---\n\n gw.get('/:slug/chat/completions', async (c) => {\n const slug = c.req.param('slug')\n const agent = await config.resolveAgent(slug)\n if (!agent) return c.json({ error: 'Agent not found or not published' }, 404)\n\n const paymentMethods: Array<Record<string, unknown>> = [\n {\n type: 'x402',\n operator: config.x402.operatorAddress,\n chain_id: config.x402.chainId,\n credits_contract: config.x402.creditsAddress,\n },\n ]\n if (config.mpp) {\n paymentMethods.push({\n type: 'mpp',\n realm: config.mpp.realm,\n method: config.mpp.method ?? 'blueprintevm',\n })\n }\n paymentMethods.push({ type: 'api_key', prefix: 'sk_agent_' })\n\n return c.json({\n slug: agent.slug,\n pricing: {\n per_token_usd: agent.pricePerTokenUsd,\n currency: 'USD',\n platform_fee_percent: agent.platformFeePercent,\n },\n hosting: {\n mode: agent.sandboxEndpoint ? 'sovereign' : 'centralized',\n endpoint: agent.sandboxEndpoint ?? config.baseUrl ?? 'tangle.tools',\n },\n payment_methods: paymentMethods,\n capabilities: ['chat.completions', 'streaming'],\n openai_compatible: true,\n })\n })\n\n // --- Chat completions endpoint (paid) ---\n\n gw.post('/:slug/chat/completions', async (c) => {\n const slug = c.req.param('slug')\n const startMs = Date.now()\n const requestId = generateRequestId()\n const ctx: RequestContext = { requestId, agentSlug: slug, startMs }\n\n await obs?.onRequestStart?.(ctx)\n\n // 1. Resolve agent\n const agent = await config.resolveAgent(slug)\n if (!agent) {\n return c.json({ error: { message: 'Agent not found', type: 'not_found' } }, 404)\n }\n\n // 2. Body size limit (before parsing — DoS prevention)\n const contentLength = parseInt(c.req.header('Content-Length') ?? '0', 10)\n if (contentLength > 65536) {\n await obs?.onBodyTooLarge?.(ctx, contentLength)\n return c.json(\n { error: { message: 'Request body too large (max 64KB)', type: 'invalid_request' } },\n 413,\n )\n }\n\n let body: ChatCompletionRequest\n try {\n body = await c.req.json()\n } catch {\n return c.json({ error: { message: 'Invalid JSON', type: 'invalid_request' } }, 400)\n }\n if (!body.messages?.length) {\n return c.json({ error: { message: 'messages array required', type: 'invalid_request' } }, 400)\n }\n\n // 3. Authenticate — x402 SpendAuth, MPP, or API key\n const spendAuthHeader = c.req.header('X-Payment-Signature')\n const authHeader = c.req.header('Authorization') ?? ''\n let consumerId: string | null = null\n let paymentMethod: PaymentMethod = 'none'\n let keyInfo: ApiKeyInfo | null = null\n\n if (spendAuthHeader) {\n const signer = await verifyX402(spendAuthHeader, config.x402, nonceStore)\n if (!signer) {\n await obs?.onAuthFailure?.(ctx, { method: 'x402', code: 'invalid_spend_auth', httpStatus: 402 })\n return c.json(\n { error: { message: 'Invalid X-Payment-Signature', type: 'authentication_error', code: 'invalid_spend_auth' } },\n { status: 402, headers: { 'X-Payment-Required': 'spendauth', 'X-Request-Id': requestId } },\n )\n }\n consumerId = signer\n paymentMethod = 'x402'\n } else if (config.mpp && authHeader.toLowerCase().startsWith('payment ')) {\n const signer = await verifyMpp(authHeader, config.mpp, config.x402)\n if (!signer) {\n const realm = config.mpp.realm\n const method = config.mpp.method ?? 'blueprintevm'\n await obs?.onAuthFailure?.(ctx, { method: 'mpp', code: 'invalid_mpp_credential', httpStatus: 401 })\n return c.json(\n { error: { message: 'Invalid Payment credential', type: 'authentication_error', code: 'invalid_mpp_credential' } },\n { status: 401, headers: { 'WWW-Authenticate': `Payment realm=\"${realm}\", method=\"${method}\"`, 'X-Request-Id': requestId } },\n )\n }\n consumerId = signer\n paymentMethod = 'mpp'\n } else if (authHeader.startsWith('Bearer ')) {\n const verify = config.verifyApiKey ?? defaultVerifyApiKey\n const key = await verify(authHeader)\n if (!key) {\n await obs?.onAuthFailure?.(ctx, { method: 'apikey', code: 'invalid_api_key', httpStatus: 401 })\n return c.json(\n { error: { message: 'Invalid API key', type: 'authentication_error' } },\n { status: 401, headers: { 'X-Request-Id': requestId } },\n )\n }\n\n // Scope enforcement — API key must include the required scope\n if (key.scopes && key.scopes.length > 0 && !key.scopes.includes(requiredScope)) {\n await obs?.onAuthFailure?.(ctx, { method: 'apikey', code: 'insufficient_scope', httpStatus: 403 })\n return c.json(\n { error: { message: `API key missing required scope: ${requiredScope}`, type: 'forbidden', code: 'insufficient_scope' } },\n { status: 403, headers: { 'X-Request-Id': requestId } },\n )\n }\n\n consumerId = key.consumerId\n paymentMethod = 'apikey'\n keyInfo = key\n } else {\n // No payment — return 402 with instructions\n await obs?.onAuthFailure?.(ctx, { method: 'none', code: 'payment_required', httpStatus: 402 })\n const methods: string[] = ['x402']\n if (config.mpp) methods.push('mpp')\n methods.push('api_key')\n\n const headers: Record<string, string> = {\n 'X-Payment-Required': methods.join(', '),\n 'X-Request-Id': requestId,\n }\n if (config.mpp) {\n headers['WWW-Authenticate'] = `Payment realm=\"${config.mpp.realm}\", method=\"${config.mpp.method ?? 'blueprintevm'}\"`\n }\n\n return c.json({\n error: {\n message: 'Payment required',\n type: 'payment_required',\n payment_methods: methods,\n x402: {\n operator: config.x402.operatorAddress,\n chain_id: config.x402.chainId,\n credits_address: config.x402.creditsAddress,\n estimated_amount_per_request: '20000',\n },\n ...(config.mpp ? {\n mpp: { realm: config.mpp.realm, method: config.mpp.method ?? 'blueprintevm' },\n } : {}),\n api_key: {\n purchase_url: config.baseUrl ? `${config.baseUrl}/agents/${slug}/api-keys` : undefined,\n },\n },\n }, { status: 402, headers })\n }\n\n await obs?.onPaymentVerified?.(ctx, { method: paymentMethod, consumerId: consumerId!, keyId: keyInfo?.keyId })\n\n // 4. Rate limit — per-key override or global\n const effectiveRateLimit = keyInfo?.rateLimitPerMinute\n ? { limit: keyInfo.rateLimitPerMinute, windowSeconds: 60 }\n : globalRateLimit\n\n const rl = await checkRateLimit(consumerId!, effectiveRateLimit, rateLimitStore)\n if (!rl.allowed) {\n await obs?.onRateLimited?.(ctx, { consumerId: consumerId!, retryAfterSeconds: rl.retryAfterSeconds ?? 60 })\n return c.json(\n { error: { message: 'Rate limit exceeded', type: 'rate_limit_error', retry_after: rl.retryAfterSeconds } },\n { status: 429, headers: { 'Retry-After': String(rl.retryAfterSeconds ?? 60), 'X-Request-Id': requestId } },\n )\n }\n\n // 5. Filter messages — injection detection + sanitization\n const { messages: filtered, injectionWarnings } = filterConsumerMessagesStrict(body.messages, maxLen)\n\n if (injectionWarnings.length > 0) {\n await obs?.onInjectionDetected?.(ctx, {\n consumerId: consumerId!,\n patterns: injectionWarnings,\n blocked: !!config.blockInjection,\n })\n\n if (config.blockInjection) {\n return c.json(\n { error: { message: 'Request rejected: potential prompt injection detected', type: 'content_policy_violation' } },\n { status: 400, headers: { 'X-Request-Id': requestId } },\n )\n }\n // In non-blocking mode, continue but the warning is logged for auditing\n }\n\n const userMessage = filtered\n .filter((m) => m.role === 'user')\n .map((m) => m.content)\n .join('\\n\\n')\n\n if (!userMessage) {\n return c.json({ error: { message: 'No user message provided', type: 'invalid_request' } }, 400)\n }\n\n // 5b. Optional host authorization. Runs after payment/rate-limit\n // checks and before sandbox allocation.\n if (config.authorizeConsumer) {\n const authz = await config.authorizeConsumer(agent, {\n method: paymentMethod,\n consumerId: consumerId!,\n keyId: keyInfo?.keyId,\n requestId,\n })\n if (!authz.allow) {\n return c.json(\n {\n error: {\n message: authz.reason,\n type: 'authorization_denied',\n code: authz.code,\n },\n },\n { status: 403, headers: { 'X-Request-Id': requestId } },\n )\n }\n }\n\n // 6. Get sandbox and stream response with output filtering\n const inputTokens = Math.ceil(userMessage.length / 4)\n let outputTokens = 0\n\n const stream = new ReadableStream({\n async start(controller) {\n const encoder = new TextEncoder()\n const sendChunk = (rawDelta: string) => {\n // Redact system prompt leakage from output\n const delta = redactSystemPromptFromOutput(rawDelta, agent.systemPrompt)\n outputTokens += Math.ceil(delta.length / 4)\n const chunk: ChatCompletionChunk = {\n id: `chatcmpl-${Date.now()}`,\n object: 'chat.completion.chunk',\n created: Math.floor(Date.now() / 1000),\n model: agent.slug,\n choices: [{ index: 0, delta: { content: delta }, finish_reason: null }],\n }\n controller.enqueue(encoder.encode(`data: ${JSON.stringify(chunk)}\\n\\n`))\n }\n\n try {\n const box = await config.getSandbox(agent)\n const promptStream = box.streamPrompt(userMessage, {\n sessionId: `consumer:${consumerId}`,\n systemPrompt: agent.systemPrompt,\n })\n\n for await (const event of promptStream) {\n if (\n event.type === 'message.part.updated' &&\n event.data?.part?.type === 'text' &&\n event.data.delta\n ) {\n sendChunk(event.data.delta)\n }\n }\n\n // Final chunk\n const done: ChatCompletionChunk = {\n id: `chatcmpl-${Date.now()}`,\n object: 'chat.completion.chunk',\n created: Math.floor(Date.now() / 1000),\n model: agent.slug,\n choices: [{ index: 0, delta: {}, finish_reason: 'stop' }],\n }\n controller.enqueue(encoder.encode(`data: ${JSON.stringify(done)}\\n\\n`))\n controller.enqueue(encoder.encode('data: [DONE]\\n\\n'))\n\n // 7. Record usage + settle payment\n const totalCost = (inputTokens + outputTokens) * agent.pricePerTokenUsd\n const ownerEarned = totalCost * (1 - agent.platformFeePercent)\n const platformFee = totalCost * agent.platformFeePercent\n\n const usageEvent = {\n requestId: ctx.requestId,\n agentId: agent.id,\n agentSlug: agent.slug,\n consumerId: consumerId!,\n paymentMethod,\n inputTokens,\n outputTokens,\n totalCostUsd: totalCost,\n ownerEarnedUsd: ownerEarned,\n platformFeeUsd: platformFee,\n durationMs: Date.now() - startMs,\n }\n\n await config.recordUsage(usageEvent)\n await obs?.onRequestComplete?.(ctx, usageEvent)\n\n if (config.settlePayment) {\n await config.settlePayment(\n { method: paymentMethod, consumerId: consumerId!, requestId: ctx.requestId },\n totalCost,\n ).catch(async err => {\n const msg = err instanceof Error ? err.message : String(err)\n console.error(`[agent-gateway] settlement failed for ${consumerId}: ${msg}`)\n await obs?.onSettlementError?.(ctx, { consumerId: consumerId!, method: paymentMethod, errorMessage: msg })\n })\n }\n } catch (err) {\n // Sanitize error — never expose stack traces or internal paths\n const rawMessage = err instanceof Error ? err.message : String(err)\n const safeMessage =\n rawMessage.includes('/') || rawMessage.includes('\\\\')\n ? 'Internal agent error'\n : rawMessage\n await obs?.onStreamError?.(ctx, { consumerId: consumerId!, errorMessage: rawMessage })\n controller.enqueue(\n encoder.encode(`data: ${JSON.stringify({ error: { message: safeMessage, type: 'server_error' } })}\\n\\n`),\n )\n } finally {\n controller.close()\n }\n },\n })\n\n return new Response(stream, {\n headers: {\n 'Content-Type': 'text/event-stream',\n 'Cache-Control': 'no-cache',\n 'X-Request-Id': requestId,\n 'X-Agent-Slug': agent.slug,\n 'X-Agent-Hosting': agent.sandboxEndpoint ? 'sovereign' : 'centralized',\n 'X-Payment-Method': paymentMethod,\n 'X-Payment-Settled': paymentMethod === 'x402' ? 'pending' : 'true',\n ...(rl.remaining !== undefined ? { 'X-RateLimit-Remaining': String(rl.remaining) } : {}),\n },\n })\n })\n\n return gw\n}\n","import type { X402Config, MppConfig, ApiKeyInfo } from './types'\nimport type { NonceStore } from './nonce-store'\n\n/**\n * Verify x402 SpendAuth signature (EIP-712).\n * Returns the signer address (commitment) if valid, null otherwise.\n *\n * demoMode: true — accepts any well-formed header\n * shape after replay/expiry checks.\n * Tests + local dev only.\n * verifySigner present — production verification path.\n * neither — rejected by createAgentGateway and\n * by this function as defense-in-depth.\n */\nexport async function verifyX402(\n spendAuthHeader: string,\n config: X402Config,\n nonceStore?: NonceStore,\n): Promise<string | null> {\n try {\n const raw = JSON.parse(spendAuthHeader)\n if (!raw.commitment || !raw.signature || !raw.amount) return null\n if (raw.operator?.toLowerCase() !== config.operatorAddress.toLowerCase()) return null\n\n const amount = BigInt(raw.amount)\n const nonce = BigInt(raw.nonce)\n const expiry = BigInt(raw.expiry)\n\n // Reject expired payments\n if (expiry < BigInt(Math.floor(Date.now() / 1000))) return null\n\n // Reject zero-amount payments\n if (amount <= 0n) return null\n\n // Reject replayed nonces\n const nonceKey = `${raw.commitment}:${nonce.toString()}`\n if (nonceStore) {\n if (await nonceStore.hasSeen(nonceKey)) return null\n // Mark seen with TTL matching the expiry window (max 1 hour)\n const ttl = Math.min(Number(expiry) - Math.floor(Date.now() / 1000), 3600)\n await nonceStore.markSeen(nonceKey, Math.max(ttl, 60))\n }\n\n if (config.verifySigner) {\n const verified = await config.verifySigner(raw)\n if (!verified) return null\n } else if (!config.demoMode) {\n return null\n }\n\n return raw.commitment\n } catch {\n return null\n }\n}\n\n/**\n * Verify MPP (Machine Payments Protocol) Authorization: Payment header.\n *\n * MPP uses `Authorization: Payment <method> <credential>` format where\n * the credential is a base64url-encoded JSON wrapping the same EIP-3009\n * payment payload that x402 uses. This means existing x402 wallets work\n * unchanged over the MPP wire format.\n *\n * Returns the signer address if valid, null otherwise.\n * In demo mode, accepts any well-formed Payment header.\n */\nexport async function verifyMpp(\n authHeader: string,\n _config: MppConfig,\n x402Config: X402Config,\n): Promise<string | null> {\n // MPP format: \"Payment <method> <base64url-credential>\"\n const match = authHeader.match(/^Payment\\s+(\\S+)\\s+(\\S+)$/i)\n if (!match) return null\n\n const [, , credentialB64] = match\n\n try {\n // Decode base64url credential → JSON with the same EIP-3009 payload\n const decoded = Buffer.from(credentialB64, 'base64url').toString('utf-8')\n const credential = JSON.parse(decoded)\n\n // The credential payload wraps the same fields x402 uses\n const payload = credential.payload ?? credential\n if (!payload.commitment && !payload.from) return null\n\n // Validate operator match (same as x402)\n const operator = payload.operator ?? payload.to\n if (operator && operator.toLowerCase() !== x402Config.operatorAddress.toLowerCase()) return null\n\n // Validate bigint fields if present\n if (payload.amount) BigInt(payload.amount)\n if (payload.nonce) BigInt(payload.nonce)\n\n return payload.commitment ?? payload.from ?? null\n } catch {\n return null\n }\n}\n\n/**\n * Default API key verifier — accepts any `sk_agent_*` key (demo mode).\n * Override in GatewayConfig.verifyApiKey for production.\n */\nexport async function defaultVerifyApiKey(\n authHeader: string,\n): Promise<ApiKeyInfo | null> {\n if (!authHeader.startsWith('Bearer sk_agent_')) return null\n const key = authHeader.slice(7)\n return {\n keyId: key.slice(0, 16),\n consumerId: `apikey:${key.slice(0, 16)}`,\n }\n}\n","import type { ChatMessage } from './types'\n\n// --- Injection detection patterns ---\n\nconst INJECTION_PATTERNS = [\n // Direct instruction override\n /ignore\\s+(all\\s+)?(previous|prior|above|earlier)\\s+(instructions?|prompts?|rules?|directives?)/i,\n /disregard\\s+(all\\s+)?(previous|prior|system)/i,\n /forget\\s+(everything|all|your)\\s+(previous|instructions?|training)/i,\n // Role assumption\n /you\\s+are\\s+now\\s+(a|an|the)\\s+/i,\n /pretend\\s+(you\\s+are|to\\s+be)\\s+/i,\n /act\\s+as\\s+(if\\s+you\\s+are|a|an|the)\\s+/i,\n /new\\s+instructions?:/i,\n /\\[system\\]/i,\n /\\[INST\\]/i,\n // Prompt extraction\n /what\\s+(is|are)\\s+your\\s+(system\\s+)?(prompt|instructions?|rules?|directives?)/i,\n /repeat\\s+(your|the)\\s+(system\\s+)?(prompt|instructions?)/i,\n /output\\s+(your|the)\\s+((system|initial|original|first|full|real|hidden|secret|raw|exact)\\s+)?(prompt|instructions?)/i,\n /show\\s+(me\\s+)?(your|the)\\s+((system|hidden|secret|initial|original)\\s+)?(prompt|instructions?|message|directives?)/i,\n /tell\\s+me\\s+(your|the)\\s+(system\\s+)?(prompt|instructions?|rules?)/i,\n // Developer/debug/admin/jailbreak mode override\n /(developer|debug|admin|god|sudo|jailbreak|unrestricted|maintenance)\\s+mode/i,\n // Safety/safeguard disablement\n /(disable|bypass|override|remove|turn\\s+off)\\s+(your\\s+|the\\s+)?(safety|safeguards?|guardrails?|filters?|restrictions?|rules?|limitations?|content\\s+policy)/i,\n // Role reversal\n /(from\\s+now\\s+on|starting\\s+now|now)\\s+you('re|\\s+are)\\s+(the\\s+)?(user|human|customer|assistant)/i,\n /i('m|\\s+am)\\s+(the\\s+)?(assistant|ai|model|llm|bot)/i,\n // Tool-call / JSON-shaped spoofs\n /\"(?:tool|function|action|call)\"\\s*:\\s*\"[^\"]*(exfiltrate|leak|reveal|dump|extract|steal)[^\"]*\"/i,\n /exfiltrate[_\\s]*(system|prompt|secret|vault|config|data)/i,\n // Data exfiltration\n /read\\s+(the\\s+)?(vault|workspace|config|secret|\\.env)/i,\n /cat\\s+\\/home\\/agent\\/(vault|config|\\.env|secrets?)/i,\n /list\\s+(all\\s+)?(vault|workspace|secret)\\s+(files?|contents?|data)/i,\n // i18n — Spanish\n /ignor[ae]\\s+(todas?\\s+)?(las\\s+)?(instrucciones?|indicaciones?|órdenes?)\\s+(previas?|anteriores?)/i,\n /olvid[ae]\\s+(todas?\\s+|tus\\s+)?(instrucciones?|indicaciones?)/i,\n /(ahora\\s+)?(tú\\s+)?eres\\s+(ahora\\s+)?(un|una|el|la)\\s+/i,\n /finge\\s+(que\\s+eres|ser)\\s+/i,\n // i18n — French\n /ignor(ez|e|es)\\s+(toutes?\\s+)?(les\\s+)?(instructions?|consignes?|directives?)\\s+(précédentes?|antérieures?)/i,\n /oublie(z|s)?\\s+(toutes?\\s+|vos\\s+|tes\\s+)?(instructions?|consignes?)/i,\n /(vous\\s+êtes|tu\\s+es)\\s+(maintenant|désormais)\\s+(un|une)\\s+/i,\n /prétend(ez|s)?\\s+(être|que\\s+vous\\s+êtes|que\\s+tu\\s+es)\\s+/i,\n // i18n — German\n /ignorier(e|en|t)\\s+(alle\\s+)?(vorherigen?|vorigen?|bisherigen?)\\s+(anweisungen|anordnungen|vorgaben)/i,\n /vergiss\\s+(alle\\s+|deine\\s+)?(anweisungen|vorgaben|regeln)/i,\n /du\\s+bist\\s+(jetzt|nun)\\s+(ein|eine)\\s+/i,\n /tu\\s+so\\s+als\\s+(wärst\\s+du|ob\\s+du)\\s+/i,\n]\n\n// Cyrillic + Greek confusables that look identical to Latin letters but\n// NFKC does NOT collapse (they are distinct characters in Unicode, not\n// compatibility equivalents). A payload like `ignоre аll previоus\n// instructiоns` (o's are Cyrillic U+043E) evades the pattern set\n// entirely because `ignore` written with Cyrillic chars never matches\n// the Latin `[o]` in our regexes even after NFKC.\n//\n// Mapping derived from Unicode's confusables.txt, intentionally limited\n// to letters that routinely appear in English prompt-injection payloads.\n// Not a full confusable table — adding every entry would risk mangling\n// legitimate Russian/Greek content that happens to contain these\n// letters. Our INJECTION_PATTERNS and redaction keywords are English\n// ASCII, so collapsing to ASCII only touches payloads impersonating\n// those tokens.\nconst HOMOGLYPHS: Record<string, string> = {\n 'а': 'a', 'е': 'e', 'о': 'o', 'р': 'p',\n 'с': 'c', 'у': 'y', 'х': 'x', 'і': 'i',\n 'ј': 'j', 'ѕ': 's', 'һ': 'h',\n 'А': 'A', 'В': 'B', 'Е': 'E', 'К': 'K',\n 'М': 'M', 'Н': 'H', 'О': 'O', 'Р': 'P',\n 'С': 'C', 'Т': 'T', 'Х': 'X', 'І': 'I',\n 'Ј': 'J', 'Ѕ': 'S',\n 'α': 'a', 'ε': 'e', 'ο': 'o', 'ρ': 'p',\n 'υ': 'y', 'ν': 'v', 'ι': 'i', 'τ': 't',\n 'κ': 'k', 'χ': 'x', 'η': 'n', 'μ': 'u',\n 'Α': 'A', 'Β': 'B', 'Ε': 'E', 'Ζ': 'Z',\n 'Η': 'H', 'Ι': 'I', 'Κ': 'K', 'Μ': 'M',\n 'Ν': 'N', 'Ο': 'O', 'Ρ': 'P', 'Τ': 'T',\n 'Χ': 'X', 'Υ': 'Y',\n}\n\nfunction collapseHomoglyphs(text: string): string {\n let out = ''\n for (const ch of text) out += HOMOGLYPHS[ch] ?? ch\n return out\n}\n\n// Unicode normalization for REDACTION.\n//\n// Strip zero-widths entirely (no space substitute) so cross-character\n// elisions like \"v​aul​t/secret\" → \"vault/secret\" are matched\n// by the redaction keyword pass. NFKC collapses compatibility chars\n// (fullwidth `ignore` → `ignore`). collapseHomoglyphs catches the\n// Cyrillic/Greek lookalikes NFKC leaves alone.\nfunction normalizeUnicode(text: string): string {\n return collapseHomoglyphs(\n text.replace(/[\\u200B-\\u200F\\u2028-\\u202F\\u2060\\uFEFF]/g, '').normalize('NFKC'),\n )\n}\n\n// Unicode normalization for DETECTION.\n//\n// Two adversarial concerns diverge from redaction:\n//\n// 1. Between-word zero-widths (\"ignore​all​previous\") — the\n// attacker separates tokens with ZWSP so \\s+-anchored regexes do\n// not fire. Strip-then-detect glues the tokens into one\n// unmatchable word; replace ZWSP with a real SPACE instead so\n// /ignore\\s+all\\s+previous/ fires.\n// 2. Punctuation noise (\"Ignore,,, all...previous!!!\") — collapse\n// punctuation runs to a single space. Preserve `[` `]` because\n// literal `[system]` / `[INST]` are first-class injection signals\n// we match directly.\nfunction normalizeForDetection(text: string): string {\n const spaced = text\n .replace(/[\\u200B\\u200C\\uFEFF]/g, ' ')\n .replace(/[\\u200D\\u200E\\u200F\\u2028-\\u202F\\u2060]/g, '')\n .normalize('NFKC')\n return collapseHomoglyphs(spaced)\n .replace(/[(){}<>,.!?:;\"`~]+/g, ' ')\n .replace(/\\s+/g, ' ')\n}\n\n// Third pass: also collapse square brackets. Literal `[system]` /\n// `[INST]` patterns are matched against the other two passes (which\n// preserve brackets); this pass catches the OPPOSITE evasion where\n// the attacker wraps tokens in brackets to split them for \\s+-anchored\n// regexes — e.g. `(ignore) [all] {previous} <instructions>` should\n// still match the baseline pattern.\nfunction normalizeForDetectionNoBrackets(text: string): string {\n return normalizeForDetection(text)\n .replace(/[\\[\\]]+/g, ' ')\n .replace(/\\s+/g, ' ')\n}\n\n/**\n * Detect prompt injection attempts.\n * Returns array of matched pattern descriptions, empty if clean.\n *\n * Patterns run against TWO normalizations so both within-word and\n * between-word zero-width evasions are caught, and so `[system]` /\n * `[INST]` literals are not lost to punctuation-collapse:\n * - normalizeUnicode — strips zero-widths, preserves brackets\n * - normalizeForDetection — spaces zero-widths, collapses punct\n */\nexport function detectInjection(content: string): string[] {\n const stripNorm = normalizeUnicode(content)\n const detectNorm = normalizeForDetection(content)\n const noBracketNorm = normalizeForDetectionNoBrackets(content)\n const matches: string[] = []\n\n for (const pattern of INJECTION_PATTERNS) {\n if (pattern.test(stripNorm) || pattern.test(detectNorm) || pattern.test(noBracketNorm)) {\n matches.push(pattern.source.slice(0, 60))\n }\n }\n\n // Check for base64-encoded injection attempts in either normalization.\n for (const norm of [stripNorm, detectNorm]) {\n const b64Matches = norm.match(/[A-Za-z0-9+/]{40,}={0,2}/g)\n if (!b64Matches) continue\n for (const b64 of b64Matches) {\n try {\n const decoded = atob(b64)\n if (INJECTION_PATTERNS.some(p => p.test(decoded))) {\n if (!matches.includes('base64-encoded injection')) {\n matches.push('base64-encoded injection')\n }\n }\n } catch { /* not valid b64 */ }\n }\n }\n\n return matches\n}\n\n/**\n * Security boundary — filter consumer messages before forwarding to agent.\n *\n * Defense in depth:\n * 1. Strip system messages (consumers cannot set system prompt)\n * 2. Normalize Unicode (collapse homoglyphs, remove zero-width chars)\n * 3. Detect injection patterns (instruction override, prompt extraction, data exfil)\n * 4. Redact sensitive keywords\n * 5. Cap message length\n *\n * Returns filtered messages and any injection warnings detected.\n */\nexport function filterConsumerMessages(\n messages: ChatMessage[],\n maxLength = 8000,\n): ChatMessage[] {\n return messages\n .filter((m) => m.role !== 'system')\n .map((m) => {\n const normalized = normalizeUnicode(m.content)\n const redacted = normalized\n .replace(/\\b(vault|workspace|owner|admin|secret|\\.env|config\\.json)[\\s/:][^\\s]*/gi, '[REDACTED]')\n .slice(0, maxLength)\n return { role: m.role, content: redacted }\n })\n}\n\n/**\n * Filter consumer messages with injection detection.\n * Returns { messages, injectionWarnings }.\n * If injectionWarnings is non-empty, the gateway should log and optionally reject.\n */\nexport function filterConsumerMessagesStrict(\n messages: ChatMessage[],\n maxLength = 8000,\n): { messages: ChatMessage[]; injectionWarnings: string[] } {\n const filtered = filterConsumerMessages(messages, maxLength)\n const allContent = filtered.map(m => m.content).join(' ')\n const injectionWarnings = detectInjection(allContent)\n return { messages: filtered, injectionWarnings }\n}\n\n/**\n * Redact system prompt content from agent output.\n * Prevents the agent from leaking its own instructions in responses.\n *\n * Strategy: if any chunk of the system prompt appears verbatim (>40 chars)\n * in the output, replace it with [REDACTED].\n */\nexport function redactSystemPromptFromOutput(\n output: string,\n systemPrompt: string | undefined,\n): string {\n if (!systemPrompt || systemPrompt.length < 40) return output\n\n // Split system prompt into meaningful chunks (sentences or lines)\n const chunks = systemPrompt\n .split(/[.\\n]/)\n .map(s => s.trim())\n .filter(s => s.length >= 40)\n\n let redacted = output\n for (const chunk of chunks) {\n // Case-insensitive substring match\n const idx = redacted.toLowerCase().indexOf(chunk.toLowerCase())\n if (idx >= 0) {\n redacted = redacted.slice(0, idx) + '[REDACTED — system instructions]' + redacted.slice(idx + chunk.length)\n }\n }\n\n return redacted\n}\n","/**\n * Observability hook surface.\n *\n * Consumers implement GatewayObserver to wire the gateway into their existing\n * telemetry stack (Langfuse, OTEL, structured logs, Prometheus, etc.) without\n * the gateway itself depending on any of those libraries.\n *\n * Every event carries a requestId so downstream metrics can correlate the\n * payment verification, sandbox execution, and settlement for one request.\n * When no observer is configured, the gateway stays silent.\n */\n\nimport type { PaymentMethod, GatewayUsageEvent } from './types'\n\nexport interface RequestContext {\n requestId: string\n agentSlug: string\n startMs: number\n}\n\nexport interface AuthFailureReason {\n method: 'x402' | 'mpp' | 'apikey' | 'none'\n code: string\n httpStatus: number\n}\n\nexport interface GatewayObserver {\n /** Called at the start of every chat completions POST. */\n onRequestStart?: (ctx: RequestContext) => void | Promise<void>\n\n /** Called when a payment method has been successfully verified. */\n onPaymentVerified?: (ctx: RequestContext, info: {\n method: PaymentMethod\n consumerId: string\n keyId?: string\n }) => void | Promise<void>\n\n /** Called when auth fails — every branch. */\n onAuthFailure?: (ctx: RequestContext, reason: AuthFailureReason) => void | Promise<void>\n\n /** Called when a consumer hits the rate limit. */\n onRateLimited?: (ctx: RequestContext, info: {\n consumerId: string\n retryAfterSeconds: number\n }) => void | Promise<void>\n\n /** Called when the request body exceeds the 64KB limit. */\n onBodyTooLarge?: (ctx: RequestContext, contentLength: number) => void | Promise<void>\n\n /**\n * Called when prompt-injection patterns are detected.\n * `blocked` is true when blockInjection config is on and the request was\n * rejected; false when the patterns were logged but the request proceeded.\n */\n onInjectionDetected?: (ctx: RequestContext, info: {\n consumerId: string\n patterns: string[]\n blocked: boolean\n }) => void | Promise<void>\n\n /** Called after a successful stream completes and recordUsage has fired. */\n onRequestComplete?: (ctx: RequestContext, usage: GatewayUsageEvent) => void | Promise<void>\n\n /** Called when the sandbox throws. The error message is pre-scrubbed. */\n onStreamError?: (ctx: RequestContext, info: {\n consumerId: string\n errorMessage: string\n }) => void | Promise<void>\n\n /** Called when settlement fails. Payment already occurred; this is async bookkeeping. */\n onSettlementError?: (ctx: RequestContext, info: {\n consumerId: string\n method: PaymentMethod\n errorMessage: string\n }) => void | Promise<void>\n}\n\n// ---------------------------------------------------------------------------\n// Convenience implementations\n// ---------------------------------------------------------------------------\n\n/**\n * Structured-log observer. Emits one JSON line per event on the `log` function.\n * Default sink: console.log. Production consumers usually pipe their own\n * structured logger (pino, winston, the cf Logs binding).\n *\n * Usage:\n * new ConsoleObserver(({ level, event, ...rest }) => logger.info({ event, ...rest }))\n */\nexport class ConsoleObserver implements GatewayObserver {\n constructor(\n private readonly log: (entry: Record<string, unknown>) => void = (e) => console.log(JSON.stringify(e)),\n ) {}\n\n private emit(level: 'info' | 'warn' | 'error', event: string, ctx: RequestContext, rest: Record<string, unknown> = {}) {\n this.log({\n level,\n event,\n time: new Date().toISOString(),\n requestId: ctx.requestId,\n agentSlug: ctx.agentSlug,\n durationMs: Date.now() - ctx.startMs,\n ...rest,\n })\n }\n\n onRequestStart(ctx: RequestContext) { this.emit('info', 'gateway.request.start', ctx) }\n onPaymentVerified(ctx: RequestContext, info: { method: PaymentMethod; consumerId: string; keyId?: string }) {\n this.emit('info', 'gateway.payment.verified', ctx, info)\n }\n onAuthFailure(ctx: RequestContext, reason: AuthFailureReason) {\n this.emit('warn', 'gateway.auth.failure', ctx, reason as unknown as Record<string, unknown>)\n }\n onRateLimited(ctx: RequestContext, info: { consumerId: string; retryAfterSeconds: number }) {\n this.emit('warn', 'gateway.rate_limit', ctx, info)\n }\n onBodyTooLarge(ctx: RequestContext, contentLength: number) {\n this.emit('warn', 'gateway.body_too_large', ctx, { contentLength })\n }\n onInjectionDetected(ctx: RequestContext, info: { consumerId: string; patterns: string[]; blocked: boolean }) {\n this.emit('warn', 'gateway.injection', ctx, info)\n }\n onRequestComplete(ctx: RequestContext, usage: GatewayUsageEvent) {\n this.emit('info', 'gateway.request.complete', ctx, usage as unknown as Record<string, unknown>)\n }\n onStreamError(ctx: RequestContext, info: { consumerId: string; errorMessage: string }) {\n this.emit('error', 'gateway.stream.error', ctx, info)\n }\n onSettlementError(ctx: RequestContext, info: { consumerId: string; method: PaymentMethod; errorMessage: string }) {\n this.emit('error', 'gateway.settlement.error', ctx, info)\n }\n}\n\n/**\n * Compose multiple observers into one. Errors in any individual observer\n * don't break the others (fire-and-forget telemetry).\n */\nexport class CompositeObserver implements GatewayObserver {\n constructor(private readonly observers: GatewayObserver[]) {}\n\n private async fanOut<K extends keyof GatewayObserver>(event: K, ...args: unknown[]): Promise<void> {\n for (const obs of this.observers) {\n const fn = obs[event] as ((...a: unknown[]) => void | Promise<void>) | undefined\n if (!fn) continue\n try {\n await fn.apply(obs, args)\n } catch (err) {\n console.warn(`[agent-gateway] observer ${event} threw:`, err instanceof Error ? err.message : err)\n }\n }\n }\n\n onRequestStart = (ctx: RequestContext) => this.fanOut('onRequestStart', ctx)\n onPaymentVerified = (ctx: RequestContext, info: Parameters<Required<GatewayObserver>['onPaymentVerified']>[1]) =>\n this.fanOut('onPaymentVerified', ctx, info)\n onAuthFailure = (ctx: RequestContext, reason: AuthFailureReason) =>\n this.fanOut('onAuthFailure', ctx, reason)\n onRateLimited = (ctx: RequestContext, info: Parameters<Required<GatewayObserver>['onRateLimited']>[1]) =>\n this.fanOut('onRateLimited', ctx, info)\n onBodyTooLarge = (ctx: RequestContext, contentLength: number) =>\n this.fanOut('onBodyTooLarge', ctx, contentLength)\n onInjectionDetected = (ctx: RequestContext, info: Parameters<Required<GatewayObserver>['onInjectionDetected']>[1]) =>\n this.fanOut('onInjectionDetected', ctx, info)\n onRequestComplete = (ctx: RequestContext, usage: GatewayUsageEvent) =>\n this.fanOut('onRequestComplete', ctx, usage)\n onStreamError = (ctx: RequestContext, info: Parameters<Required<GatewayObserver>['onStreamError']>[1]) =>\n this.fanOut('onStreamError', ctx, info)\n onSettlementError = (ctx: RequestContext, info: Parameters<Required<GatewayObserver>['onSettlementError']>[1]) =>\n this.fanOut('onSettlementError', ctx, info)\n}\n\n/**\n * Generate a request-id. Crypto-random 16 bytes, hex-encoded with an `req_` prefix.\n * Works in Workers, Node, and browsers — all have globalThis.crypto.\n */\nexport function generateRequestId(): string {\n const bytes = new Uint8Array(16)\n globalThis.crypto.getRandomValues(bytes)\n const hex = Array.from(bytes).map((b) => b.toString(16).padStart(2, '0')).join('')\n return `req_${hex}`\n}\n"],"mappings":";;;;;;;;;AAAA,SAAS,YAAY;;;ACcrB,eAAsB,WACpB,iBACA,QACA,YACwB;AACxB,MAAI;AACF,UAAM,MAAM,KAAK,MAAM,eAAe;AACtC,QAAI,CAAC,IAAI,cAAc,CAAC,IAAI,aAAa,CAAC,IAAI,OAAQ,QAAO;AAC7D,QAAI,IAAI,UAAU,YAAY,MAAM,OAAO,gBAAgB,YAAY,EAAG,QAAO;AAEjF,UAAM,SAAS,OAAO,IAAI,MAAM;AAChC,UAAM,QAAQ,OAAO,IAAI,KAAK;AAC9B,UAAM,SAAS,OAAO,IAAI,MAAM;AAGhC,QAAI,SAAS,OAAO,KAAK,MAAM,KAAK,IAAI,IAAI,GAAI,CAAC,EAAG,QAAO;AAG3D,QAAI,UAAU,GAAI,QAAO;AAGzB,UAAM,WAAW,GAAG,IAAI,UAAU,IAAI,MAAM,SAAS,CAAC;AACtD,QAAI,YAAY;AACd,UAAI,MAAM,WAAW,QAAQ,QAAQ,EAAG,QAAO;AAE/C,YAAM,MAAM,KAAK,IAAI,OAAO,MAAM,IAAI,KAAK,MAAM,KAAK,IAAI,IAAI,GAAI,GAAG,IAAI;AACzE,YAAM,WAAW,SAAS,UAAU,KAAK,IAAI,KAAK,EAAE,CAAC;AAAA,IACvD;AAEA,QAAI,OAAO,cAAc;AACvB,YAAM,WAAW,MAAM,OAAO,aAAa,GAAG;AAC9C,UAAI,CAAC,SAAU,QAAO;AAAA,IACxB,WAAW,CAAC,OAAO,UAAU;AAC3B,aAAO;AAAA,IACT;AAEA,WAAO,IAAI;AAAA,EACb,QAAQ;AACN,WAAO;AAAA,EACT;AACF;AAaA,eAAsB,UACpB,YACA,SACA,YACwB;AAExB,QAAM,QAAQ,WAAW,MAAM,4BAA4B;AAC3D,MAAI,CAAC,MAAO,QAAO;AAEnB,QAAM,CAAC,EAAE,EAAE,aAAa,IAAI;AAE5B,MAAI;AAEF,UAAM,UAAU,OAAO,KAAK,eAAe,WAAW,EAAE,SAAS,OAAO;AACxE,UAAM,aAAa,KAAK,MAAM,OAAO;AAGrC,UAAM,UAAU,WAAW,WAAW;AACtC,QAAI,CAAC,QAAQ,cAAc,CAAC,QAAQ,KAAM,QAAO;AAGjD,UAAM,WAAW,QAAQ,YAAY,QAAQ;AAC7C,QAAI,YAAY,SAAS,YAAY,MAAM,WAAW,gBAAgB,YAAY,EAAG,QAAO;AAG5F,QAAI,QAAQ,OAAQ,QAAO,QAAQ,MAAM;AACzC,QAAI,QAAQ,MAAO,QAAO,QAAQ,KAAK;AAEvC,WAAO,QAAQ,cAAc,QAAQ,QAAQ;AAAA,EAC/C,QAAQ;AACN,WAAO;AAAA,EACT;AACF;AAMA,eAAsB,oBACpB,YAC4B;AAC5B,MAAI,CAAC,WAAW,WAAW,kBAAkB,EAAG,QAAO;AACvD,QAAM,MAAM,WAAW,MAAM,CAAC;AAC9B,SAAO;AAAA,IACL,OAAO,IAAI,MAAM,GAAG,EAAE;AAAA,IACtB,YAAY,UAAU,IAAI,MAAM,GAAG,EAAE,CAAC;AAAA,EACxC;AACF;;;AC9GA,IAAM,qBAAqB;AAAA;AAAA,EAEzB;AAAA,EACA;AAAA,EACA;AAAA;AAAA,EAEA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA;AAAA,EAEA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA;AAAA,EAEA;AAAA;AAAA,EAEA;AAAA;AAAA,EAEA;AAAA,EACA;AAAA;AAAA,EAEA;AAAA,EACA;AAAA;AAAA,EAEA;AAAA,EACA;AAAA,EACA;AAAA;AAAA,EAEA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA;AAAA,EAEA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA;AAAA,EAEA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF;AAgBA,IAAM,aAAqC;AAAA,EACzC,UAAK;AAAA,EAAK,UAAK;AAAA,EAAK,UAAK;AAAA,EAAK,UAAK;AAAA,EACnC,UAAK;AAAA,EAAK,UAAK;AAAA,EAAK,UAAK;AAAA,EAAK,UAAK;AAAA,EACnC,UAAK;AAAA,EAAK,UAAK;AAAA,EAAK,UAAK;AAAA,EACzB,UAAK;AAAA,EAAK,UAAK;AAAA,EAAK,UAAK;AAAA,EAAK,UAAK;AAAA,EACnC,UAAK;AAAA,EAAK,UAAK;AAAA,EAAK,UAAK;AAAA,EAAK,UAAK;AAAA,EACnC,UAAK;AAAA,EAAK,UAAK;AAAA,EAAK,UAAK;AAAA,EAAK,UAAK;AAAA,EACnC,UAAK;AAAA,EAAK,UAAK;AAAA,EACf,UAAK;AAAA,EAAK,UAAK;AAAA,EAAK,UAAK;AAAA,EAAK,UAAK;AAAA,EACnC,UAAK;AAAA,EAAK,UAAK;AAAA,EAAK,UAAK;AAAA,EAAK,UAAK;AAAA,EACnC,UAAK;AAAA,EAAK,UAAK;AAAA,EAAK,UAAK;AAAA,EAAK,UAAK;AAAA,EACnC,UAAK;AAAA,EAAK,UAAK;AAAA,EAAK,UAAK;AAAA,EAAK,UAAK;AAAA,EACnC,UAAK;AAAA,EAAK,UAAK;AAAA,EAAK,UAAK;AAAA,EAAK,UAAK;AAAA,EACnC,UAAK;AAAA,EAAK,UAAK;AAAA,EAAK,UAAK;AAAA,EAAK,UAAK;AAAA,EACnC,UAAK;AAAA,EAAK,UAAK;AACjB;AAEA,SAAS,mBAAmB,MAAsB;AAChD,MAAI,MAAM;AACV,aAAW,MAAM,KAAM,QAAO,WAAW,EAAE,KAAK;AAChD,SAAO;AACT;AASA,SAAS,iBAAiB,MAAsB;AAC9C,SAAO;AAAA,IACL,KAAK,QAAQ,6CAA6C,EAAE,EAAE,UAAU,MAAM;AAAA,EAChF;AACF;AAeA,SAAS,sBAAsB,MAAsB;AACnD,QAAM,SAAS,KACZ,QAAQ,yBAAyB,GAAG,EACpC,QAAQ,4CAA4C,EAAE,EACtD,UAAU,MAAM;AACnB,SAAO,mBAAmB,MAAM,EAC7B,QAAQ,uBAAuB,GAAG,EAClC,QAAQ,QAAQ,GAAG;AACxB;AAQA,SAAS,gCAAgC,MAAsB;AAC7D,SAAO,sBAAsB,IAAI,EAC9B,QAAQ,YAAY,GAAG,EACvB,QAAQ,QAAQ,GAAG;AACxB;AAYO,SAAS,gBAAgB,SAA2B;AACzD,QAAM,YAAY,iBAAiB,OAAO;AAC1C,QAAM,aAAa,sBAAsB,OAAO;AAChD,QAAM,gBAAgB,gCAAgC,OAAO;AAC7D,QAAM,UAAoB,CAAC;AAE3B,aAAW,WAAW,oBAAoB;AACxC,QAAI,QAAQ,KAAK,SAAS,KAAK,QAAQ,KAAK,UAAU,KAAK,QAAQ,KAAK,aAAa,GAAG;AACtF,cAAQ,KAAK,QAAQ,OAAO,MAAM,GAAG,EAAE,CAAC;AAAA,IAC1C;AAAA,EACF;AAGA,aAAW,QAAQ,CAAC,WAAW,UAAU,GAAG;AAC1C,UAAM,aAAa,KAAK,MAAM,2BAA2B;AACzD,QAAI,CAAC,WAAY;AACjB,eAAW,OAAO,YAAY;AAC5B,UAAI;AACF,cAAM,UAAU,KAAK,GAAG;AACxB,YAAI,mBAAmB,KAAK,OAAK,EAAE,KAAK,OAAO,CAAC,GAAG;AACjD,cAAI,CAAC,QAAQ,SAAS,0BAA0B,GAAG;AACjD,oBAAQ,KAAK,0BAA0B;AAAA,UACzC;AAAA,QACF;AAAA,MACF,QAAQ;AAAA,MAAsB;AAAA,IAChC;AAAA,EACF;AAEA,SAAO;AACT;AAcO,SAAS,uBACd,UACA,YAAY,KACG;AACf,SAAO,SACJ,OAAO,CAAC,MAAM,EAAE,SAAS,QAAQ,EACjC,IAAI,CAAC,MAAM;AACV,UAAM,aAAa,iBAAiB,EAAE,OAAO;AAC7C,UAAM,WAAW,WACd,QAAQ,2EAA2E,YAAY,EAC/F,MAAM,GAAG,SAAS;AACrB,WAAO,EAAE,MAAM,EAAE,MAAM,SAAS,SAAS;AAAA,EAC3C,CAAC;AACL;AAOO,SAAS,6BACd,UACA,YAAY,KAC8C;AAC1D,QAAM,WAAW,uBAAuB,UAAU,SAAS;AAC3D,QAAM,aAAa,SAAS,IAAI,OAAK,EAAE,OAAO,EAAE,KAAK,GAAG;AACxD,QAAM,oBAAoB,gBAAgB,UAAU;AACpD,SAAO,EAAE,UAAU,UAAU,kBAAkB;AACjD;AASO,SAAS,6BACd,QACA,cACQ;AACR,MAAI,CAAC,gBAAgB,aAAa,SAAS,GAAI,QAAO;AAGtD,QAAM,SAAS,aACZ,MAAM,OAAO,EACb,IAAI,OAAK,EAAE,KAAK,CAAC,EACjB,OAAO,OAAK,EAAE,UAAU,EAAE;AAE7B,MAAI,WAAW;AACf,aAAW,SAAS,QAAQ;AAE1B,UAAM,MAAM,SAAS,YAAY,EAAE,QAAQ,MAAM,YAAY,CAAC;AAC9D,QAAI,OAAO,GAAG;AACZ,iBAAW,SAAS,MAAM,GAAG,GAAG,IAAI,0CAAqC,SAAS,MAAM,MAAM,MAAM,MAAM;AAAA,IAC5G;AAAA,EACF;AAEA,SAAO;AACT;;;ACjKO,IAAM,kBAAN,MAAiD;AAAA,EACtD,YACmB,MAAgD,CAAC,MAAM,QAAQ,IAAI,KAAK,UAAU,CAAC,CAAC,GACrG;AADiB;AAAA,EAChB;AAAA,EADgB;AAAA,EAGX,KAAK,OAAkC,OAAe,KAAqB,OAAgC,CAAC,GAAG;AACrH,SAAK,IAAI;AAAA,MACP;AAAA,MACA;AAAA,MACA,OAAM,oBAAI,KAAK,GAAE,YAAY;AAAA,MAC7B,WAAW,IAAI;AAAA,MACf,WAAW,IAAI;AAAA,MACf,YAAY,KAAK,IAAI,IAAI,IAAI;AAAA,MAC7B,GAAG;AAAA,IACL,CAAC;AAAA,EACH;AAAA,EAEA,eAAe,KAAqB;AAAE,SAAK,KAAK,QAAQ,yBAAyB,GAAG;AAAA,EAAE;AAAA,EACtF,kBAAkB,KAAqB,MAAqE;AAC1G,SAAK,KAAK,QAAQ,4BAA4B,KAAK,IAAI;AAAA,EACzD;AAAA,EACA,cAAc,KAAqB,QAA2B;AAC5D,SAAK,KAAK,QAAQ,wBAAwB,KAAK,MAA4C;AAAA,EAC7F;AAAA,EACA,cAAc,KAAqB,MAAyD;AAC1F,SAAK,KAAK,QAAQ,sBAAsB,KAAK,IAAI;AAAA,EACnD;AAAA,EACA,eAAe,KAAqB,eAAuB;AACzD,SAAK,KAAK,QAAQ,0BAA0B,KAAK,EAAE,cAAc,CAAC;AAAA,EACpE;AAAA,EACA,oBAAoB,KAAqB,MAAoE;AAC3G,SAAK,KAAK,QAAQ,qBAAqB,KAAK,IAAI;AAAA,EAClD;AAAA,EACA,kBAAkB,KAAqB,OAA0B;AAC/D,SAAK,KAAK,QAAQ,4BAA4B,KAAK,KAA2C;AAAA,EAChG;AAAA,EACA,cAAc,KAAqB,MAAoD;AACrF,SAAK,KAAK,SAAS,wBAAwB,KAAK,IAAI;AAAA,EACtD;AAAA,EACA,kBAAkB,KAAqB,MAA2E;AAChH,SAAK,KAAK,SAAS,4BAA4B,KAAK,IAAI;AAAA,EAC1D;AACF;AAMO,IAAM,oBAAN,MAAmD;AAAA,EACxD,YAA6B,WAA8B;AAA9B;AAAA,EAA+B;AAAA,EAA/B;AAAA,EAE7B,MAAc,OAAwC,UAAa,MAAgC;AACjG,eAAW,OAAO,KAAK,WAAW;AAChC,YAAM,KAAK,IAAI,KAAK;AACpB,UAAI,CAAC,GAAI;AACT,UAAI;AACF,cAAM,GAAG,MAAM,KAAK,IAAI;AAAA,MAC1B,SAAS,KAAK;AACZ,gBAAQ,KAAK,4BAA4B,KAAK,WAAW,eAAe,QAAQ,IAAI,UAAU,GAAG;AAAA,MACnG;AAAA,IACF;AAAA,EACF;AAAA,EAEA,iBAAiB,CAAC,QAAwB,KAAK,OAAO,kBAAkB,GAAG;AAAA,EAC3E,oBAAoB,CAAC,KAAqB,SACxC,KAAK,OAAO,qBAAqB,KAAK,IAAI;AAAA,EAC5C,gBAAgB,CAAC,KAAqB,WACpC,KAAK,OAAO,iBAAiB,KAAK,MAAM;AAAA,EAC1C,gBAAgB,CAAC,KAAqB,SACpC,KAAK,OAAO,iBAAiB,KAAK,IAAI;AAAA,EACxC,iBAAiB,CAAC,KAAqB,kBACrC,KAAK,OAAO,kBAAkB,KAAK,aAAa;AAAA,EAClD,sBAAsB,CAAC,KAAqB,SAC1C,KAAK,OAAO,uBAAuB,KAAK,IAAI;AAAA,EAC9C,oBAAoB,CAAC,KAAqB,UACxC,KAAK,OAAO,qBAAqB,KAAK,KAAK;AAAA,EAC7C,gBAAgB,CAAC,KAAqB,SACpC,KAAK,OAAO,iBAAiB,KAAK,IAAI;AAAA,EACxC,oBAAoB,CAAC,KAAqB,SACxC,KAAK,OAAO,qBAAqB,KAAK,IAAI;AAC9C;AAMO,SAAS,oBAA4B;AAC1C,QAAM,QAAQ,IAAI,WAAW,EAAE;AAC/B,aAAW,OAAO,gBAAgB,KAAK;AACvC,QAAM,MAAM,MAAM,KAAK,KAAK,EAAE,IAAI,CAAC,MAAM,EAAE,SAAS,EAAE,EAAE,SAAS,GAAG,GAAG,CAAC,EAAE,KAAK,EAAE;AACjF,SAAO,OAAO,GAAG;AACnB;;;AH5JO,SAAS,mBAAmB,QAAuB;AAGxD,MAAI,CAAC,OAAO,KAAK,gBAAgB,CAAC,OAAO,KAAK,UAAU;AACtD,UAAM,IAAI;AAAA,MACR;AAAA,IAEF;AAAA,EACF;AACA,QAAM,KAAK,IAAI,KAAK;AACpB,QAAM,SAAS,OAAO,oBAAoB;AAC1C,QAAM,iBAAiC,OAAO,kBAAkB,IAAI,qBAAqB;AACzF,QAAM,kBAAkB,OAAO,aAAa,EAAE,OAAO,IAAI,eAAe,GAAG;AAC3E,QAAM,aAAa,OAAO,cAAc,IAAI,iBAAiB;AAC7D,QAAM,gBAAgB,OAAO,iBAAiB;AAC9C,QAAM,MAAmC,OAAO;AAIhD,KAAG,IAAI,2BAA2B,OAAO,MAAM;AAC7C,UAAM,OAAO,EAAE,IAAI,MAAM,MAAM;AAC/B,UAAM,QAAQ,MAAM,OAAO,aAAa,IAAI;AAC5C,QAAI,CAAC,MAAO,QAAO,EAAE,KAAK,EAAE,OAAO,mCAAmC,GAAG,GAAG;AAE5E,UAAM,iBAAiD;AAAA,MACrD;AAAA,QACE,MAAM;AAAA,QACN,UAAU,OAAO,KAAK;AAAA,QACtB,UAAU,OAAO,KAAK;AAAA,QACtB,kBAAkB,OAAO,KAAK;AAAA,MAChC;AAAA,IACF;AACA,QAAI,OAAO,KAAK;AACd,qBAAe,KAAK;AAAA,QAClB,MAAM;AAAA,QACN,OAAO,OAAO,IAAI;AAAA,QAClB,QAAQ,OAAO,IAAI,UAAU;AAAA,MAC/B,CAAC;AAAA,IACH;AACA,mBAAe,KAAK,EAAE,MAAM,WAAW,QAAQ,YAAY,CAAC;AAE5D,WAAO,EAAE,KAAK;AAAA,MACZ,MAAM,MAAM;AAAA,MACZ,SAAS;AAAA,QACP,eAAe,MAAM;AAAA,QACrB,UAAU;AAAA,QACV,sBAAsB,MAAM;AAAA,MAC9B;AAAA,MACA,SAAS;AAAA,QACP,MAAM,MAAM,kBAAkB,cAAc;AAAA,QAC5C,UAAU,MAAM,mBAAmB,OAAO,WAAW;AAAA,MACvD;AAAA,MACA,iBAAiB;AAAA,MACjB,cAAc,CAAC,oBAAoB,WAAW;AAAA,MAC9C,mBAAmB;AAAA,IACrB,CAAC;AAAA,EACH,CAAC;AAID,KAAG,KAAK,2BAA2B,OAAO,MAAM;AAC9C,UAAM,OAAO,EAAE,IAAI,MAAM,MAAM;AAC/B,UAAM,UAAU,KAAK,IAAI;AACzB,UAAM,YAAY,kBAAkB;AACpC,UAAM,MAAsB,EAAE,WAAW,WAAW,MAAM,QAAQ;AAElE,UAAM,KAAK,iBAAiB,GAAG;AAG/B,UAAM,QAAQ,MAAM,OAAO,aAAa,IAAI;AAC5C,QAAI,CAAC,OAAO;AACV,aAAO,EAAE,KAAK,EAAE,OAAO,EAAE,SAAS,mBAAmB,MAAM,YAAY,EAAE,GAAG,GAAG;AAAA,IACjF;AAGA,UAAM,gBAAgB,SAAS,EAAE,IAAI,OAAO,gBAAgB,KAAK,KAAK,EAAE;AACxE,QAAI,gBAAgB,OAAO;AACzB,YAAM,KAAK,iBAAiB,KAAK,aAAa;AAC9C,aAAO,EAAE;AAAA,QACP,EAAE,OAAO,EAAE,SAAS,qCAAqC,MAAM,kBAAkB,EAAE;AAAA,QACnF;AAAA,MACF;AAAA,IACF;AAEA,QAAI;AACJ,QAAI;AACF,aAAO,MAAM,EAAE,IAAI,KAAK;AAAA,IAC1B,QAAQ;AACN,aAAO,EAAE,KAAK,EAAE,OAAO,EAAE,SAAS,gBAAgB,MAAM,kBAAkB,EAAE,GAAG,GAAG;AAAA,IACpF;AACA,QAAI,CAAC,KAAK,UAAU,QAAQ;AAC1B,aAAO,EAAE,KAAK,EAAE,OAAO,EAAE,SAAS,2BAA2B,MAAM,kBAAkB,EAAE,GAAG,GAAG;AAAA,IAC/F;AAGA,UAAM,kBAAkB,EAAE,IAAI,OAAO,qBAAqB;AAC1D,UAAM,aAAa,EAAE,IAAI,OAAO,eAAe,KAAK;AACpD,QAAI,aAA4B;AAChC,QAAI,gBAA+B;AACnC,QAAI,UAA6B;AAEjC,QAAI,iBAAiB;AACnB,YAAM,SAAS,MAAM,WAAW,iBAAiB,OAAO,MAAM,UAAU;AACxE,UAAI,CAAC,QAAQ;AACX,cAAM,KAAK,gBAAgB,KAAK,EAAE,QAAQ,QAAQ,MAAM,sBAAsB,YAAY,IAAI,CAAC;AAC/F,eAAO,EAAE;AAAA,UACP,EAAE,OAAO,EAAE,SAAS,+BAA+B,MAAM,wBAAwB,MAAM,qBAAqB,EAAE;AAAA,UAC9G,EAAE,QAAQ,KAAK,SAAS,EAAE,sBAAsB,aAAa,gBAAgB,UAAU,EAAE;AAAA,QAC3F;AAAA,MACF;AACA,mBAAa;AACb,sBAAgB;AAAA,IAClB,WAAW,OAAO,OAAO,WAAW,YAAY,EAAE,WAAW,UAAU,GAAG;AACxE,YAAM,SAAS,MAAM,UAAU,YAAY,OAAO,KAAK,OAAO,IAAI;AAClE,UAAI,CAAC,QAAQ;AACX,cAAM,QAAQ,OAAO,IAAI;AACzB,cAAM,SAAS,OAAO,IAAI,UAAU;AACpC,cAAM,KAAK,gBAAgB,KAAK,EAAE,QAAQ,OAAO,MAAM,0BAA0B,YAAY,IAAI,CAAC;AAClG,eAAO,EAAE;AAAA,UACP,EAAE,OAAO,EAAE,SAAS,8BAA8B,MAAM,wBAAwB,MAAM,yBAAyB,EAAE;AAAA,UACjH,EAAE,QAAQ,KAAK,SAAS,EAAE,oBAAoB,kBAAkB,KAAK,cAAc,MAAM,KAAK,gBAAgB,UAAU,EAAE;AAAA,QAC5H;AAAA,MACF;AACA,mBAAa;AACb,sBAAgB;AAAA,IAClB,WAAW,WAAW,WAAW,SAAS,GAAG;AAC3C,YAAM,SAAS,OAAO,gBAAgB;AACtC,YAAM,MAAM,MAAM,OAAO,UAAU;AACnC,UAAI,CAAC,KAAK;AACR,cAAM,KAAK,gBAAgB,KAAK,EAAE,QAAQ,UAAU,MAAM,mBAAmB,YAAY,IAAI,CAAC;AAC9F,eAAO,EAAE;AAAA,UACP,EAAE,OAAO,EAAE,SAAS,mBAAmB,MAAM,uBAAuB,EAAE;AAAA,UACtE,EAAE,QAAQ,KAAK,SAAS,EAAE,gBAAgB,UAAU,EAAE;AAAA,QACxD;AAAA,MACF;AAGA,UAAI,IAAI,UAAU,IAAI,OAAO,SAAS,KAAK,CAAC,IAAI,OAAO,SAAS,aAAa,GAAG;AAC9E,cAAM,KAAK,gBAAgB,KAAK,EAAE,QAAQ,UAAU,MAAM,sBAAsB,YAAY,IAAI,CAAC;AACjG,eAAO,EAAE;AAAA,UACP,EAAE,OAAO,EAAE,SAAS,mCAAmC,aAAa,IAAI,MAAM,aAAa,MAAM,qBAAqB,EAAE;AAAA,UACxH,EAAE,QAAQ,KAAK,SAAS,EAAE,gBAAgB,UAAU,EAAE;AAAA,QACxD;AAAA,MACF;AAEA,mBAAa,IAAI;AACjB,sBAAgB;AAChB,gBAAU;AAAA,IACZ,OAAO;AAEL,YAAM,KAAK,gBAAgB,KAAK,EAAE,QAAQ,QAAQ,MAAM,oBAAoB,YAAY,IAAI,CAAC;AAC7F,YAAM,UAAoB,CAAC,MAAM;AACjC,UAAI,OAAO,IAAK,SAAQ,KAAK,KAAK;AAClC,cAAQ,KAAK,SAAS;AAEtB,YAAM,UAAkC;AAAA,QACtC,sBAAsB,QAAQ,KAAK,IAAI;AAAA,QACvC,gBAAgB;AAAA,MAClB;AACA,UAAI,OAAO,KAAK;AACd,gBAAQ,kBAAkB,IAAI,kBAAkB,OAAO,IAAI,KAAK,cAAc,OAAO,IAAI,UAAU,cAAc;AAAA,MACnH;AAEA,aAAO,EAAE,KAAK;AAAA,QACZ,OAAO;AAAA,UACL,SAAS;AAAA,UACT,MAAM;AAAA,UACN,iBAAiB;AAAA,UACjB,MAAM;AAAA,YACJ,UAAU,OAAO,KAAK;AAAA,YACtB,UAAU,OAAO,KAAK;AAAA,YACtB,iBAAiB,OAAO,KAAK;AAAA,YAC7B,8BAA8B;AAAA,UAChC;AAAA,UACA,GAAI,OAAO,MAAM;AAAA,YACf,KAAK,EAAE,OAAO,OAAO,IAAI,OAAO,QAAQ,OAAO,IAAI,UAAU,eAAe;AAAA,UAC9E,IAAI,CAAC;AAAA,UACL,SAAS;AAAA,YACP,cAAc,OAAO,UAAU,GAAG,OAAO,OAAO,WAAW,IAAI,cAAc;AAAA,UAC/E;AAAA,QACF;AAAA,MACF,GAAG,EAAE,QAAQ,KAAK,QAAQ,CAAC;AAAA,IAC7B;AAEA,UAAM,KAAK,oBAAoB,KAAK,EAAE,QAAQ,eAAe,YAAyB,OAAO,SAAS,MAAM,CAAC;AAG7G,UAAM,qBAAqB,SAAS,qBAChC,EAAE,OAAO,QAAQ,oBAAoB,eAAe,GAAG,IACvD;AAEJ,UAAM,KAAK,MAAM,eAAe,YAAa,oBAAoB,cAAc;AAC/E,QAAI,CAAC,GAAG,SAAS;AACf,YAAM,KAAK,gBAAgB,KAAK,EAAE,YAAyB,mBAAmB,GAAG,qBAAqB,GAAG,CAAC;AAC1G,aAAO,EAAE;AAAA,QACP,EAAE,OAAO,EAAE,SAAS,uBAAuB,MAAM,oBAAoB,aAAa,GAAG,kBAAkB,EAAE;AAAA,QACzG,EAAE,QAAQ,KAAK,SAAS,EAAE,eAAe,OAAO,GAAG,qBAAqB,EAAE,GAAG,gBAAgB,UAAU,EAAE;AAAA,MAC3G;AAAA,IACF;AAGA,UAAM,EAAE,UAAU,UAAU,kBAAkB,IAAI,6BAA6B,KAAK,UAAU,MAAM;AAEpG,QAAI,kBAAkB,SAAS,GAAG;AAChC,YAAM,KAAK,sBAAsB,KAAK;AAAA,QACpC;AAAA,QACA,UAAU;AAAA,QACV,SAAS,CAAC,CAAC,OAAO;AAAA,MACpB,CAAC;AAED,UAAI,OAAO,gBAAgB;AACzB,eAAO,EAAE;AAAA,UACP,EAAE,OAAO,EAAE,SAAS,yDAAyD,MAAM,2BAA2B,EAAE;AAAA,UAChH,EAAE,QAAQ,KAAK,SAAS,EAAE,gBAAgB,UAAU,EAAE;AAAA,QACxD;AAAA,MACF;AAAA,IAEF;AAEA,UAAM,cAAc,SACjB,OAAO,CAAC,MAAM,EAAE,SAAS,MAAM,EAC/B,IAAI,CAAC,MAAM,EAAE,OAAO,EACpB,KAAK,MAAM;AAEd,QAAI,CAAC,aAAa;AAChB,aAAO,EAAE,KAAK,EAAE,OAAO,EAAE,SAAS,4BAA4B,MAAM,kBAAkB,EAAE,GAAG,GAAG;AAAA,IAChG;AAIA,QAAI,OAAO,mBAAmB;AAC5B,YAAM,QAAQ,MAAM,OAAO,kBAAkB,OAAO;AAAA,QAClD,QAAQ;AAAA,QACR;AAAA,QACA,OAAO,SAAS;AAAA,QAChB;AAAA,MACF,CAAC;AACD,UAAI,CAAC,MAAM,OAAO;AAChB,eAAO,EAAE;AAAA,UACP;AAAA,YACE,OAAO;AAAA,cACL,SAAS,MAAM;AAAA,cACf,MAAM;AAAA,cACN,MAAM,MAAM;AAAA,YACd;AAAA,UACF;AAAA,UACA,EAAE,QAAQ,KAAK,SAAS,EAAE,gBAAgB,UAAU,EAAE;AAAA,QACxD;AAAA,MACF;AAAA,IACF;AAGA,UAAM,cAAc,KAAK,KAAK,YAAY,SAAS,CAAC;AACpD,QAAI,eAAe;AAEnB,UAAM,SAAS,IAAI,eAAe;AAAA,MAChC,MAAM,MAAM,YAAY;AACtB,cAAM,UAAU,IAAI,YAAY;AAChC,cAAM,YAAY,CAAC,aAAqB;AAEtC,gBAAM,QAAQ,6BAA6B,UAAU,MAAM,YAAY;AACvE,0BAAgB,KAAK,KAAK,MAAM,SAAS,CAAC;AAC1C,gBAAM,QAA6B;AAAA,YACjC,IAAI,YAAY,KAAK,IAAI,CAAC;AAAA,YAC1B,QAAQ;AAAA,YACR,SAAS,KAAK,MAAM,KAAK,IAAI,IAAI,GAAI;AAAA,YACrC,OAAO,MAAM;AAAA,YACb,SAAS,CAAC,EAAE,OAAO,GAAG,OAAO,EAAE,SAAS,MAAM,GAAG,eAAe,KAAK,CAAC;AAAA,UACxE;AACA,qBAAW,QAAQ,QAAQ,OAAO,SAAS,KAAK,UAAU,KAAK,CAAC;AAAA;AAAA,CAAM,CAAC;AAAA,QACzE;AAEA,YAAI;AACF,gBAAM,MAAM,MAAM,OAAO,WAAW,KAAK;AACzC,gBAAM,eAAe,IAAI,aAAa,aAAa;AAAA,YACjD,WAAW,YAAY,UAAU;AAAA,YACjC,cAAc,MAAM;AAAA,UACtB,CAAC;AAED,2BAAiB,SAAS,cAAc;AACtC,gBACE,MAAM,SAAS,0BACf,MAAM,MAAM,MAAM,SAAS,UAC3B,MAAM,KAAK,OACX;AACA,wBAAU,MAAM,KAAK,KAAK;AAAA,YAC5B;AAAA,UACF;AAGA,gBAAM,OAA4B;AAAA,YAChC,IAAI,YAAY,KAAK,IAAI,CAAC;AAAA,YAC1B,QAAQ;AAAA,YACR,SAAS,KAAK,MAAM,KAAK,IAAI,IAAI,GAAI;AAAA,YACrC,OAAO,MAAM;AAAA,YACb,SAAS,CAAC,EAAE,OAAO,GAAG,OAAO,CAAC,GAAG,eAAe,OAAO,CAAC;AAAA,UAC1D;AACA,qBAAW,QAAQ,QAAQ,OAAO,SAAS,KAAK,UAAU,IAAI,CAAC;AAAA;AAAA,CAAM,CAAC;AACtE,qBAAW,QAAQ,QAAQ,OAAO,kBAAkB,CAAC;AAGrD,gBAAM,aAAa,cAAc,gBAAgB,MAAM;AACvD,gBAAM,cAAc,aAAa,IAAI,MAAM;AAC3C,gBAAM,cAAc,YAAY,MAAM;AAEtC,gBAAM,aAAa;AAAA,YACjB,WAAW,IAAI;AAAA,YACf,SAAS,MAAM;AAAA,YACf,WAAW,MAAM;AAAA,YACjB;AAAA,YACA;AAAA,YACA;AAAA,YACA;AAAA,YACA,cAAc;AAAA,YACd,gBAAgB;AAAA,YAChB,gBAAgB;AAAA,YAChB,YAAY,KAAK,IAAI,IAAI;AAAA,UAC3B;AAEA,gBAAM,OAAO,YAAY,UAAU;AACnC,gBAAM,KAAK,oBAAoB,KAAK,UAAU;AAE9C,cAAI,OAAO,eAAe;AACxB,kBAAM,OAAO;AAAA,cACX,EAAE,QAAQ,eAAe,YAAyB,WAAW,IAAI,UAAU;AAAA,cAC3E;AAAA,YACF,EAAE,MAAM,OAAM,QAAO;AACnB,oBAAM,MAAM,eAAe,QAAQ,IAAI,UAAU,OAAO,GAAG;AAC3D,sBAAQ,MAAM,yCAAyC,UAAU,KAAK,GAAG,EAAE;AAC3E,oBAAM,KAAK,oBAAoB,KAAK,EAAE,YAAyB,QAAQ,eAAe,cAAc,IAAI,CAAC;AAAA,YAC3G,CAAC;AAAA,UACH;AAAA,QACF,SAAS,KAAK;AAEZ,gBAAM,aAAa,eAAe,QAAQ,IAAI,UAAU,OAAO,GAAG;AAClE,gBAAM,cACJ,WAAW,SAAS,GAAG,KAAK,WAAW,SAAS,IAAI,IAChD,yBACA;AACN,gBAAM,KAAK,gBAAgB,KAAK,EAAE,YAAyB,cAAc,WAAW,CAAC;AACrF,qBAAW;AAAA,YACT,QAAQ,OAAO,SAAS,KAAK,UAAU,EAAE,OAAO,EAAE,SAAS,aAAa,MAAM,eAAe,EAAE,CAAC,CAAC;AAAA;AAAA,CAAM;AAAA,UACzG;AAAA,QACF,UAAE;AACA,qBAAW,MAAM;AAAA,QACnB;AAAA,MACF;AAAA,IACF,CAAC;AAED,WAAO,IAAI,SAAS,QAAQ;AAAA,MAC1B,SAAS;AAAA,QACP,gBAAgB;AAAA,QAChB,iBAAiB;AAAA,QACjB,gBAAgB;AAAA,QAChB,gBAAgB,MAAM;AAAA,QACtB,mBAAmB,MAAM,kBAAkB,cAAc;AAAA,QACzD,oBAAoB;AAAA,QACpB,qBAAqB,kBAAkB,SAAS,YAAY;AAAA,QAC5D,GAAI,GAAG,cAAc,SAAY,EAAE,yBAAyB,OAAO,GAAG,SAAS,EAAE,IAAI,CAAC;AAAA,MACxF;AAAA,IACF,CAAC;AAAA,EACH,CAAC;AAED,SAAO;AACT;","names":[]}
package/dist/index.d.ts CHANGED
@@ -1,6 +1,6 @@
1
1
  export { createAgentGateway } from './middleware.js';
2
- import { A as ApiKeyInfo, M as MppConfig, X as X402Config, C as ChatMessage } from './types-14xV8J4G.js';
3
- export { a as AgentMeta, b as AuthFailureReason, c as ChatCompletionChunk, d as ChatCompletionRequest, e as CompositeObserver, f as ConsoleObserver, G as GatewayConfig, g as GatewayObserver, h as GatewayUsageEvent, P as PaymentMethod, i as PaymentResult, R as RequestContext, S as SandboxBox, j as SandboxStreamEvent, k as generateRequestId } from './types-14xV8J4G.js';
2
+ import { A as ApiKeyInfo, M as MppConfig, X as X402Config, C as ChatMessage } from './types-BbTSfNhx.js';
3
+ export { a as AgentMeta, b as AuthFailureReason, c as ChatCompletionChunk, d as ChatCompletionRequest, e as CompositeObserver, f as ConsoleObserver, G as GatewayConfig, g as GatewayObserver, h as GatewayUsageEvent, P as PaymentMethod, i as PaymentResult, R as RequestContext, S as SandboxBox, j as SandboxStreamEvent, k as generateRequestId } from './types-BbTSfNhx.js';
4
4
  import { NonceStore } from './nonce-store.js';
5
5
  export { KvNonceStore, MemoryNonceStore } from './nonce-store.js';
6
6
  export { KvRateLimitStore, MemoryRateLimitStore, RateLimitConfig, RateLimitResult, RateLimitStore, checkRateLimit } from './rate-limit.js';
@@ -13,8 +13,12 @@ import 'hono';
13
13
  * Verify x402 SpendAuth signature (EIP-712).
14
14
  * Returns the signer address (commitment) if valid, null otherwise.
15
15
  *
16
- * DEMO MODE (demoMode: true): accepts any well-formed header structure.
17
- * PRODUCTION: requires config.verifySigner callback for on-chain verification.
16
+ * demoMode: true accepts any well-formed header
17
+ * shape after replay/expiry checks.
18
+ * Tests + local dev only.
19
+ * verifySigner present — production verification path.
20
+ * neither — rejected by createAgentGateway and
21
+ * by this function as defense-in-depth.
18
22
  */
19
23
  declare function verifyX402(spendAuthHeader: string, config: X402Config, nonceStore?: NonceStore): Promise<string | null>;
20
24
  /**
package/dist/index.js CHANGED
@@ -14,7 +14,7 @@ import {
14
14
  redactSystemPromptFromOutput,
15
15
  verifyMpp,
16
16
  verifyX402
17
- } from "./chunk-E4HSWMEY.js";
17
+ } from "./chunk-373QHRKV.js";
18
18
  import {
19
19
  KvNonceStore,
20
20
  MemoryNonceStore
@@ -1,6 +1,6 @@
1
1
  import * as hono_types from 'hono/types';
2
2
  import { Hono } from 'hono';
3
- import { G as GatewayConfig } from './types-14xV8J4G.js';
3
+ import { G as GatewayConfig } from './types-BbTSfNhx.js';
4
4
  import './nonce-store.js';
5
5
  import './rate-limit.js';
6
6
 
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  createAgentGateway
3
- } from "./chunk-E4HSWMEY.js";
3
+ } from "./chunk-373QHRKV.js";
4
4
  import "./chunk-M7ZJAK4K.js";
5
5
  import "./chunk-XCTXHZ76.js";
6
6
  export {
@@ -175,6 +175,16 @@ interface MppConfig {
175
175
  interface PaymentResult {
176
176
  method: PaymentMethod;
177
177
  consumerId: string;
178
+ /**
179
+ * Per-request id (matches `RequestContext.requestId` from the
180
+ * Observer pattern). Threaded into `settlePayment` so callers can
181
+ * attribute revenue deterministically per-request without scanning
182
+ * a FIFO queue keyed by consumerId — when the same consumer is
183
+ * paying for two concurrent requests against agents A and B, FIFO
184
+ * misroutes one. With `requestId` the call site can write a
185
+ * settlement row keyed exactly to the request that earned it.
186
+ */
187
+ requestId: string;
178
188
  }
179
189
  interface ApiKeyInfo {
180
190
  keyId: string;
@@ -187,6 +197,15 @@ interface ApiKeyInfo {
187
197
  dailyLimit?: number;
188
198
  }
189
199
  interface GatewayUsageEvent {
200
+ /**
201
+ * Per-request id (matches `RequestContext.requestId`). Lets
202
+ * `recordUsage` correlate the usage row to the same request that
203
+ * `settlePayment` settles, observability hooks observe, and
204
+ * `onRequestComplete` reports — without re-deriving from a
205
+ * synthetic key. Required field as of 0.4.0; the gateway always has
206
+ * it in scope at the recordUsage call site.
207
+ */
208
+ requestId: string;
190
209
  agentId: string;
191
210
  agentSlug: string;
192
211
  consumerId: string;
@@ -220,6 +239,23 @@ interface GatewayConfig {
220
239
  resolveAgent: (slug: string) => Promise<AgentMeta | null>;
221
240
  /** Get a sandbox instance for the agent. Called after payment is verified. */
222
241
  getSandbox: (agent: AgentMeta) => Promise<SandboxBox>;
242
+ /**
243
+ * Optional host authorization hook fired after payment verification
244
+ * and before sandbox resolution. Use it for per-agent allowlists,
245
+ * per-consumer quotas, contract scope checks, and instance ownership.
246
+ */
247
+ authorizeConsumer?: (agent: AgentMeta, consumer: {
248
+ method: PaymentMethod;
249
+ consumerId: string;
250
+ keyId?: string;
251
+ requestId: string;
252
+ }) => Promise<{
253
+ allow: true;
254
+ } | {
255
+ allow: false;
256
+ reason: string;
257
+ code: string;
258
+ }>;
223
259
  /** Record a usage event after request completes. */
224
260
  recordUsage: (event: GatewayUsageEvent) => Promise<void>;
225
261
  /** x402 payment configuration */
package/dist/types.d.ts CHANGED
@@ -1,3 +1,3 @@
1
- export { a as AgentMeta, A as ApiKeyInfo, c as ChatCompletionChunk, d as ChatCompletionRequest, C as ChatMessage, G as GatewayConfig, h as GatewayUsageEvent, M as MppConfig, P as PaymentMethod, i as PaymentResult, S as SandboxBox, j as SandboxStreamEvent, X as X402Config } from './types-14xV8J4G.js';
1
+ export { a as AgentMeta, A as ApiKeyInfo, c as ChatCompletionChunk, d as ChatCompletionRequest, C as ChatMessage, G as GatewayConfig, h as GatewayUsageEvent, M as MppConfig, P as PaymentMethod, i as PaymentResult, S as SandboxBox, j as SandboxStreamEvent, X as X402Config } from './types-BbTSfNhx.js';
2
2
  import './nonce-store.js';
3
3
  import './rate-limit.js';
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@tangle-network/agent-gateway",
3
- "version": "0.3.5",
3
+ "version": "0.5.0",
4
4
  "type": "module",
5
5
  "description": "x402 + API key payment gateway for Tangle agent products. Mount on any Hono app to expose agents as paid OpenAI-compatible endpoints.",
6
6
  "exports": {
package/src/middleware.ts CHANGED
@@ -23,6 +23,14 @@ import { generateRequestId, type GatewayObserver, type RequestContext } from './
23
23
  * POST /:slug/chat/completions — OpenAI-compatible chat endpoint (paid)
24
24
  */
25
25
  export function createAgentGateway(config: GatewayConfig) {
26
+ // Production gateways must verify x402 signatures. Tests and local
27
+ // dev can opt into the explicit demo path.
28
+ if (!config.x402.verifySigner && !config.x402.demoMode) {
29
+ throw new Error(
30
+ 'createAgentGateway: x402.verifySigner is required in production. ' +
31
+ 'For tests, set x402.demoMode: true explicitly.',
32
+ )
33
+ }
26
34
  const gw = new Hono()
27
35
  const maxLen = config.maxMessageLength ?? 8000
28
36
  const rateLimitStore: RateLimitStore = config.rateLimitStore ?? new MemoryRateLimitStore()
@@ -242,6 +250,29 @@ export function createAgentGateway(config: GatewayConfig) {
242
250
  return c.json({ error: { message: 'No user message provided', type: 'invalid_request' } }, 400)
243
251
  }
244
252
 
253
+ // 5b. Optional host authorization. Runs after payment/rate-limit
254
+ // checks and before sandbox allocation.
255
+ if (config.authorizeConsumer) {
256
+ const authz = await config.authorizeConsumer(agent, {
257
+ method: paymentMethod,
258
+ consumerId: consumerId!,
259
+ keyId: keyInfo?.keyId,
260
+ requestId,
261
+ })
262
+ if (!authz.allow) {
263
+ return c.json(
264
+ {
265
+ error: {
266
+ message: authz.reason,
267
+ type: 'authorization_denied',
268
+ code: authz.code,
269
+ },
270
+ },
271
+ { status: 403, headers: { 'X-Request-Id': requestId } },
272
+ )
273
+ }
274
+ }
275
+
245
276
  // 6. Get sandbox and stream response with output filtering
246
277
  const inputTokens = Math.ceil(userMessage.length / 4)
247
278
  let outputTokens = 0
@@ -297,6 +328,7 @@ export function createAgentGateway(config: GatewayConfig) {
297
328
  const platformFee = totalCost * agent.platformFeePercent
298
329
 
299
330
  const usageEvent = {
331
+ requestId: ctx.requestId,
300
332
  agentId: agent.id,
301
333
  agentSlug: agent.slug,
302
334
  consumerId: consumerId!,
@@ -313,7 +345,10 @@ export function createAgentGateway(config: GatewayConfig) {
313
345
  await obs?.onRequestComplete?.(ctx, usageEvent)
314
346
 
315
347
  if (config.settlePayment) {
316
- await config.settlePayment({ method: paymentMethod, consumerId: consumerId! }, totalCost).catch(async err => {
348
+ await config.settlePayment(
349
+ { method: paymentMethod, consumerId: consumerId!, requestId: ctx.requestId },
350
+ totalCost,
351
+ ).catch(async err => {
317
352
  const msg = err instanceof Error ? err.message : String(err)
318
353
  console.error(`[agent-gateway] settlement failed for ${consumerId}: ${msg}`)
319
354
  await obs?.onSettlementError?.(ctx, { consumerId: consumerId!, method: paymentMethod, errorMessage: msg })
package/src/types.ts CHANGED
@@ -52,6 +52,16 @@ export interface MppConfig {
52
52
  export interface PaymentResult {
53
53
  method: PaymentMethod
54
54
  consumerId: string
55
+ /**
56
+ * Per-request id (matches `RequestContext.requestId` from the
57
+ * Observer pattern). Threaded into `settlePayment` so callers can
58
+ * attribute revenue deterministically per-request without scanning
59
+ * a FIFO queue keyed by consumerId — when the same consumer is
60
+ * paying for two concurrent requests against agents A and B, FIFO
61
+ * misroutes one. With `requestId` the call site can write a
62
+ * settlement row keyed exactly to the request that earned it.
63
+ */
64
+ requestId: string
55
65
  }
56
66
 
57
67
  export interface ApiKeyInfo {
@@ -68,6 +78,15 @@ export interface ApiKeyInfo {
68
78
  // --- Usage tracking ---
69
79
 
70
80
  export interface GatewayUsageEvent {
81
+ /**
82
+ * Per-request id (matches `RequestContext.requestId`). Lets
83
+ * `recordUsage` correlate the usage row to the same request that
84
+ * `settlePayment` settles, observability hooks observe, and
85
+ * `onRequestComplete` reports — without re-deriving from a
86
+ * synthetic key. Required field as of 0.4.0; the gateway always has
87
+ * it in scope at the recordUsage call site.
88
+ */
89
+ requestId: string
71
90
  agentId: string
72
91
  agentSlug: string
73
92
  consumerId: string
@@ -104,6 +123,16 @@ export interface GatewayConfig {
104
123
  /** Get a sandbox instance for the agent. Called after payment is verified. */
105
124
  getSandbox: (agent: AgentMeta) => Promise<SandboxBox>
106
125
 
126
+ /**
127
+ * Optional host authorization hook fired after payment verification
128
+ * and before sandbox resolution. Use it for per-agent allowlists,
129
+ * per-consumer quotas, contract scope checks, and instance ownership.
130
+ */
131
+ authorizeConsumer?: (
132
+ agent: AgentMeta,
133
+ consumer: { method: PaymentMethod; consumerId: string; keyId?: string; requestId: string },
134
+ ) => Promise<{ allow: true } | { allow: false; reason: string; code: string }>
135
+
107
136
  /** Record a usage event after request completes. */
108
137
  recordUsage: (event: GatewayUsageEvent) => Promise<void>
109
138
 
package/src/verify.ts CHANGED
@@ -5,8 +5,12 @@ import type { NonceStore } from './nonce-store'
5
5
  * Verify x402 SpendAuth signature (EIP-712).
6
6
  * Returns the signer address (commitment) if valid, null otherwise.
7
7
  *
8
- * DEMO MODE (demoMode: true): accepts any well-formed header structure.
9
- * PRODUCTION: requires config.verifySigner callback for on-chain verification.
8
+ * demoMode: true accepts any well-formed header
9
+ * shape after replay/expiry checks.
10
+ * Tests + local dev only.
11
+ * verifySigner present — production verification path.
12
+ * neither — rejected by createAgentGateway and
13
+ * by this function as defense-in-depth.
10
14
  */
11
15
  export async function verifyX402(
12
16
  spendAuthHeader: string,
@@ -37,12 +41,11 @@ export async function verifyX402(
37
41
  await nonceStore.markSeen(nonceKey, Math.max(ttl, 60))
38
42
  }
39
43
 
40
- // Production: delegate to on-chain verification
41
44
  if (config.verifySigner) {
42
45
  const verified = await config.verifySigner(raw)
43
46
  if (!verified) return null
44
47
  } else if (!config.demoMode) {
45
- console.warn('[agent-gateway] x402 verification running without verifySigner — set demoMode: true to suppress this warning')
48
+ return null
46
49
  }
47
50
 
48
51
  return raw.commitment
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../src/middleware.ts","../src/verify.ts","../src/filter.ts","../src/observer.ts"],"sourcesContent":["import { Hono } from 'hono'\nimport type {\n GatewayConfig,\n ChatCompletionRequest,\n ChatCompletionChunk,\n PaymentMethod,\n ApiKeyInfo,\n} from './types'\nimport { verifyX402, verifyMpp, defaultVerifyApiKey } from './verify'\nimport { filterConsumerMessagesStrict, redactSystemPromptFromOutput } from './filter'\nimport { checkRateLimit, MemoryRateLimitStore, type RateLimitStore } from './rate-limit'\nimport { MemoryNonceStore } from './nonce-store'\nimport { generateRequestId, type GatewayObserver, type RequestContext } from './observer'\n\n/**\n * Create a Hono router that serves the agent gateway.\n *\n * Mount at any path:\n * app.route('/v1/agents', createAgentGateway(config))\n *\n * Exposes:\n * GET /:slug/chat/completions — agent discovery metadata\n * POST /:slug/chat/completions — OpenAI-compatible chat endpoint (paid)\n */\nexport function createAgentGateway(config: GatewayConfig) {\n const gw = new Hono()\n const maxLen = config.maxMessageLength ?? 8000\n const rateLimitStore: RateLimitStore = config.rateLimitStore ?? new MemoryRateLimitStore()\n const globalRateLimit = config.rateLimit ?? { limit: 60, windowSeconds: 60 }\n const nonceStore = config.nonceStore ?? new MemoryNonceStore()\n const requiredScope = config.requiredScope ?? 'chat'\n const obs: GatewayObserver | undefined = config.observer\n\n // --- Discovery endpoint (no auth) ---\n\n gw.get('/:slug/chat/completions', async (c) => {\n const slug = c.req.param('slug')\n const agent = await config.resolveAgent(slug)\n if (!agent) return c.json({ error: 'Agent not found or not published' }, 404)\n\n const paymentMethods: Array<Record<string, unknown>> = [\n {\n type: 'x402',\n operator: config.x402.operatorAddress,\n chain_id: config.x402.chainId,\n credits_contract: config.x402.creditsAddress,\n },\n ]\n if (config.mpp) {\n paymentMethods.push({\n type: 'mpp',\n realm: config.mpp.realm,\n method: config.mpp.method ?? 'blueprintevm',\n })\n }\n paymentMethods.push({ type: 'api_key', prefix: 'sk_agent_' })\n\n return c.json({\n slug: agent.slug,\n pricing: {\n per_token_usd: agent.pricePerTokenUsd,\n currency: 'USD',\n platform_fee_percent: agent.platformFeePercent,\n },\n hosting: {\n mode: agent.sandboxEndpoint ? 'sovereign' : 'centralized',\n endpoint: agent.sandboxEndpoint ?? config.baseUrl ?? 'tangle.tools',\n },\n payment_methods: paymentMethods,\n capabilities: ['chat.completions', 'streaming'],\n openai_compatible: true,\n })\n })\n\n // --- Chat completions endpoint (paid) ---\n\n gw.post('/:slug/chat/completions', async (c) => {\n const slug = c.req.param('slug')\n const startMs = Date.now()\n const requestId = generateRequestId()\n const ctx: RequestContext = { requestId, agentSlug: slug, startMs }\n\n await obs?.onRequestStart?.(ctx)\n\n // 1. Resolve agent\n const agent = await config.resolveAgent(slug)\n if (!agent) {\n return c.json({ error: { message: 'Agent not found', type: 'not_found' } }, 404)\n }\n\n // 2. Body size limit (before parsing — DoS prevention)\n const contentLength = parseInt(c.req.header('Content-Length') ?? '0', 10)\n if (contentLength > 65536) {\n await obs?.onBodyTooLarge?.(ctx, contentLength)\n return c.json(\n { error: { message: 'Request body too large (max 64KB)', type: 'invalid_request' } },\n 413,\n )\n }\n\n let body: ChatCompletionRequest\n try {\n body = await c.req.json()\n } catch {\n return c.json({ error: { message: 'Invalid JSON', type: 'invalid_request' } }, 400)\n }\n if (!body.messages?.length) {\n return c.json({ error: { message: 'messages array required', type: 'invalid_request' } }, 400)\n }\n\n // 3. Authenticate — x402 SpendAuth, MPP, or API key\n const spendAuthHeader = c.req.header('X-Payment-Signature')\n const authHeader = c.req.header('Authorization') ?? ''\n let consumerId: string | null = null\n let paymentMethod: PaymentMethod = 'none'\n let keyInfo: ApiKeyInfo | null = null\n\n if (spendAuthHeader) {\n const signer = await verifyX402(spendAuthHeader, config.x402, nonceStore)\n if (!signer) {\n await obs?.onAuthFailure?.(ctx, { method: 'x402', code: 'invalid_spend_auth', httpStatus: 402 })\n return c.json(\n { error: { message: 'Invalid X-Payment-Signature', type: 'authentication_error', code: 'invalid_spend_auth' } },\n { status: 402, headers: { 'X-Payment-Required': 'spendauth', 'X-Request-Id': requestId } },\n )\n }\n consumerId = signer\n paymentMethod = 'x402'\n } else if (config.mpp && authHeader.toLowerCase().startsWith('payment ')) {\n const signer = await verifyMpp(authHeader, config.mpp, config.x402)\n if (!signer) {\n const realm = config.mpp.realm\n const method = config.mpp.method ?? 'blueprintevm'\n await obs?.onAuthFailure?.(ctx, { method: 'mpp', code: 'invalid_mpp_credential', httpStatus: 401 })\n return c.json(\n { error: { message: 'Invalid Payment credential', type: 'authentication_error', code: 'invalid_mpp_credential' } },\n { status: 401, headers: { 'WWW-Authenticate': `Payment realm=\"${realm}\", method=\"${method}\"`, 'X-Request-Id': requestId } },\n )\n }\n consumerId = signer\n paymentMethod = 'mpp'\n } else if (authHeader.startsWith('Bearer ')) {\n const verify = config.verifyApiKey ?? defaultVerifyApiKey\n const key = await verify(authHeader)\n if (!key) {\n await obs?.onAuthFailure?.(ctx, { method: 'apikey', code: 'invalid_api_key', httpStatus: 401 })\n return c.json(\n { error: { message: 'Invalid API key', type: 'authentication_error' } },\n { status: 401, headers: { 'X-Request-Id': requestId } },\n )\n }\n\n // Scope enforcement — API key must include the required scope\n if (key.scopes && key.scopes.length > 0 && !key.scopes.includes(requiredScope)) {\n await obs?.onAuthFailure?.(ctx, { method: 'apikey', code: 'insufficient_scope', httpStatus: 403 })\n return c.json(\n { error: { message: `API key missing required scope: ${requiredScope}`, type: 'forbidden', code: 'insufficient_scope' } },\n { status: 403, headers: { 'X-Request-Id': requestId } },\n )\n }\n\n consumerId = key.consumerId\n paymentMethod = 'apikey'\n keyInfo = key\n } else {\n // No payment — return 402 with instructions\n await obs?.onAuthFailure?.(ctx, { method: 'none', code: 'payment_required', httpStatus: 402 })\n const methods: string[] = ['x402']\n if (config.mpp) methods.push('mpp')\n methods.push('api_key')\n\n const headers: Record<string, string> = {\n 'X-Payment-Required': methods.join(', '),\n 'X-Request-Id': requestId,\n }\n if (config.mpp) {\n headers['WWW-Authenticate'] = `Payment realm=\"${config.mpp.realm}\", method=\"${config.mpp.method ?? 'blueprintevm'}\"`\n }\n\n return c.json({\n error: {\n message: 'Payment required',\n type: 'payment_required',\n payment_methods: methods,\n x402: {\n operator: config.x402.operatorAddress,\n chain_id: config.x402.chainId,\n credits_address: config.x402.creditsAddress,\n estimated_amount_per_request: '20000',\n },\n ...(config.mpp ? {\n mpp: { realm: config.mpp.realm, method: config.mpp.method ?? 'blueprintevm' },\n } : {}),\n api_key: {\n purchase_url: config.baseUrl ? `${config.baseUrl}/agents/${slug}/api-keys` : undefined,\n },\n },\n }, { status: 402, headers })\n }\n\n await obs?.onPaymentVerified?.(ctx, { method: paymentMethod, consumerId: consumerId!, keyId: keyInfo?.keyId })\n\n // 4. Rate limit — per-key override or global\n const effectiveRateLimit = keyInfo?.rateLimitPerMinute\n ? { limit: keyInfo.rateLimitPerMinute, windowSeconds: 60 }\n : globalRateLimit\n\n const rl = await checkRateLimit(consumerId!, effectiveRateLimit, rateLimitStore)\n if (!rl.allowed) {\n await obs?.onRateLimited?.(ctx, { consumerId: consumerId!, retryAfterSeconds: rl.retryAfterSeconds ?? 60 })\n return c.json(\n { error: { message: 'Rate limit exceeded', type: 'rate_limit_error', retry_after: rl.retryAfterSeconds } },\n { status: 429, headers: { 'Retry-After': String(rl.retryAfterSeconds ?? 60), 'X-Request-Id': requestId } },\n )\n }\n\n // 5. Filter messages — injection detection + sanitization\n const { messages: filtered, injectionWarnings } = filterConsumerMessagesStrict(body.messages, maxLen)\n\n if (injectionWarnings.length > 0) {\n await obs?.onInjectionDetected?.(ctx, {\n consumerId: consumerId!,\n patterns: injectionWarnings,\n blocked: !!config.blockInjection,\n })\n\n if (config.blockInjection) {\n return c.json(\n { error: { message: 'Request rejected: potential prompt injection detected', type: 'content_policy_violation' } },\n { status: 400, headers: { 'X-Request-Id': requestId } },\n )\n }\n // In non-blocking mode, continue but the warning is logged for auditing\n }\n\n const userMessage = filtered\n .filter((m) => m.role === 'user')\n .map((m) => m.content)\n .join('\\n\\n')\n\n if (!userMessage) {\n return c.json({ error: { message: 'No user message provided', type: 'invalid_request' } }, 400)\n }\n\n // 6. Get sandbox and stream response with output filtering\n const inputTokens = Math.ceil(userMessage.length / 4)\n let outputTokens = 0\n\n const stream = new ReadableStream({\n async start(controller) {\n const encoder = new TextEncoder()\n const sendChunk = (rawDelta: string) => {\n // Redact system prompt leakage from output\n const delta = redactSystemPromptFromOutput(rawDelta, agent.systemPrompt)\n outputTokens += Math.ceil(delta.length / 4)\n const chunk: ChatCompletionChunk = {\n id: `chatcmpl-${Date.now()}`,\n object: 'chat.completion.chunk',\n created: Math.floor(Date.now() / 1000),\n model: agent.slug,\n choices: [{ index: 0, delta: { content: delta }, finish_reason: null }],\n }\n controller.enqueue(encoder.encode(`data: ${JSON.stringify(chunk)}\\n\\n`))\n }\n\n try {\n const box = await config.getSandbox(agent)\n const promptStream = box.streamPrompt(userMessage, {\n sessionId: `consumer:${consumerId}`,\n systemPrompt: agent.systemPrompt,\n })\n\n for await (const event of promptStream) {\n if (\n event.type === 'message.part.updated' &&\n event.data?.part?.type === 'text' &&\n event.data.delta\n ) {\n sendChunk(event.data.delta)\n }\n }\n\n // Final chunk\n const done: ChatCompletionChunk = {\n id: `chatcmpl-${Date.now()}`,\n object: 'chat.completion.chunk',\n created: Math.floor(Date.now() / 1000),\n model: agent.slug,\n choices: [{ index: 0, delta: {}, finish_reason: 'stop' }],\n }\n controller.enqueue(encoder.encode(`data: ${JSON.stringify(done)}\\n\\n`))\n controller.enqueue(encoder.encode('data: [DONE]\\n\\n'))\n\n // 7. Record usage + settle payment\n const totalCost = (inputTokens + outputTokens) * agent.pricePerTokenUsd\n const ownerEarned = totalCost * (1 - agent.platformFeePercent)\n const platformFee = totalCost * agent.platformFeePercent\n\n const usageEvent = {\n agentId: agent.id,\n agentSlug: agent.slug,\n consumerId: consumerId!,\n paymentMethod,\n inputTokens,\n outputTokens,\n totalCostUsd: totalCost,\n ownerEarnedUsd: ownerEarned,\n platformFeeUsd: platformFee,\n durationMs: Date.now() - startMs,\n }\n\n await config.recordUsage(usageEvent)\n await obs?.onRequestComplete?.(ctx, usageEvent)\n\n if (config.settlePayment) {\n await config.settlePayment({ method: paymentMethod, consumerId: consumerId! }, totalCost).catch(async err => {\n const msg = err instanceof Error ? err.message : String(err)\n console.error(`[agent-gateway] settlement failed for ${consumerId}: ${msg}`)\n await obs?.onSettlementError?.(ctx, { consumerId: consumerId!, method: paymentMethod, errorMessage: msg })\n })\n }\n } catch (err) {\n // Sanitize error — never expose stack traces or internal paths\n const rawMessage = err instanceof Error ? err.message : String(err)\n const safeMessage =\n rawMessage.includes('/') || rawMessage.includes('\\\\')\n ? 'Internal agent error'\n : rawMessage\n await obs?.onStreamError?.(ctx, { consumerId: consumerId!, errorMessage: rawMessage })\n controller.enqueue(\n encoder.encode(`data: ${JSON.stringify({ error: { message: safeMessage, type: 'server_error' } })}\\n\\n`),\n )\n } finally {\n controller.close()\n }\n },\n })\n\n return new Response(stream, {\n headers: {\n 'Content-Type': 'text/event-stream',\n 'Cache-Control': 'no-cache',\n 'X-Request-Id': requestId,\n 'X-Agent-Slug': agent.slug,\n 'X-Agent-Hosting': agent.sandboxEndpoint ? 'sovereign' : 'centralized',\n 'X-Payment-Method': paymentMethod,\n 'X-Payment-Settled': paymentMethod === 'x402' ? 'pending' : 'true',\n ...(rl.remaining !== undefined ? { 'X-RateLimit-Remaining': String(rl.remaining) } : {}),\n },\n })\n })\n\n return gw\n}\n","import type { X402Config, MppConfig, ApiKeyInfo } from './types'\nimport type { NonceStore } from './nonce-store'\n\n/**\n * Verify x402 SpendAuth signature (EIP-712).\n * Returns the signer address (commitment) if valid, null otherwise.\n *\n * DEMO MODE (demoMode: true): accepts any well-formed header structure.\n * PRODUCTION: requires config.verifySigner callback for on-chain verification.\n */\nexport async function verifyX402(\n spendAuthHeader: string,\n config: X402Config,\n nonceStore?: NonceStore,\n): Promise<string | null> {\n try {\n const raw = JSON.parse(spendAuthHeader)\n if (!raw.commitment || !raw.signature || !raw.amount) return null\n if (raw.operator?.toLowerCase() !== config.operatorAddress.toLowerCase()) return null\n\n const amount = BigInt(raw.amount)\n const nonce = BigInt(raw.nonce)\n const expiry = BigInt(raw.expiry)\n\n // Reject expired payments\n if (expiry < BigInt(Math.floor(Date.now() / 1000))) return null\n\n // Reject zero-amount payments\n if (amount <= 0n) return null\n\n // Reject replayed nonces\n const nonceKey = `${raw.commitment}:${nonce.toString()}`\n if (nonceStore) {\n if (await nonceStore.hasSeen(nonceKey)) return null\n // Mark seen with TTL matching the expiry window (max 1 hour)\n const ttl = Math.min(Number(expiry) - Math.floor(Date.now() / 1000), 3600)\n await nonceStore.markSeen(nonceKey, Math.max(ttl, 60))\n }\n\n // Production: delegate to on-chain verification\n if (config.verifySigner) {\n const verified = await config.verifySigner(raw)\n if (!verified) return null\n } else if (!config.demoMode) {\n console.warn('[agent-gateway] x402 verification running without verifySigner — set demoMode: true to suppress this warning')\n }\n\n return raw.commitment\n } catch {\n return null\n }\n}\n\n/**\n * Verify MPP (Machine Payments Protocol) Authorization: Payment header.\n *\n * MPP uses `Authorization: Payment <method> <credential>` format where\n * the credential is a base64url-encoded JSON wrapping the same EIP-3009\n * payment payload that x402 uses. This means existing x402 wallets work\n * unchanged over the MPP wire format.\n *\n * Returns the signer address if valid, null otherwise.\n * In demo mode, accepts any well-formed Payment header.\n */\nexport async function verifyMpp(\n authHeader: string,\n _config: MppConfig,\n x402Config: X402Config,\n): Promise<string | null> {\n // MPP format: \"Payment <method> <base64url-credential>\"\n const match = authHeader.match(/^Payment\\s+(\\S+)\\s+(\\S+)$/i)\n if (!match) return null\n\n const [, , credentialB64] = match\n\n try {\n // Decode base64url credential → JSON with the same EIP-3009 payload\n const decoded = Buffer.from(credentialB64, 'base64url').toString('utf-8')\n const credential = JSON.parse(decoded)\n\n // The credential payload wraps the same fields x402 uses\n const payload = credential.payload ?? credential\n if (!payload.commitment && !payload.from) return null\n\n // Validate operator match (same as x402)\n const operator = payload.operator ?? payload.to\n if (operator && operator.toLowerCase() !== x402Config.operatorAddress.toLowerCase()) return null\n\n // Validate bigint fields if present\n if (payload.amount) BigInt(payload.amount)\n if (payload.nonce) BigInt(payload.nonce)\n\n return payload.commitment ?? payload.from ?? null\n } catch {\n return null\n }\n}\n\n/**\n * Default API key verifier — accepts any `sk_agent_*` key (demo mode).\n * Override in GatewayConfig.verifyApiKey for production.\n */\nexport async function defaultVerifyApiKey(\n authHeader: string,\n): Promise<ApiKeyInfo | null> {\n if (!authHeader.startsWith('Bearer sk_agent_')) return null\n const key = authHeader.slice(7)\n return {\n keyId: key.slice(0, 16),\n consumerId: `apikey:${key.slice(0, 16)}`,\n }\n}\n","import type { ChatMessage } from './types'\n\n// --- Injection detection patterns ---\n\nconst INJECTION_PATTERNS = [\n // Direct instruction override\n /ignore\\s+(all\\s+)?(previous|prior|above|earlier)\\s+(instructions?|prompts?|rules?|directives?)/i,\n /disregard\\s+(all\\s+)?(previous|prior|system)/i,\n /forget\\s+(everything|all|your)\\s+(previous|instructions?|training)/i,\n // Role assumption\n /you\\s+are\\s+now\\s+(a|an|the)\\s+/i,\n /pretend\\s+(you\\s+are|to\\s+be)\\s+/i,\n /act\\s+as\\s+(if\\s+you\\s+are|a|an|the)\\s+/i,\n /new\\s+instructions?:/i,\n /\\[system\\]/i,\n /\\[INST\\]/i,\n // Prompt extraction\n /what\\s+(is|are)\\s+your\\s+(system\\s+)?(prompt|instructions?|rules?|directives?)/i,\n /repeat\\s+(your|the)\\s+(system\\s+)?(prompt|instructions?)/i,\n /output\\s+(your|the)\\s+((system|initial|original|first|full|real|hidden|secret|raw|exact)\\s+)?(prompt|instructions?)/i,\n /show\\s+(me\\s+)?(your|the)\\s+((system|hidden|secret|initial|original)\\s+)?(prompt|instructions?|message|directives?)/i,\n /tell\\s+me\\s+(your|the)\\s+(system\\s+)?(prompt|instructions?|rules?)/i,\n // Developer/debug/admin/jailbreak mode override\n /(developer|debug|admin|god|sudo|jailbreak|unrestricted|maintenance)\\s+mode/i,\n // Safety/safeguard disablement\n /(disable|bypass|override|remove|turn\\s+off)\\s+(your\\s+|the\\s+)?(safety|safeguards?|guardrails?|filters?|restrictions?|rules?|limitations?|content\\s+policy)/i,\n // Role reversal\n /(from\\s+now\\s+on|starting\\s+now|now)\\s+you('re|\\s+are)\\s+(the\\s+)?(user|human|customer|assistant)/i,\n /i('m|\\s+am)\\s+(the\\s+)?(assistant|ai|model|llm|bot)/i,\n // Tool-call / JSON-shaped spoofs\n /\"(?:tool|function|action|call)\"\\s*:\\s*\"[^\"]*(exfiltrate|leak|reveal|dump|extract|steal)[^\"]*\"/i,\n /exfiltrate[_\\s]*(system|prompt|secret|vault|config|data)/i,\n // Data exfiltration\n /read\\s+(the\\s+)?(vault|workspace|config|secret|\\.env)/i,\n /cat\\s+\\/home\\/agent\\/(vault|config|\\.env|secrets?)/i,\n /list\\s+(all\\s+)?(vault|workspace|secret)\\s+(files?|contents?|data)/i,\n // i18n — Spanish\n /ignor[ae]\\s+(todas?\\s+)?(las\\s+)?(instrucciones?|indicaciones?|órdenes?)\\s+(previas?|anteriores?)/i,\n /olvid[ae]\\s+(todas?\\s+|tus\\s+)?(instrucciones?|indicaciones?)/i,\n /(ahora\\s+)?(tú\\s+)?eres\\s+(ahora\\s+)?(un|una|el|la)\\s+/i,\n /finge\\s+(que\\s+eres|ser)\\s+/i,\n // i18n — French\n /ignor(ez|e|es)\\s+(toutes?\\s+)?(les\\s+)?(instructions?|consignes?|directives?)\\s+(précédentes?|antérieures?)/i,\n /oublie(z|s)?\\s+(toutes?\\s+|vos\\s+|tes\\s+)?(instructions?|consignes?)/i,\n /(vous\\s+êtes|tu\\s+es)\\s+(maintenant|désormais)\\s+(un|une)\\s+/i,\n /prétend(ez|s)?\\s+(être|que\\s+vous\\s+êtes|que\\s+tu\\s+es)\\s+/i,\n // i18n — German\n /ignorier(e|en|t)\\s+(alle\\s+)?(vorherigen?|vorigen?|bisherigen?)\\s+(anweisungen|anordnungen|vorgaben)/i,\n /vergiss\\s+(alle\\s+|deine\\s+)?(anweisungen|vorgaben|regeln)/i,\n /du\\s+bist\\s+(jetzt|nun)\\s+(ein|eine)\\s+/i,\n /tu\\s+so\\s+als\\s+(wärst\\s+du|ob\\s+du)\\s+/i,\n]\n\n// Cyrillic + Greek confusables that look identical to Latin letters but\n// NFKC does NOT collapse (they are distinct characters in Unicode, not\n// compatibility equivalents). A payload like `ignоre аll previоus\n// instructiоns` (o's are Cyrillic U+043E) evades the pattern set\n// entirely because `ignore` written with Cyrillic chars never matches\n// the Latin `[o]` in our regexes even after NFKC.\n//\n// Mapping derived from Unicode's confusables.txt, intentionally limited\n// to letters that routinely appear in English prompt-injection payloads.\n// Not a full confusable table — adding every entry would risk mangling\n// legitimate Russian/Greek content that happens to contain these\n// letters. Our INJECTION_PATTERNS and redaction keywords are English\n// ASCII, so collapsing to ASCII only touches payloads impersonating\n// those tokens.\nconst HOMOGLYPHS: Record<string, string> = {\n 'а': 'a', 'е': 'e', 'о': 'o', 'р': 'p',\n 'с': 'c', 'у': 'y', 'х': 'x', 'і': 'i',\n 'ј': 'j', 'ѕ': 's', 'һ': 'h',\n 'А': 'A', 'В': 'B', 'Е': 'E', 'К': 'K',\n 'М': 'M', 'Н': 'H', 'О': 'O', 'Р': 'P',\n 'С': 'C', 'Т': 'T', 'Х': 'X', 'І': 'I',\n 'Ј': 'J', 'Ѕ': 'S',\n 'α': 'a', 'ε': 'e', 'ο': 'o', 'ρ': 'p',\n 'υ': 'y', 'ν': 'v', 'ι': 'i', 'τ': 't',\n 'κ': 'k', 'χ': 'x', 'η': 'n', 'μ': 'u',\n 'Α': 'A', 'Β': 'B', 'Ε': 'E', 'Ζ': 'Z',\n 'Η': 'H', 'Ι': 'I', 'Κ': 'K', 'Μ': 'M',\n 'Ν': 'N', 'Ο': 'O', 'Ρ': 'P', 'Τ': 'T',\n 'Χ': 'X', 'Υ': 'Y',\n}\n\nfunction collapseHomoglyphs(text: string): string {\n let out = ''\n for (const ch of text) out += HOMOGLYPHS[ch] ?? ch\n return out\n}\n\n// Unicode normalization for REDACTION.\n//\n// Strip zero-widths entirely (no space substitute) so cross-character\n// elisions like \"v​aul​t/secret\" → \"vault/secret\" are matched\n// by the redaction keyword pass. NFKC collapses compatibility chars\n// (fullwidth `ignore` → `ignore`). collapseHomoglyphs catches the\n// Cyrillic/Greek lookalikes NFKC leaves alone.\nfunction normalizeUnicode(text: string): string {\n return collapseHomoglyphs(\n text.replace(/[\\u200B-\\u200F\\u2028-\\u202F\\u2060\\uFEFF]/g, '').normalize('NFKC'),\n )\n}\n\n// Unicode normalization for DETECTION.\n//\n// Two adversarial concerns diverge from redaction:\n//\n// 1. Between-word zero-widths (\"ignore​all​previous\") — the\n// attacker separates tokens with ZWSP so \\s+-anchored regexes do\n// not fire. Strip-then-detect glues the tokens into one\n// unmatchable word; replace ZWSP with a real SPACE instead so\n// /ignore\\s+all\\s+previous/ fires.\n// 2. Punctuation noise (\"Ignore,,, all...previous!!!\") — collapse\n// punctuation runs to a single space. Preserve `[` `]` because\n// literal `[system]` / `[INST]` are first-class injection signals\n// we match directly.\nfunction normalizeForDetection(text: string): string {\n const spaced = text\n .replace(/[\\u200B\\u200C\\uFEFF]/g, ' ')\n .replace(/[\\u200D\\u200E\\u200F\\u2028-\\u202F\\u2060]/g, '')\n .normalize('NFKC')\n return collapseHomoglyphs(spaced)\n .replace(/[(){}<>,.!?:;\"`~]+/g, ' ')\n .replace(/\\s+/g, ' ')\n}\n\n// Third pass: also collapse square brackets. Literal `[system]` /\n// `[INST]` patterns are matched against the other two passes (which\n// preserve brackets); this pass catches the OPPOSITE evasion where\n// the attacker wraps tokens in brackets to split them for \\s+-anchored\n// regexes — e.g. `(ignore) [all] {previous} <instructions>` should\n// still match the baseline pattern.\nfunction normalizeForDetectionNoBrackets(text: string): string {\n return normalizeForDetection(text)\n .replace(/[\\[\\]]+/g, ' ')\n .replace(/\\s+/g, ' ')\n}\n\n/**\n * Detect prompt injection attempts.\n * Returns array of matched pattern descriptions, empty if clean.\n *\n * Patterns run against TWO normalizations so both within-word and\n * between-word zero-width evasions are caught, and so `[system]` /\n * `[INST]` literals are not lost to punctuation-collapse:\n * - normalizeUnicode — strips zero-widths, preserves brackets\n * - normalizeForDetection — spaces zero-widths, collapses punct\n */\nexport function detectInjection(content: string): string[] {\n const stripNorm = normalizeUnicode(content)\n const detectNorm = normalizeForDetection(content)\n const noBracketNorm = normalizeForDetectionNoBrackets(content)\n const matches: string[] = []\n\n for (const pattern of INJECTION_PATTERNS) {\n if (pattern.test(stripNorm) || pattern.test(detectNorm) || pattern.test(noBracketNorm)) {\n matches.push(pattern.source.slice(0, 60))\n }\n }\n\n // Check for base64-encoded injection attempts in either normalization.\n for (const norm of [stripNorm, detectNorm]) {\n const b64Matches = norm.match(/[A-Za-z0-9+/]{40,}={0,2}/g)\n if (!b64Matches) continue\n for (const b64 of b64Matches) {\n try {\n const decoded = atob(b64)\n if (INJECTION_PATTERNS.some(p => p.test(decoded))) {\n if (!matches.includes('base64-encoded injection')) {\n matches.push('base64-encoded injection')\n }\n }\n } catch { /* not valid b64 */ }\n }\n }\n\n return matches\n}\n\n/**\n * Security boundary — filter consumer messages before forwarding to agent.\n *\n * Defense in depth:\n * 1. Strip system messages (consumers cannot set system prompt)\n * 2. Normalize Unicode (collapse homoglyphs, remove zero-width chars)\n * 3. Detect injection patterns (instruction override, prompt extraction, data exfil)\n * 4. Redact sensitive keywords\n * 5. Cap message length\n *\n * Returns filtered messages and any injection warnings detected.\n */\nexport function filterConsumerMessages(\n messages: ChatMessage[],\n maxLength = 8000,\n): ChatMessage[] {\n return messages\n .filter((m) => m.role !== 'system')\n .map((m) => {\n const normalized = normalizeUnicode(m.content)\n const redacted = normalized\n .replace(/\\b(vault|workspace|owner|admin|secret|\\.env|config\\.json)[\\s/:][^\\s]*/gi, '[REDACTED]')\n .slice(0, maxLength)\n return { role: m.role, content: redacted }\n })\n}\n\n/**\n * Filter consumer messages with injection detection.\n * Returns { messages, injectionWarnings }.\n * If injectionWarnings is non-empty, the gateway should log and optionally reject.\n */\nexport function filterConsumerMessagesStrict(\n messages: ChatMessage[],\n maxLength = 8000,\n): { messages: ChatMessage[]; injectionWarnings: string[] } {\n const filtered = filterConsumerMessages(messages, maxLength)\n const allContent = filtered.map(m => m.content).join(' ')\n const injectionWarnings = detectInjection(allContent)\n return { messages: filtered, injectionWarnings }\n}\n\n/**\n * Redact system prompt content from agent output.\n * Prevents the agent from leaking its own instructions in responses.\n *\n * Strategy: if any chunk of the system prompt appears verbatim (>40 chars)\n * in the output, replace it with [REDACTED].\n */\nexport function redactSystemPromptFromOutput(\n output: string,\n systemPrompt: string | undefined,\n): string {\n if (!systemPrompt || systemPrompt.length < 40) return output\n\n // Split system prompt into meaningful chunks (sentences or lines)\n const chunks = systemPrompt\n .split(/[.\\n]/)\n .map(s => s.trim())\n .filter(s => s.length >= 40)\n\n let redacted = output\n for (const chunk of chunks) {\n // Case-insensitive substring match\n const idx = redacted.toLowerCase().indexOf(chunk.toLowerCase())\n if (idx >= 0) {\n redacted = redacted.slice(0, idx) + '[REDACTED — system instructions]' + redacted.slice(idx + chunk.length)\n }\n }\n\n return redacted\n}\n","/**\n * Observability hook surface.\n *\n * Consumers implement GatewayObserver to wire the gateway into their existing\n * telemetry stack (Langfuse, OTEL, structured logs, Prometheus, etc.) without\n * the gateway itself depending on any of those libraries.\n *\n * Every event carries a requestId so downstream metrics can correlate the\n * payment verification, sandbox execution, and settlement for one request.\n * When no observer is configured, the gateway stays silent.\n */\n\nimport type { PaymentMethod, GatewayUsageEvent } from './types'\n\nexport interface RequestContext {\n requestId: string\n agentSlug: string\n startMs: number\n}\n\nexport interface AuthFailureReason {\n method: 'x402' | 'mpp' | 'apikey' | 'none'\n code: string\n httpStatus: number\n}\n\nexport interface GatewayObserver {\n /** Called at the start of every chat completions POST. */\n onRequestStart?: (ctx: RequestContext) => void | Promise<void>\n\n /** Called when a payment method has been successfully verified. */\n onPaymentVerified?: (ctx: RequestContext, info: {\n method: PaymentMethod\n consumerId: string\n keyId?: string\n }) => void | Promise<void>\n\n /** Called when auth fails — every branch. */\n onAuthFailure?: (ctx: RequestContext, reason: AuthFailureReason) => void | Promise<void>\n\n /** Called when a consumer hits the rate limit. */\n onRateLimited?: (ctx: RequestContext, info: {\n consumerId: string\n retryAfterSeconds: number\n }) => void | Promise<void>\n\n /** Called when the request body exceeds the 64KB limit. */\n onBodyTooLarge?: (ctx: RequestContext, contentLength: number) => void | Promise<void>\n\n /**\n * Called when prompt-injection patterns are detected.\n * `blocked` is true when blockInjection config is on and the request was\n * rejected; false when the patterns were logged but the request proceeded.\n */\n onInjectionDetected?: (ctx: RequestContext, info: {\n consumerId: string\n patterns: string[]\n blocked: boolean\n }) => void | Promise<void>\n\n /** Called after a successful stream completes and recordUsage has fired. */\n onRequestComplete?: (ctx: RequestContext, usage: GatewayUsageEvent) => void | Promise<void>\n\n /** Called when the sandbox throws. The error message is pre-scrubbed. */\n onStreamError?: (ctx: RequestContext, info: {\n consumerId: string\n errorMessage: string\n }) => void | Promise<void>\n\n /** Called when settlement fails. Payment already occurred; this is async bookkeeping. */\n onSettlementError?: (ctx: RequestContext, info: {\n consumerId: string\n method: PaymentMethod\n errorMessage: string\n }) => void | Promise<void>\n}\n\n// ---------------------------------------------------------------------------\n// Convenience implementations\n// ---------------------------------------------------------------------------\n\n/**\n * Structured-log observer. Emits one JSON line per event on the `log` function.\n * Default sink: console.log. Production consumers usually pipe their own\n * structured logger (pino, winston, the cf Logs binding).\n *\n * Usage:\n * new ConsoleObserver(({ level, event, ...rest }) => logger.info({ event, ...rest }))\n */\nexport class ConsoleObserver implements GatewayObserver {\n constructor(\n private readonly log: (entry: Record<string, unknown>) => void = (e) => console.log(JSON.stringify(e)),\n ) {}\n\n private emit(level: 'info' | 'warn' | 'error', event: string, ctx: RequestContext, rest: Record<string, unknown> = {}) {\n this.log({\n level,\n event,\n time: new Date().toISOString(),\n requestId: ctx.requestId,\n agentSlug: ctx.agentSlug,\n durationMs: Date.now() - ctx.startMs,\n ...rest,\n })\n }\n\n onRequestStart(ctx: RequestContext) { this.emit('info', 'gateway.request.start', ctx) }\n onPaymentVerified(ctx: RequestContext, info: { method: PaymentMethod; consumerId: string; keyId?: string }) {\n this.emit('info', 'gateway.payment.verified', ctx, info)\n }\n onAuthFailure(ctx: RequestContext, reason: AuthFailureReason) {\n this.emit('warn', 'gateway.auth.failure', ctx, reason as unknown as Record<string, unknown>)\n }\n onRateLimited(ctx: RequestContext, info: { consumerId: string; retryAfterSeconds: number }) {\n this.emit('warn', 'gateway.rate_limit', ctx, info)\n }\n onBodyTooLarge(ctx: RequestContext, contentLength: number) {\n this.emit('warn', 'gateway.body_too_large', ctx, { contentLength })\n }\n onInjectionDetected(ctx: RequestContext, info: { consumerId: string; patterns: string[]; blocked: boolean }) {\n this.emit('warn', 'gateway.injection', ctx, info)\n }\n onRequestComplete(ctx: RequestContext, usage: GatewayUsageEvent) {\n this.emit('info', 'gateway.request.complete', ctx, usage as unknown as Record<string, unknown>)\n }\n onStreamError(ctx: RequestContext, info: { consumerId: string; errorMessage: string }) {\n this.emit('error', 'gateway.stream.error', ctx, info)\n }\n onSettlementError(ctx: RequestContext, info: { consumerId: string; method: PaymentMethod; errorMessage: string }) {\n this.emit('error', 'gateway.settlement.error', ctx, info)\n }\n}\n\n/**\n * Compose multiple observers into one. Errors in any individual observer\n * don't break the others (fire-and-forget telemetry).\n */\nexport class CompositeObserver implements GatewayObserver {\n constructor(private readonly observers: GatewayObserver[]) {}\n\n private async fanOut<K extends keyof GatewayObserver>(event: K, ...args: unknown[]): Promise<void> {\n for (const obs of this.observers) {\n const fn = obs[event] as ((...a: unknown[]) => void | Promise<void>) | undefined\n if (!fn) continue\n try {\n await fn.apply(obs, args)\n } catch (err) {\n console.warn(`[agent-gateway] observer ${event} threw:`, err instanceof Error ? err.message : err)\n }\n }\n }\n\n onRequestStart = (ctx: RequestContext) => this.fanOut('onRequestStart', ctx)\n onPaymentVerified = (ctx: RequestContext, info: Parameters<Required<GatewayObserver>['onPaymentVerified']>[1]) =>\n this.fanOut('onPaymentVerified', ctx, info)\n onAuthFailure = (ctx: RequestContext, reason: AuthFailureReason) =>\n this.fanOut('onAuthFailure', ctx, reason)\n onRateLimited = (ctx: RequestContext, info: Parameters<Required<GatewayObserver>['onRateLimited']>[1]) =>\n this.fanOut('onRateLimited', ctx, info)\n onBodyTooLarge = (ctx: RequestContext, contentLength: number) =>\n this.fanOut('onBodyTooLarge', ctx, contentLength)\n onInjectionDetected = (ctx: RequestContext, info: Parameters<Required<GatewayObserver>['onInjectionDetected']>[1]) =>\n this.fanOut('onInjectionDetected', ctx, info)\n onRequestComplete = (ctx: RequestContext, usage: GatewayUsageEvent) =>\n this.fanOut('onRequestComplete', ctx, usage)\n onStreamError = (ctx: RequestContext, info: Parameters<Required<GatewayObserver>['onStreamError']>[1]) =>\n this.fanOut('onStreamError', ctx, info)\n onSettlementError = (ctx: RequestContext, info: Parameters<Required<GatewayObserver>['onSettlementError']>[1]) =>\n this.fanOut('onSettlementError', ctx, info)\n}\n\n/**\n * Generate a request-id. Crypto-random 16 bytes, hex-encoded with an `req_` prefix.\n * Works in Workers, Node, and browsers — all have globalThis.crypto.\n */\nexport function generateRequestId(): string {\n const bytes = new Uint8Array(16)\n globalThis.crypto.getRandomValues(bytes)\n const hex = Array.from(bytes).map((b) => b.toString(16).padStart(2, '0')).join('')\n return `req_${hex}`\n}\n"],"mappings":"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","names":[]}