Ctrl K
ComunidadGuías…

src/types.ts

Rama main · réplica de lectura del repositorio original.

Archivo original ↗Preguntar a KiBot
// --- Mirrored from app/lib/types.ts ---

export type PiiCategory =
  | "email"
  | "phone"
  | "ssn"
  | "credit_card"
  | "ip_address"
  | "address"
  | "name"
  | "date_of_birth";

export type AiPiiCategory =
  | "person"
  | "location"
  | "organization";

export type ToxicityCategory = "toxic" | "severe_toxic" | "obscene" | "threat" | "insult" | "identity_hate";
export type DlpCategory = "source_code" | "credentials" | "internal_document" | "financial_data";
export type ComplianceDomain = "healthcare" | "financial" | "legal" | "insurance";

type ActionBase = {
  id: string;
  enabled: boolean;
};

export type RedactPiiAction = ActionBase & {
  type: "redact_pii";
  categories: PiiCategory[];
  replacement: "placeholder" | "hash" | "mask";
  mode: "redact" | "mask_and_restore";
  token_prefix: string;
  ttl_seconds: number;
};

export type FindReplaceAction = ActionBase & {
  type: "find_replace";
  find: string;
  replace: string;
  is_regex: boolean;
  case_sensitive: boolean;
  mode?: "redact" | "mask_and_restore";
  token_prefix?: string;
  ttl_seconds?: number;
};

export type TokenizeAction = ActionBase & {
  type: "tokenize";
  fields: string[];
  token_prefix: string;
  ttl_seconds: number;
};

export type RedactFieldAction = ActionBase & {
  type: "redact_field";
  fields: string[];
  replacement: string;
};

export type BlockRequestAction = ActionBase & {
  type: "block_request";
  status_code: number;
  message: string;
};

export type LogOnlyAction = ActionBase & {
  type: "log_only";
  severity: "info" | "warn" | "critical";
  label: string;
};

export type RestrictToolsAction = ActionBase & {
  type: "restrict_tools";
  // Tool names the agent is permitted to call. Anything not listed is disallowed.
  allowed_tools: string[];
  // Which enforcement points are active for this policy.
  enforce: { request: boolean; response: boolean };
  // What to do when a disallowed tool call appears in the response (buffered only).
  on_violation: "block" | "strip";
  block_status_code: number;
  block_message: string;
};

export type RouteModelAction = ActionBase & {
  type: "route_model";
  // Model to send upstream instead of the requested one. Must be served by the
  // same provider as the original request — the provider key chain is resolved
  // from the target URL before rules run.
  target_model: string;
  // "always": every matching request. "budget_over_pct": only once the most
  // constrained matching budget has spent at least `budget_pct` of its limit.
  when: "always" | "budget_over_pct";
  budget_pct: number;
};

export type AiDetectPiiAction = ActionBase & {
  type: "ai_detect_pii";
  categories: AiPiiCategory[];
  replacement: "placeholder" | "hash" | "mask";
  mode: "redact" | "mask_and_restore";
  token_prefix: string;
  ttl_seconds: number;
  /** Scan all conversation text instead of the first few thousand characters.
   * Slower: adds roughly 65ms per 1,000 characters scanned. */
  scan_full_body?: boolean;
};

export type AiDetectInjectionAction = ActionBase & {
  type: "ai_detect_injection";
  threshold: number; // 0-1, block if score >= threshold
  on_detect: "block" | "log";
  block_status_code: number;
  block_message: string;
};

export type AiDetectToxicityAction = ActionBase & {
  type: "ai_detect_toxicity";
  categories: ToxicityCategory[];
  threshold: number;
  on_detect: "block" | "log" | "redact";
  block_status_code: number;
  block_message: string;
};

export type AiDetectDlpAction = ActionBase & {
  type: "ai_detect_dlp";
  categories: DlpCategory[];
  threshold: number;
  on_detect: "block" | "log";
  block_status_code: number;
  block_message: string;
};

