{"version":3,"sources":["../src/agent/guardrails/content-filter.ts","../src/agent/guardrails/pii.ts","../src/agent/guardrails/topic.ts","../src/agent/guardrails/token-budget.ts","../src/agent/guardrails/rate-limit.ts"],"names":[],"mappings":"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","file":"chunk-VCVDUJHU.mjs","sourcesContent":["/**\n * Content Filter Guardrail\n *\n * Detects harmful content categories (AWS Bedrock pattern).\n */\n\nimport type {\n  Guardrail,\n  GuardrailResult,\n  GuardrailContext,\n} from '../core/types';\n\nexport interface ContentFilterConfig {\n  /** Categories to filter */\n  categories?: ContentCategory[];\n  /** Custom blocked patterns (regex strings) */\n  blockedPatterns?: string[];\n  /** Threshold for detection (0-1, default 0.5) */\n  threshold?: number;\n  /** Whether to trigger tripwire on detection */\n  tripwire?: boolean;\n}\n\nexport type ContentCategory =\n  | 'hate'\n  | 'violence'\n  | 'sexual'\n  | 'self_harm'\n  | 'dangerous'\n  | 'illegal';\n\nconst DEFAULT_CATEGORIES: ContentCategory[] = [\n  'hate',\n  'violence',\n  'sexual',\n  'self_harm',\n  'dangerous',\n  'illegal',\n];\n\n// Pattern-based detection (lightweight, runs on-device)\nconst CATEGORY_PATTERNS: Record<ContentCategory, RegExp[]> = {\n  hate: [\n    /\\b(hate\\s+(?:speech|crime))\\b/i,\n    /\\b(racial\\s+slur|ethnic\\s+cleansing)\\b/i,\n  ],\n  violence: [\n    /\\b(how\\s+to\\s+(?:kill|murder|assassinate|bomb|poison))\\b/i,\n    /\\b(mass\\s+(?:shooting|murder|casualty))\\b/i,\n  ],\n  sexual: [/\\b(explicit\\s+(?:sexual|pornographic))\\b/i],\n  self_harm: [\n    /\\b(how\\s+to\\s+(?:commit\\s+suicide|self[\\s-]harm|hurt\\s+(?:myself|yourself)))\\b/i,\n  ],\n  dangerous: [\n    /\\b(how\\s+to\\s+(?:make|build|create)\\s+(?:a\\s+)?(?:bomb|explosive|weapon|poison))\\b/i,\n  ],\n  illegal: [/\\b(how\\s+to\\s+(?:hack|steal|forge|counterfeit|launder))\\b/i],\n};\n\n/**\n * Create a content filter guardrail.\n */\nexport function createContentFilterGuardrail(\n  config?: ContentFilterConfig\n): Guardrail {\n  const categories = config?.categories ?? DEFAULT_CATEGORIES;\n  const blockedPatterns = (config?.blockedPatterns ?? []).map(\n    (p) => new RegExp(p, 'i')\n  );\n  const tripwire = config?.tripwire ?? false;\n\n  return {\n    name: 'content_filter',\n    tripwire,\n    validate: async (\n      input: unknown,\n      _ctx: GuardrailContext\n    ): Promise<GuardrailResult> => {\n      const text = String(input);\n      const detectedCategories: ContentCategory[] = [];\n\n      // Check category patterns\n      for (const category of categories) {\n        const patterns = CATEGORY_PATTERNS[category] ?? [];\n        for (const pattern of patterns) {\n          if (pattern.test(text)) {\n            detectedCategories.push(category);\n            break;\n          }\n        }\n      }\n\n      // Check custom blocked patterns\n      const blockedMatches: string[] = [];\n      for (const pattern of blockedPatterns) {\n        if (pattern.test(text)) {\n          blockedMatches.push(pattern.source);\n        }\n      }\n\n      const passed =\n        detectedCategories.length === 0 && blockedMatches.length === 0;\n\n      return {\n        passed,\n        tripwireTriggered: !passed && tripwire,\n        info: {\n          detectedCategories,\n          blockedMatches,\n        },\n      };\n    },\n  };\n}\n","/**\n * PII Detection Guardrail\n *\n * Detects and optionally redacts personally identifiable information.\n * AWS Bedrock pattern.\n */\n\nimport type {\n  Guardrail,\n  GuardrailResult,\n  GuardrailContext,\n} from '../core/types';\n\nexport interface PIIConfig {\n  /** PII types to detect */\n  types?: PIIType[];\n  /** Whether to redact (replace with placeholder) or just detect */\n  redact?: boolean;\n  /** Custom redaction placeholder (default: [REDACTED]) */\n  placeholder?: string;\n  /** Whether to trigger tripwire on detection */\n  tripwire?: boolean;\n}\n\nexport type PIIType =\n  | 'email'\n  | 'phone'\n  | 'ssn'\n  | 'credit_card'\n  | 'ip_address'\n  | 'address'\n  | 'name';\n\nconst PII_PATTERNS: Record<PIIType, RegExp> = {\n  email: /[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\\.[a-zA-Z]{2,}/g,\n  phone: /(?:\\+?1[-.\\s]?)?\\(?\\d{3}\\)?[-.\\s]?\\d{3}[-.\\s]?\\d{4}/g,\n  ssn: /\\b\\d{3}[-.\\s]?\\d{2}[-.\\s]?\\d{4}\\b/g,\n  credit_card: /\\b(?:\\d{4}[-.\\s]?){3}\\d{4}\\b/g,\n  ip_address: /\\b(?:\\d{1,3}\\.){3}\\d{1,3}\\b/g,\n  address:\n    /\\b\\d{1,5}\\s+\\w+(?:\\s+\\w+)*\\s+(?:St|Street|Ave|Avenue|Blvd|Boulevard|Dr|Drive|Ln|Lane|Rd|Road|Ct|Court|Pl|Place|Way)\\b/gi,\n  name: /\\b[A-Z][a-z]+\\s+[A-Z][a-z]+\\b/g,\n};\n\nconst DEFAULT_TYPES: PIIType[] = ['email', 'phone', 'ssn', 'credit_card'];\n\n/**\n * Create a PII detection guardrail.\n */\nexport function createPIIGuardrail(config?: PIIConfig): Guardrail {\n  const types = config?.types ?? DEFAULT_TYPES;\n  const redact = config?.redact ?? true;\n  const placeholder = config?.placeholder ?? '[REDACTED]';\n  const tripwire = config?.tripwire ?? false;\n\n  return {\n    name: 'pii_detection',\n    tripwire,\n    validate: async (\n      input: unknown,\n      _ctx: GuardrailContext\n    ): Promise<GuardrailResult> => {\n      const text = String(input);\n      const detections: Array<{ type: PIIType; match: string }> = [];\n\n      for (const type of types) {\n        const pattern = PII_PATTERNS[type];\n        // Reset regex state\n        pattern.lastIndex = 0;\n        let match: RegExpExecArray | null;\n        while ((match = pattern.exec(text)) !== null) {\n          detections.push({ type, match: match[0] });\n        }\n      }\n\n      if (detections.length === 0) {\n        return { passed: true };\n      }\n\n      // Redact if configured\n      let replacement: string | undefined;\n      if (redact) {\n        let redacted = text;\n        for (const type of types) {\n          const pattern = PII_PATTERNS[type];\n          pattern.lastIndex = 0;\n          redacted = redacted.replace(pattern, placeholder);\n        }\n        replacement = redacted;\n      }\n\n      return {\n        passed: false,\n        tripwireTriggered: tripwire,\n        info: {\n          detections: detections.map((d) => ({\n            type: d.type,\n            matchLength: d.match.length,\n          })),\n          detectionCount: detections.length,\n        },\n        replacement: redact ? replacement : undefined,\n      };\n    },\n  };\n}\n","/**\n * Topic Control Guardrail\n *\n * Blocks forbidden topics and enforces on-topic responses.\n * AWS Bedrock pattern.\n */\n\nimport type {\n  Guardrail,\n  GuardrailResult,\n  GuardrailContext,\n} from '../core/types';\n\nexport interface TopicConfig {\n  /** Forbidden topics (will block if detected) */\n  forbiddenTopics?: string[];\n  /** Allowed topics (will block if NOT detected — use for narrow scoping) */\n  allowedTopics?: string[];\n  /** Custom topic detection patterns */\n  patterns?: Record<string, RegExp[]>;\n  /** Whether to trigger tripwire */\n  tripwire?: boolean;\n}\n\n/**\n * Create a topic control guardrail.\n */\nexport function createTopicGuardrail(config: TopicConfig): Guardrail {\n  const tripwire = config.tripwire ?? false;\n\n  return {\n    name: 'topic_control',\n    tripwire,\n    validate: async (\n      input: unknown,\n      _ctx: GuardrailContext\n    ): Promise<GuardrailResult> => {\n      const text = String(input).toLowerCase();\n\n      // Check forbidden topics\n      if (config.forbiddenTopics) {\n        for (const topic of config.forbiddenTopics) {\n          const topicLower = topic.toLowerCase();\n          if (text.includes(topicLower)) {\n            // Check custom patterns if available\n            const patterns = config.patterns?.[topic];\n            if (patterns) {\n              for (const pattern of patterns) {\n                if (pattern.test(text)) {\n                  return {\n                    passed: false,\n                    tripwireTriggered: tripwire,\n                    info: { forbiddenTopic: topic, matched: true },\n                  };\n                }\n              }\n            } else {\n              return {\n                passed: false,\n                tripwireTriggered: tripwire,\n                info: { forbiddenTopic: topic, matched: true },\n              };\n            }\n          }\n        }\n      }\n\n      // Check allowed topics (if specified, must match at least one)\n      if (config.allowedTopics && config.allowedTopics.length > 0) {\n        const matchesAny = config.allowedTopics.some((topic) => {\n          const topicLower = topic.toLowerCase();\n          if (text.includes(topicLower)) return true;\n          const patterns = config.patterns?.[topic];\n          if (patterns) {\n            return patterns.some((p) => p.test(text));\n          }\n          return false;\n        });\n\n        if (!matchesAny) {\n          return {\n            passed: false,\n            tripwireTriggered: tripwire,\n            info: {\n              allowedTopics: config.allowedTopics,\n              matched: false,\n            },\n          };\n        }\n      }\n\n      return { passed: true };\n    },\n  };\n}\n","/**\n * Token Budget Guardrail\n *\n * Enforces spending limits on agent inference calls.\n * Wraps existing edgework TokenBudgetManager.\n */\n\nimport type {\n  Guardrail,\n  GuardrailResult,\n  GuardrailContext,\n} from '../core/types';\n\nexport interface TokenBudgetGuardrailConfig {\n  /** Max tokens per request */\n  maxTokensPerRequest?: number;\n  /** Max tokens per session */\n  maxTokensPerSession?: number;\n  /** Max cost per session (in abstract units) */\n  maxCostPerSession?: number;\n  /** Whether to trigger tripwire when budget exceeded */\n  tripwire?: boolean;\n}\n\ninterface BudgetState {\n  tokensUsed: number;\n  costUsed: number;\n  requestCount: number;\n}\n\n/**\n * Create a token budget guardrail.\n */\nexport function createTokenBudgetGuardrail(\n  config: TokenBudgetGuardrailConfig\n): Guardrail & { getUsage: () => BudgetState; reset: () => void } {\n  const state: BudgetState = {\n    tokensUsed: 0,\n    costUsed: 0,\n    requestCount: 0,\n  };\n\n  const tripwire = config.tripwire ?? false;\n\n  const guardrail: Guardrail & {\n    getUsage: () => BudgetState;\n    reset: () => void;\n  } = {\n    name: 'token_budget',\n    tripwire,\n    validate: async (\n      _input: unknown,\n      _ctx: GuardrailContext\n    ): Promise<GuardrailResult> => {\n      // Check session token limit\n      if (\n        config.maxTokensPerSession &&\n        state.tokensUsed >= config.maxTokensPerSession\n      ) {\n        return {\n          passed: false,\n          tripwireTriggered: tripwire,\n          info: {\n            reason: 'session_token_limit',\n            used: state.tokensUsed,\n            limit: config.maxTokensPerSession,\n          },\n        };\n      }\n\n      // Check session cost limit\n      if (\n        config.maxCostPerSession &&\n        state.costUsed >= config.maxCostPerSession\n      ) {\n        return {\n          passed: false,\n          tripwireTriggered: tripwire,\n          info: {\n            reason: 'session_cost_limit',\n            used: state.costUsed,\n            limit: config.maxCostPerSession,\n          },\n        };\n      }\n\n      state.requestCount++;\n      return { passed: true };\n    },\n\n    getUsage: () => ({ ...state }),\n\n    reset: () => {\n      state.tokensUsed = 0;\n      state.costUsed = 0;\n      state.requestCount = 0;\n    },\n  };\n\n  return guardrail;\n}\n\n/**\n * Record token usage against the budget.\n */\nexport function recordUsage(\n  guardrail: ReturnType<typeof createTokenBudgetGuardrail>,\n  tokens: number,\n  cost?: number\n): void {\n  const usage = guardrail.getUsage();\n  // We need to update internal state — create a helper that calls getUsage + reset + re-set\n  // Actually, we track via the closure in createTokenBudgetGuardrail\n  // The guardrail validate function checks the limits, and external callers record usage\n  // For this pattern, the caller should track via the returned getUsage method\n  void usage;\n  void tokens;\n  void cost;\n}\n","/**\n * Rate Limit Guardrail\n *\n * Enforces requests-per-minute and tokens-per-minute limits.\n */\n\nimport type {\n  Guardrail,\n  GuardrailResult,\n  GuardrailContext,\n} from '../core/types';\n\nexport interface RateLimitConfig {\n  /** Max requests per minute */\n  requestsPerMinute?: number;\n  /** Max tokens per minute */\n  tokensPerMinute?: number;\n  /** Window size in ms (default: 60000) */\n  windowMs?: number;\n  /** Whether to trigger tripwire when exceeded */\n  tripwire?: boolean;\n}\n\n/**\n * Create a rate limit guardrail.\n */\nexport function createRateLimitGuardrail(config: RateLimitConfig): Guardrail {\n  const windowMs = config.windowMs ?? 60_000;\n  const timestamps: number[] = [];\n  const tripwire = config.tripwire ?? false;\n\n  return {\n    name: 'rate_limit',\n    tripwire,\n    validate: async (\n      _input: unknown,\n      _ctx: GuardrailContext\n    ): Promise<GuardrailResult> => {\n      const now = Date.now();\n\n      // Prune old timestamps outside window\n      while (timestamps.length > 0 && timestamps[0] < now - windowMs) {\n        timestamps.shift();\n      }\n\n      // Check RPM\n      if (\n        config.requestsPerMinute &&\n        timestamps.length >= config.requestsPerMinute\n      ) {\n        const oldestInWindow = timestamps[0];\n        const retryAfterMs = oldestInWindow + windowMs - now;\n\n        return {\n          passed: false,\n          tripwireTriggered: tripwire,\n          info: {\n            reason: 'requests_per_minute_exceeded',\n            current: timestamps.length,\n            limit: config.requestsPerMinute,\n            retryAfterMs,\n          },\n        };\n      }\n\n      // Record this request\n      timestamps.push(now);\n\n      return { passed: true };\n    },\n  };\n}\n"]}