{"version":3,"file":"policy-types.mjs","names":[],"sources":["../../../src/services/policy-types.ts"],"sourcesContent":["/**\n * Policy Engine — Type Definitions\n *\n * Defines the type system for user-defined spending policies, approval rules,\n * and autonomy bounds. Policies are checked before every write-tool execution.\n *\n * Design principles:\n * - Natural language in, structured rules out. User never writes JSON.\n * - Multi-turn confirmation: agent proposes interpretation, user approves.\n *   Ambiguity → clarifying questions, never silent defaults.\n * - Full transparency: every policy shows the original NL description AND\n *   the exact structured rules being enforced. No hidden interpretation.\n * - Rules map cleanly to EIP-7710 caveats for future on-chain enforcement.\n * - Most restrictive rule wins: block > confirm > allow.\n */\n\n// ─── Policy ─────────────────────────────────────────────────────────────\n\nexport type PolicyStatus = 'draft' | 'active' | 'disabled';\n\nexport interface Policy {\n  id: string;\n  name: string;\n  description: string;          // original natural language, verbatim\n  rules: PolicyRule[];\n  scope: PolicyScope;\n  status: PolicyStatus;\n  /** Why it's a draft — what the agent still needs to clarify. */\n  pendingClarifications?: string[];\n  /** Timestamp when user explicitly confirmed the draft. Set by handleConfirm. */\n  confirmedAt?: number;\n  createdAt: number;\n  updatedAt: number;\n  userId: string;\n  /**\n   * On-chain delegation metadata (EIP-7710).\n   * Present when the policy has been compiled and signed as an on-chain delegation.\n   * Contains chain ID, delegation hash, addresses, and lifecycle status.\n   */\n  delegation?: DelegationInfo;\n}\n\n/**\n * On-chain delegation info stored with a policy.\n * Maps to DelegationMetadata in delegation-types.ts but uses plain types\n * here to avoid circular imports. The delegation service converts between them.\n */\nexport interface DelegationInfo {\n  /** Chain ID where the delegation is deployed. */\n  chainId: number;\n  /** Keccak256 hash of the delegation struct. */\n  hash: string;\n  /** DelegationManager contract address on this chain. */\n  delegationManager: string;\n  /** Current lifecycle status. */\n  status: 'unsigned' | 'signed' | 'active' | 'revoked' | 'expired';\n  /** The delegate address (agent). */\n  delegate: string;\n  /** The delegator address (user). */\n  delegator: string;\n  /** Salt used for uniqueness. */\n  salt: string;\n  /** ISO timestamp when the delegation was created. */\n  createdAt: string;\n  /** ISO timestamp when the delegation expires. Null = no time-bound. */\n  expiresAt?: string;\n  /** ISO timestamp when last status check was performed. */\n  lastCheckedAt?: string;\n  /** Policy rules that couldn't map to on-chain caveats (app-layer only). */\n  unmappedRules?: string[];\n}\n\n// ─── Scope ──────────────────────────────────────────────────────────────\n\nexport interface PolicyScope {\n  type: 'all_write' | 'tools' | 'categories';\n  tools?: string[];             // specific tool names\n  categories?: string[];        // category keys from TOOL_CATEGORIES\n}\n\n// ─── Rules ──────────────────────────────────────────────────────────────\n\nexport type PolicyRule =\n  | SpendingLimitRule\n  | RateLimitRule\n  | AllowlistRule\n  | BlocklistRule\n  | TimeWindowRule\n  | ApprovalThresholdRule\n  | MaxAmountRule\n  | Erc20LimitRule;\n\n/** Cumulative spending cap over a time period. */\nexport interface SpendingLimitRule {\n  type: 'spending_limit';\n  maxAmountUsd: number;\n  period: 'hourly' | 'daily' | 'weekly' | 'monthly';\n}\n\n/** Maximum number of tool calls in a time window. */\nexport interface RateLimitRule {\n  type: 'rate_limit';\n  maxCalls: number;\n  periodMs: number;\n}\n\n/** Only allow interactions with specific tokens/chains/addresses. */\nexport interface AllowlistRule {\n  type: 'allowlist';\n  field: 'tokens' | 'chains' | 'addresses' | 'contracts';\n  values: string[];             // lowercase for comparison\n}\n\n/** Block interactions with specific tokens/chains/addresses. */\nexport interface BlocklistRule {\n  type: 'blocklist';\n  field: 'tokens' | 'chains' | 'addresses' | 'contracts';\n  values: string[];             // lowercase for comparison\n}\n\n/** Restrict tool execution to certain hours / days. */\nexport interface TimeWindowRule {\n  type: 'time_window';\n  allowedHours?: { start: number; end: number };  // 0-23\n  allowedDays?: number[];                          // 0=Sun, 6=Sat\n  timezone?: string;                               // IANA, default UTC\n}\n\n/** Require human confirmation above a USD amount. */\nexport interface ApprovalThresholdRule {\n  type: 'approval_threshold';\n  amountUsd: number;\n}\n\n/** Cap cumulative ERC-20 transfer amount for a specific token. */\nexport interface Erc20LimitRule {\n  type: 'erc20_limit';\n  token: string;       // ERC-20 contract address (0x...)\n  maxAmount: string;    // Human-readable amount (e.g., \"100\" for 100 USDC)\n  decimals: number;     // Token decimals (6 for USDC, 18 for DAI)\n}\n\n/** Hard block on any single transaction above a USD amount. */\nexport interface MaxAmountRule {\n  type: 'max_amount';\n  maxAmountUsd: number;\n}\n\n// ─── Evaluation ─────────────────────────────────────────────────────────\n\nexport type PolicyAction = 'allow' | 'confirm' | 'block';\n\nexport interface PolicyDecision {\n  action: PolicyAction;\n  reason?: string;\n  policyId?: string;\n  policyName?: string;\n  ruleSummary?: string;         // human-readable rule that triggered decision\n}\n\n/** Context about the action being evaluated. */\nexport interface ActionContext {\n  toolName: string;\n  action?: string;              // sub-action (e.g., 'send', 'swap', 'supply')\n  amountUsd?: number;           // estimated USD value (undefined = unknown)\n  token?: string;               // token symbol or address\n  chain?: number;               // chain ID\n  toAddress?: string;           // destination address\n  userId: string;\n}\n\n// ─── Usage Tracking ─────────────────────────────────────────────────────\n\nexport interface UsageEntry {\n  timestamp: number;\n  toolName: string;\n  action?: string;\n  amountUsd?: number;\n}\n\nexport interface PolicyUsage {\n  policyId: string;\n  entries: UsageEntry[];\n}\n\n// ─── Display ────────────────────────────────────────────────────────────\n\n/**\n * Human-readable rendering of a policy for user verification.\n * Both the NL description and the structured interpretation are shown\n * so there is zero ambiguity about what's enforced.\n */\nexport interface PolicyDisplay {\n  name: string;\n  status: PolicyStatus;\n  /** The user's original words. */\n  description: string;\n  /** Plain-English rendering of each structured rule. */\n  ruleDescriptions: string[];\n  /** Which tools are affected. */\n  scopeDescription: string;\n  /** Current period usage vs limits (for spending/rate limits). */\n  usageSummary?: string;\n}\n\n// ─── Tool Categories ────────────────────────────────────────────────────\n\n/**\n * Maps category names to the write tools they contain.\n * Every tool in WRITE_TOOL_NAMES should be in exactly one category.\n */\nexport const TOOL_CATEGORIES: Record<string, string[]> = {\n  defi:          ['defi_swap', 'defi_lend', 'defi_stake', 'liquidity', 'yield',\n                  'bridge', 'permit2', 'approvals', 'wayfinder', 'molten'],\n  transfer:      ['transfer'],\n  fiat:          ['fiat_payment'],\n  bankr:         ['bankr_launch', 'bankr_automate', 'bankr_polymarket', 'bankr_leverage'],\n  social:        ['clawnx', 'farcaster'],\n  nft:           ['nft', 'airdrop'],\n  governance:    ['governance', 'safe'],\n  platform:      ['clawnch_launch', 'clawnch_fees', 'clawnch_info', 'hummingbot'],\n  orchestration: ['manage_orders', 'compound_action', 'crypto_workflow'],\n  privacy:       ['privacy'],\n  browser:       ['browser'],\n  wallet:        ['clawnchconnect'],\n};\n\n/** Human-readable category labels. */\nexport const CATEGORY_LABELS: Record<string, string> = {\n  defi:          'DeFi (swap, lend, stake, bridge, LP, yield, approvals)',\n  transfer:      'Transfers (send ETH/tokens)',\n  fiat:          'Fiat (on-ramp, off-ramp)',\n  bankr:         'Bankr (launch, automate, polymarket, leverage)',\n  social:        'Social (X/Twitter, Farcaster)',\n  nft:           'NFT & Airdrops',\n  governance:    'Governance & Safe multisig',\n  platform:      'Platform (launch, fees, hummingbot)',\n  orchestration: 'Orchestration (orders, compound actions, workflows)',\n  privacy:       'Privacy tools',\n  browser:       'Web3 browser',\n  wallet:        'Wallet connection',\n};\n\n/** Reverse lookup: tool name → category. */\nexport const TOOL_TO_CATEGORY: Record<string, string> = {};\nfor (const [category, tools] of Object.entries(TOOL_CATEGORIES)) {\n  for (const tool of tools) {\n    TOOL_TO_CATEGORY[tool] = category;\n  }\n}\n\n// ─── Period Helpers ─────────────────────────────────────────────────────\n\nconst PERIOD_MS: Record<string, number> = {\n  hourly:  60 * 60 * 1000,\n  daily:   24 * 60 * 60 * 1000,\n  weekly:  7 * 24 * 60 * 60 * 1000,\n  monthly: 30 * 24 * 60 * 60 * 1000,\n};\n\n/** Convert a named period to milliseconds. Throws on unknown periods. */\nexport function periodToMs(period: string): number {\n  const ms = PERIOD_MS[period];\n  if (ms == null) {\n    throw new Error(`Unknown period: \"${period}\". Valid: ${Object.keys(PERIOD_MS).join(', ')}.`);\n  }\n  return ms;\n}\n\n// ─── Rule Rendering ─────────────────────────────────────────────────────\n\n/** Render a single rule as unambiguous plain English. */\nexport function describeRule(rule: PolicyRule): string {\n  switch (rule.type) {\n    case 'spending_limit':\n      return `Spending limit: max $${rule.maxAmountUsd} USD per ${rule.period} period`;\n    case 'rate_limit': {\n      const hours = Math.round(rule.periodMs / 3_600_000);\n      const label = hours >= 24 ? `${Math.round(hours / 24)} day(s)` : `${hours} hour(s)`;\n      return `Rate limit: max ${rule.maxCalls} calls per ${label}`;\n    }\n    case 'allowlist':\n      return `Allowlist (${rule.field}): only ${rule.values.join(', ')}`;\n    case 'blocklist':\n      return `Blocklist (${rule.field}): never ${rule.values.join(', ')}`;\n    case 'time_window': {\n      const parts: string[] = [];\n      if (rule.allowedHours) parts.push(`hours ${rule.allowedHours.start}:00–${rule.allowedHours.end}:00`);\n      if (rule.allowedDays) {\n        const dayNames = ['Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat'];\n        parts.push(rule.allowedDays.map(d => dayNames[d] ?? d).join(', '));\n      }\n      const tz = rule.timezone ?? 'UTC';\n      return `Time window: allowed during ${parts.join(' on ')} (${tz})`;\n    }\n    case 'approval_threshold':\n      return `Require confirmation: for any action above $${rule.amountUsd} USD`;\n    case 'max_amount':\n      return `Hard limit: block any single action above $${rule.maxAmountUsd} USD`;\n    case 'erc20_limit': {\n      const KNOWN_TOKENS: Record<string, string> = {\n        '0x833589fcd6edb6e08f4c7c32d4f71b54bda02913': 'USDC',\n        '0xfde4c96c8593536e31f229ea8f37b2ada2699bb2': 'USDT',\n        '0x50c5725949a6f0c72e6c4a641f24049a917db0cb': 'DAI',\n        '0x4200000000000000000000000000000000000006': 'WETH',\n        '0xa1f72459dfa10bad200ac160ecd78c6b77a747be': 'CLAWNCH',\n      };\n      const symbol = KNOWN_TOKENS[rule.token.toLowerCase()] ?? `${rule.token.slice(0, 10)}...`;\n      return `ERC-20 limit: max ${rule.maxAmount} ${symbol}`;\n    }\n  }\n}\n\n/** Render a scope as plain English. */\nexport function describeScope(scope: PolicyScope): string {\n  switch (scope.type) {\n    case 'all_write':\n      return 'All write operations (any tool that sends transactions or modifies state)';\n    case 'tools':\n      return `Specific tools: ${(scope.tools ?? []).join(', ')}`;\n    case 'categories': {\n      const labels = (scope.categories ?? []).map(c => CATEGORY_LABELS[c] ?? c);\n      return `Categories: ${labels.join('; ')}`;\n    }\n  }\n}\n\n// ─── Policy Mode ────────────────────────────────────────────────────────\n//\n// Controls how policies are enforced:\n//   - 'delegation' (default): policies compile to EIP-7710 on-chain delegations.\n//     The agent redeems delegations for execution. Full on-chain enforcement.\n//   - 'simple': policies are natural-language only, enforced at app layer.\n//     No on-chain delegation, no signing, no smart account required.\n//     This is the V6 behavior — lightweight and works with any wallet.\n\nimport { existsSync, readFileSync, writeFileSync, mkdirSync, renameSync } from 'node:fs';\nimport { join } from 'node:path';\n\nexport type PolicyMode = 'delegation' | 'simple';\n\nconst POLICY_MODE_HOME = join(process.env.HOME ?? '/home/openclawnch', '.openclawnch');\nconst POLICY_MODE_FILE = join(POLICY_MODE_HOME, 'policy-mode.json');\n\n/** Default mode — delegation is the default for new installs. */\nconst DEFAULT_MODE: PolicyMode = 'delegation';\n\n/** In-memory cache. */\nlet _cachedMode: PolicyMode | null = null;\n\n/** Get the current policy enforcement mode. */\nexport function getPolicyMode(): PolicyMode {\n  if (_cachedMode) return _cachedMode;\n\n  try {\n    if (existsSync(POLICY_MODE_FILE)) {\n      const data = JSON.parse(readFileSync(POLICY_MODE_FILE, 'utf8'));\n      if (data.mode === 'delegation' || data.mode === 'simple') {\n        _cachedMode = data.mode;\n        return data.mode as PolicyMode;\n      }\n    }\n  } catch { /* fall through to default */ }\n\n  _cachedMode = DEFAULT_MODE;\n  return _cachedMode;\n}\n\n/** Set the policy enforcement mode. Persists to disk. */\nexport function setPolicyMode(mode: PolicyMode): void {\n  _cachedMode = mode;\n  if (!existsSync(POLICY_MODE_HOME)) {\n    mkdirSync(POLICY_MODE_HOME, { recursive: true });\n  }\n  const tmpPath = POLICY_MODE_FILE + '.tmp.' + Date.now();\n  writeFileSync(tmpPath, JSON.stringify({ mode }, null, 2), { mode: 0o600 });\n  renameSync(tmpPath, POLICY_MODE_FILE);\n}\n\n/** Check if delegation features are enabled (mode === 'delegation'). */\nexport function isDelegationMode(): boolean {\n  return getPolicyMode() === 'delegation';\n}\n\n/** Reset cached mode (for testing). */\nexport function resetPolicyMode(): void {\n  _cachedMode = null;\n}\n"],"mappings":"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"}