export type AiDetectComplianceAction = ActionBase & {
  type: "ai_detect_compliance";
  domains: ComplianceDomain[];
  threshold: number;
  on_detect: "block" | "log" | "flag";
  block_status_code: number;
  block_message: string;
};

export type RuleAction =
  | RedactPiiAction
  | FindReplaceAction
  | TokenizeAction
  | RedactFieldAction
  | BlockRequestAction
  | LogOnlyAction
  | AiDetectPiiAction
  | AiDetectInjectionAction
  | AiDetectToxicityAction
  | AiDetectDlpAction
  | AiDetectComplianceAction
  | RestrictToolsAction
  | RouteModelAction;

export type RuleCondition = {
  id: string;
  field: "header" | "model" | "body" | "url";
  operator: "contains" | "equals" | "matches" | "exists";
  value: string;
};

export type RuleConditionGroup = {
  id: string;
  logic: "and" | "or";
  conditions: RuleCondition[];
};

export type Rule = {
  id: string;
  user_id: string;
  team_id: string;
  name: string;
  description: string;
  enabled: boolean;
  apply_to: "input" | "output" | "both";
  sampling_rate: number;
  timeout_seconds: number;
  conditions: RuleConditionGroup[];
  match_all: boolean;
  actions: RuleAction[];
  created_at: string;
  updated_at: string;
};

export type TrafficLogEntry = {
  id?: string;
  user_id: string;
  team_id: string;
  api_key_id?: string | null;
  method: string;
  target_url: string;
  status_code: number;
  rules_applied: string[];
  duration_ms: number;
  request_headers: Record<string, string>;
  request_body: string;
  response_headers: Record<string, string>;
  response_body: string;
  prompt_tokens?: number | null;
  completion_tokens?: number | null;
  total_tokens?: number | null;
  cache_read_tokens?: number | null;
  cache_write_tokens?: number | null;
  model?: string | null;
  provider?: string | null;
  /** Model the caller asked for when a route_model action swapped it. */
  requested_model?: string | null;
  original_request_body?: string | null;
  request_body_r2_key?: string | null;
  response_body_r2_key?: string | null;
  original_request_body_r2_key?: string | null;
  trace_id?: string | null;
  parent_span_id?: string | null;
  label?: string | null;
  metadata?: Record<string, string> | null;
  seq?: number | null;
  session_id?: string | null;
  prompt_id?: string | null;
  prompt_version?: number | null;
  provider_key_id?: string | null;
  source?: "proxy" | "trace";
  created_at?: string;
};

// Row inserted into `tool_calls` when the proxy sees an assistant response
// emit a tool_use block. `status` starts as 'pending' and is updated by
// link_tool_call_results when the matching tool_result arrives.
export type ToolCallInsertRow = {
  id?: string;
  team_id: string;
  user_id: string;
  traffic_log_id: string;
  session_id: string | null;
  trace_id: string | null;
  provider_tool_call_id: string;
  tool_name: string;
  arguments: Record<string, unknown>;
  arguments_hash: string;
  status: "pending";
  model: string | null;
  provider: string | null;
};

// One link entry passed to link_tool_call_results.
export type ToolCallLink = {
  provider_tool_call_id: string;
  result: unknown;
  is_error: boolean;
  result_traffic_log_id: string;
};

export type EmbeddingLogEntry = {
  id?: string;
  team_id: string;
  trace_id?: string | null;
  provider_key_id?: string | null;
  byok: boolean;
  model: string;
  input_count: number;
  total_chars: number;
  dimensions?: number | null;
  redaction_strategy: "placeholder" | "hash" | "mask";
  redaction_categories: string[];
  redaction_count: number;
  redaction_source?: "regex" | "ai" | "both" | null;
  blocked: boolean;
  status_code: number;
  tokens?: number | null;
  duration_ms: number;
};

export type ApiSettings = {
  id: string;
  user_id: string;
  team_id: string;
  api_key: string;
  fallback_mode: "passthrough" | "error";
  zero_data_mode: boolean;
  created_at: string;
  updated_at: string;
};

// --- Prompt types ---

export type PromptMessage = {
  role: "system" | "user" | "assistant";
  content: string;
};

export type PromptVariable = {
  name: string;
  type: "string" | "number" | "boolean";
  required: boolean;
  default?: string;
};

export type PromptRecord = {
  id: string;
  team_id: string;
  slug: string;
  name: string;
  skip_rules: boolean;
  active_version: number | null;
};

export type PromptVersionRecord = {
  id: string;
  prompt_id: string;
  version: number | null;
  messages: PromptMessage[];
  variables: PromptVariable[] | null;
  published_at: string | null;
};

// --- Proxy-specific types ---

export type AuthInfo = {
  team_id: string;
  user_id: string;
  api_settings_id: string;
  fallback_mode: "passthrough" | "error";
  zero_data_mode: boolean;
  tier: string;
};

export type RequestContext = {
  requestId: string;
  auth: AuthInfo;
  method: string;
  targetUrl: string;
  headers: Record<string, string>;
  body: string;
  parsedBody: unknown;
  startedAt: number;
  traceId: string | null;
  /** Caller's span id from an incoming W3C traceparent header (only set when
   * traceId also came from traceparent). */
  parentSpanId: string | null;
  label: string | null;
  metadata: Record<string, string> | null;
  seq: number | null;
  sessionId: string | null;
  /** From X-Grepture-Token-Scope: makes mask-and-restore tokens deterministic
   * within this scope (see pii/token.ts). */
  tokenScope?: string | null;
  /** From X-Grepture-Token-Binding: scope — tokens minted by this request
   * restore only under the same tokenScope. */
  tokenScopeBound?: boolean;
  /** Highest spend ratio (0-100+) across budgets matching this request, or
   * null when no budget matches. Consumed by route_model. */
  budgetSpendPct: number | null;
  /** Original model when a route_model action rewrote it. */
  routedFrom: string | null;
  /** Which pipeline pass this context represents. Unset/"input" = request pass;
   * "output" = response pass (set by the handler on the response-oriented context).
   * Lets a single action behave differently per direction. */
  phase?: "input" | "output";
  /** When set, redaction actions push per-match details here for the debug pipeline view. */
  debugTrace?: DebugTrace | null;
};

/**
 * Per-request capture of every pipeline stage for the debug demo view.
 * Only populated when the caller opts in via X-Grepture-Debug header AND
 * the team is not in zero_data_mode. Persisted to `debug_traces` with a
 * 24h TTL, never to `traffic_logs`.
 */
export type DebugRedactionEntry = {
  source: "redact_pii" | "ai_detect_pii" | "tokenize" | "find_replace";
  category?: string;
  field?: string;
  original: string;
  replacement: string;
  mode: "redact" | "mask_and_restore";
};

export type DebugTrace = {
  // Stage 1: exactly what the caller sent.
  input_raw: string;
  // Stage 2: every redaction/token replacement performed on the input.
  redactions: DebugRedactionEntry[];
  // Stage 3: what we actually forwarded upstream (post-redaction body).
  upstream_request_body: string;
  upstream_target_url: string;
  // Stage 4: raw upstream response (pre-detokenize, pre-output-rules).
  upstream_response_body: string;
  upstream_status: number;
  // Stage 5: final body returned to the caller (post-detokenize).
  output_final: string;
  // Timing per stage, useful in the demo view.
  timings_ms: {
    upstream?: number;
    total?: number;
  };
  rules_applied: string[];
};

export type ActionResult = {
  blocked?: boolean;
  statusCode?: number;
  message?: string;
  tags?: Array<{ severity: string; label: string }>;
};

export type ProxyResult = {
  response: Response;
  rulesApplied: string[];
  tags: Array<{ severity: string; label: string }>;
};