{"version":3,"file":"delegation-executor.mjs","names":[],"sources":["../../../src/services/delegation-executor.ts"],"sourcesContent":["/**\n * Delegation Executor — Bridges the policy gate to on-chain delegation redemption.\n *\n * This is the \"last mile\" that connects the delegation infrastructure to the\n * actual tool execution path. When a write tool is about to execute:\n *\n *   1. Policy gate evaluates rules → allow/confirm/block\n *   2. If allowed AND delegation mode is active AND a matching delegation exists:\n *      → Route through redeemDelegation() (on-chain enforcement)\n *   3. If delegation redemption fails or no delegation exists:\n *      → Fall back to normal tool execution (ClawnchConnect approval)\n *\n * The executor does NOT replace the tool's execution — it intercepts the\n * transaction at the point where the tool would call wallet.sendTransaction().\n * For tools where we can extract the target/value/calldata, we redeem directly.\n * For tools where we can't, we fall through to normal execution.\n *\n * Design constraints:\n * - Must be non-breaking: if anything fails, fall back silently\n * - Must not modify tool signatures or return types\n * - Must work with all 3 wallet modes (private_key, walletconnect, bankr)\n * - Must record usage on successful redemption\n */\n\nimport type { ActionContext } from './policy-types.js';\nimport { isDelegationMode } from './policy-types.js';\nimport { getPolicyStore } from './policy-store.js';\nimport { canRedeem, redeemDelegation, getDelegatedPolicies } from './delegation-service.js';\n// detectAccountType removed — EOA check now uses getCode on the delegation's delegator address\nimport type { ExecutionAction } from './delegation-types.js';\nimport type { Address, Hex } from 'viem';\n\n// ─── Types ──────────────────────────────────────────────────────────────\n\nexport interface DelegationExecutionResult {\n  /** Whether the action was executed via delegation. */\n  executed: boolean;\n  /** Transaction hash from on-chain redemption. */\n  txHash?: string;\n  /** Chain ID where the redemption happened. */\n  chainId?: number;\n  /** Why delegation execution was skipped (for debugging). */\n  skipReason?: string;\n  /** Error message if delegation execution was attempted but failed. */\n  error?: string;\n}\n\n// ─── Tool-to-Action Extraction ──────────────────────────────────────────\n\n/**\n * Tools that support delegation execution.\n *\n * Each entry maps a tool name to an async function that extracts the\n * ExecutionAction (target, value, callData) from the tool's args.\n *\n * Supported:\n * - transfer: native ETH sends and ERC-20 transfers\n * - clawnchconnect: raw transaction sends (send_tx action)\n *\n * Tier 1 (simple extraction): transfer, clawnchconnect, approvals, permit2, nft\n * Tier 2 (known ABIs): defi_lend (borrow/withdraw), defi_stake, governance\n * Tier 3 (approval-gated): yield (withdraw only)\n *\n * Not extractable (calldata from external APIs/SDKs):\n * - defi_swap: DEX aggregator constructs calldata (0x/1inch/Paraswap)\n * - bridge: LI.FI aggregator constructs calldata\n * - liquidity: Uniswap SDK multicall with complex math\n * - privacy: ZK proof generation via Veil SDK\n * - bankr_*: natural-language prompt API, opaque\n * - farcaster/clawnx: API-only, no on-chain tx\n *\n * For unsupported tools, the delegation's on-chain caveats still enforce\n * limits when the wallet IS the delegator's smart account — the tool\n * submits the tx through the wallet, and the wallet's caveats are checked.\n */\n\n/** Context passed to extractors for API-based calldata resolution. */\ninterface ExtractorContext {\n  walletAddress?: Address;\n  delegatorAddress?: Address;\n  chainId?: number;\n}\n\ninterface ActionExtractor {\n  (args: Record<string, unknown>, ctx?: ExtractorContext): Promise<ExecutionAction | null>;\n}\n\n// ─── Redemption Rate Limiter ────────────────────────────────────────────\n// Prevents gas-burning loops when a malfunctioning agent repeatedly\n// attempts to redeem a delegation that reverts.\n// Max 3 attempts per policy per 60 seconds. Resets on success.\n\nconst RATE_LIMIT_WINDOW_MS = 60_000;\nconst RATE_LIMIT_MAX_ATTEMPTS = 3;\nconst _redemptionAttempts = new Map<string, number[]>();\n\n// H1: Key on (userId, toolName) composite to prevent bypass via multiple policies\nfunction rateLimitKey(userId: string, toolName: string): string {\n  return `${userId}:${toolName}`;\n}\n\nfunction isRateLimited(userId: string, toolName: string): boolean {\n  const key = rateLimitKey(userId, toolName);\n  const now = Date.now();\n  const attempts = _redemptionAttempts.get(key) ?? [];\n  const recent = attempts.filter(t => now - t < RATE_LIMIT_WINDOW_MS);\n  return recent.length >= RATE_LIMIT_MAX_ATTEMPTS;\n}\n\nfunction recordRedemptionAttempt(userId: string, toolName: string): void {\n  const key = rateLimitKey(userId, toolName);\n  const now = Date.now();\n  const attempts = _redemptionAttempts.get(key) ?? [];\n  const recent = attempts.filter(t => now - t < RATE_LIMIT_WINDOW_MS);\n  recent.push(now);\n  _redemptionAttempts.set(key, recent);\n}\n\n/** Clear rate limit for a user+tool (call on successful redemption). */\nexport function clearRateLimit(userId: string, toolName: string): void {\n  _redemptionAttempts.delete(rateLimitKey(userId, toolName));\n}\n\n// ─── Well-Known Token Decimals ──────────────────────────────────────────\n// Avoids async on-chain calls for common tokens. Unknown tokens fall back\n// to 18 decimals (standard ERC-20 default).\n\nconst WELL_KNOWN_DECIMALS: Record<string, number> = {\n  '0x833589fcd6edb6e08f4c7c32d4f71b54bda02913': 6,  // USDC (Base)\n  '0xfde4c96c8593536e31f229ea8f37b2ada2699bb2': 6,  // USDT (Base)\n  '0x50c5725949a6f0c72e6c4a641f24049a917db0cb': 18, // DAI (Base)\n  '0x4200000000000000000000000000000000000006': 18,   // WETH (Base)\n  '0xa1f72459dfa10bad200ac160ecd78c6b77a747be': 18,  // CLAWNCH (Base)\n};\n\nfunction getTokenDecimals(address: string): number {\n  return WELL_KNOWN_DECIMALS[address.toLowerCase()] ?? 18;\n}\n\n/**\n * Encode ERC-20 transfer(address,uint256) calldata.\n * Selector: 0xa9059cbb\n */\nfunction encodeErc20Transfer(to: Address, amount: bigint): Hex {\n  const selector = '0xa9059cbb';\n  const toParam = to.slice(2).toLowerCase().padStart(64, '0');\n  const amountParam = amount.toString(16).padStart(64, '0');\n  return `${selector}${toParam}${amountParam}` as Hex;\n}\n\n/**\n * Encode ERC-20 approve(address,uint256) calldata.\n * Selector: 0x095ea7b3\n */\nfunction encodeErc20Approve(spender: Address, amount: bigint): Hex {\n  const selector = '0x095ea7b3';\n  const spenderParam = spender.slice(2).toLowerCase().padStart(64, '0');\n  const amountParam = amount.toString(16).padStart(64, '0');\n  return `${selector}${spenderParam}${amountParam}` as Hex;\n}\n\n/**\n * Parse a human-readable token amount to wei/smallest unit.\n * e.g., \"100\" with 6 decimals → 100_000_000n\n */\nfunction parseTokenAmount(amount: string, decimals: number): bigint {\n  const num = parseFloat(amount);\n  if (isNaN(num) || num <= 0) return 0n;\n  // Use string math to avoid floating point precision loss\n  const [whole = '0', frac = ''] = amount.split('.');\n  const paddedFrac = (frac + '0'.repeat(decimals)).slice(0, decimals);\n  return BigInt(whole + paddedFrac);\n}\n\n// ─── Target Address Validation ──────────────────────────────────────────\n// API-sourced extractors (swap, bridge) must validate that the returned\n// target address matches a known router/bridge contract to prevent\n// arbitrary code execution if the API is compromised.\n\nconst KNOWN_SWAP_TARGETS = new Set([\n  '0xdef1c0ded9bec7f1a1670819833240f027b25eff', // 0x Exchange Proxy (multi-chain)\n  '0x1111111254eeb25477b68fb85ed929f73a960582', // 1inch Router V5\n  '0x111111125421ca6dc452d289314280a0f8842a65', // 1inch Router V6\n  '0xdef171fe48cf0115b1d80b88dc8eab59176fee57', // ParaSwap Augustus\n  '0x6131b5fae19ea4f9d964eac0408e4408b66337b5', // KyberSwap\n  '0x000000000022d473030f116ddee9f6b43ac78ba3', // Permit2 (AllowanceHolder)\n].map(a => a.toLowerCase()));\n\nconst KNOWN_BRIDGE_TARGETS = new Set([\n  '0x1231deb6f5749ef6ce6943a275a1d3e7486f4eae', // LI.FI Diamond\n  '0x341e94069f53234fE6DabeF707aD424830525715', // LI.FI Diamond V2\n].map(a => a.toLowerCase()));\n\nfunction isKnownSwapTarget(addr: string): boolean {\n  return KNOWN_SWAP_TARGETS.has(addr.toLowerCase());\n}\n\nfunction isKnownBridgeTarget(addr: string): boolean {\n  return KNOWN_BRIDGE_TARGETS.has(addr.toLowerCase());\n}\n\n/** Encode a signed int24 as a 32-byte ABI word (two's complement). */\nfunction toInt24Hex(val: number): string {\n  if (val >= 0) return val.toString(16).padStart(64, '0');\n  // Two's complement for negative values in uint256 space\n  const twos = BigInt(2) ** BigInt(256) + BigInt(val);\n  return twos.toString(16).padStart(64, '0');\n}\n\nconst SUPPORTED_EXTRACTORS: Record<string, ActionExtractor> = {\n  /**\n   * transfer tool — native ETH sends and ERC-20 transfers.\n   * Args: { action: 'send', to: address, amount: string, token?: string }\n   * token is a contract address (0x...) for ERC-20, absent/ETH for native.\n   */\n  transfer: async (args) => {\n    const action = args.action as string | undefined;\n    if (action !== 'send') return null;\n\n    const to = args.to as string | undefined;\n    const amount = args.amount as string | undefined;\n    const token = args.token as string | undefined;\n\n    if (!to || !amount) return null;\n    if (!/^0x[0-9a-fA-F]{40}$/.test(to)) return null;\n\n    const isErc20 = token && /^0x[0-9a-fA-F]{40}$/.test(token);\n    const isNative = !token || token.toUpperCase() === 'ETH' || token.toUpperCase() === 'NATIVE';\n\n    if (isErc20) {\n      // ERC-20 transfer: target = token contract, calldata = transfer(to, amount)\n      try {\n        const decimals = getTokenDecimals(token);\n        const amountWei = parseTokenAmount(amount, decimals);\n        if (amountWei <= 0n) return null;\n\n        return {\n          target: token as Address,\n          value: 0n,\n          callData: encodeErc20Transfer(to as Address, amountWei),\n        };\n      } catch {\n        return null;\n      }\n    }\n\n    if (isNative) {\n      // Native ETH transfer: target = recipient, value = amount in wei\n      try {\n        // C3: Use string-math, not floating point (avoids precision loss)\n        const weiValue = parseTokenAmount(amount, 18);\n        if (weiValue <= 0n) return null;\n\n        return {\n          target: to as Address,\n          value: weiValue,\n          callData: '0x' as Hex,\n        };\n      } catch {\n        return null;\n      }\n    }\n\n    return null;\n  },\n\n  /**\n   * clawnchconnect tool — raw transaction sends.\n   * Args: { action: 'send_tx', to: address, value?: string (ETH), data?: hex }\n   */\n  clawnchconnect: async (args) => {\n    const action = args.action as string | undefined;\n    if (action !== 'send_tx') return null;\n\n    const to = args.to as string | undefined;\n    if (!to || !/^0x[0-9a-fA-F]{40}$/.test(to)) return null;\n\n    const valueStr = args.value as string | undefined;\n    const data = args.data as string | undefined;\n\n    try {\n      let value = 0n;\n      if (valueStr) {\n        // C3: Use string-math parsing, not floating point (avoids precision loss)\n        value = parseTokenAmount(valueStr, 18);\n        if (value < 0n) value = 0n;\n      }\n\n      const callData = (data && data.startsWith('0x') ? data : '0x') as Hex;\n\n      return {\n        target: to as Address,\n        value,\n        callData,\n      };\n    } catch {\n      return null;\n    }\n  },\n\n  // ── Tier 1: Simple arg extraction ────────────────────────────────────\n\n  /**\n   * approvals tool — revoke ERC-20 approvals.\n   * Args: { action: 'revoke', token: address, spender: address }\n   */\n  approvals: async (args) => {\n    const action = args.action as string | undefined;\n    if (action !== 'revoke') return null;\n\n    const token = args.token as string | undefined;\n    const spender = args.spender as string | undefined;\n    if (!token || !spender) return null;\n    if (!/^0x[0-9a-fA-F]{40}$/.test(token) || !/^0x[0-9a-fA-F]{40}$/.test(spender)) return null;\n\n    return {\n      target: token as Address,\n      value: 0n,\n      callData: encodeErc20Approve(spender as Address, 0n),\n    };\n  },\n\n  /**\n   * permit2 tool — approve tokens to the Permit2 contract.\n   * Args: { action: 'approve', token: address }\n   */\n  permit2: async (args) => {\n    const action = args.action as string | undefined;\n    if (action !== 'approve') return null;\n\n    const token = args.token as string | undefined;\n    if (!token || !/^0x[0-9a-fA-F]{40}$/.test(token)) return null;\n\n    const PERMIT2 = '0x000000000022D473030F116dDEE9F6B43aC78BA3' as Address;\n    const MAX_UINT256 = (2n ** 256n) - 1n;\n\n    return {\n      target: token as Address,\n      value: 0n,\n      callData: encodeErc20Approve(PERMIT2, MAX_UINT256),\n    };\n  },\n\n  /**\n   * nft tool — transfer ERC-721 tokens.\n   * Args: { action: 'transfer', contract: address, token_id: string, to: address }\n   */\n  nft: async (args) => {\n    const action = args.action as string | undefined;\n    if (action !== 'transfer') return null;\n\n    const contract = args.contract as string | undefined;\n    const tokenId = args.token_id as string | undefined;\n    const to = args.to as string | undefined;\n    if (!contract || !tokenId || !to) return null;\n    if (!/^0x[0-9a-fA-F]{40}$/.test(contract) || !/^0x[0-9a-fA-F]{40}$/.test(to)) return null;\n\n    // ERC-721 transferFrom(address from, address to, uint256 tokenId)\n    // Selector: 0x23b872dd\n    // Note: uses transferFrom not safeTransferFrom to avoid callback complexity\n    const selector = '0x23b872dd';\n    const fromParam = '0'.repeat(64); // filled at execution time from delegator address\n    const toParam = to.slice(2).toLowerCase().padStart(64, '0');\n    const idParam = BigInt(tokenId).toString(16).padStart(64, '0');\n\n    return {\n      target: contract as Address,\n      value: 0n,\n      callData: `${selector}${fromParam}${toParam}${idParam}` as Hex,\n    };\n  },\n\n  // ── Tier 2: Known ABIs, deterministic args ───────────────────────────\n\n  /**\n   * defi_lend tool — Aave V3 supply/borrow/repay/withdraw.\n   * Args: { action: 'supply'|'borrow'|'repay'|'withdraw', asset: string, amount: string }\n   *\n   * supply/repay require the Aave Pool to have ERC-20 approval for the asset.\n   * If approval is missing, the on-chain tx reverts (caught by gas simulation\n   * with a clear error). Most DeFi users have existing or infinite approvals.\n   */\n  defi_lend: async (args) => {\n    const action = args.action as string | undefined;\n    if (action !== 'borrow' && action !== 'withdraw' && action !== 'supply' && action !== 'repay') return null;\n\n    const asset = args.asset as string | undefined;\n    const amount = args.amount as string | undefined;\n    if (!asset || !amount) return null;\n\n    // Resolve asset symbol to address (Base mainnet)\n    const AAVE_ASSETS: Record<string, { address: Address; decimals: number }> = {\n      'eth': { address: '0x4200000000000000000000000000000000000006' as Address, decimals: 18 },\n      'weth': { address: '0x4200000000000000000000000000000000000006' as Address, decimals: 18 },\n      'usdc': { address: '0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913' as Address, decimals: 6 },\n      'usdbc': { address: '0xd9aAEc86B65D86f6A7B5B1b0c42FFA531710b6CA' as Address, decimals: 6 },\n      'cbeth': { address: '0x2Ae3F1Ec7F1F5012CFEab0185bfc7aa3cf0DEc22' as Address, decimals: 18 },\n    };\n\n    const assetLower = asset.toLowerCase();\n    const assetInfo = AAVE_ASSETS[assetLower];\n    // If asset is already a 0x address, use it directly\n    const assetAddr = assetInfo?.address ?? ((/^0x[0-9a-fA-F]{40}$/.test(asset)) ? asset as Address : null);\n    if (!assetAddr) return null;\n\n    const decimals = assetInfo?.decimals ?? 18;\n    const amountWei = parseTokenAmount(amount, decimals);\n    if (amountWei <= 0n) return null;\n\n    const AAVE_POOL = '0xA238Dd80C259a72e81d7e4664a9801593F98d1c5' as Address; // Base\n\n    if (action === 'borrow') {\n      // borrow(address asset, uint256 amount, uint256 interestRateMode, uint16 referralCode, address onBehalfOf)\n      // Selector: 0xa415bcad\n      const sel = '0xa415bcad';\n      const p1 = assetAddr.slice(2).toLowerCase().padStart(64, '0');\n      const p2 = amountWei.toString(16).padStart(64, '0');\n      const p3 = (2n).toString(16).padStart(64, '0'); // variable rate\n      const p4 = (0n).toString(16).padStart(64, '0'); // referralCode\n      const p5 = '0'.repeat(64); // onBehalfOf = delegator (filled at execution)\n      return { target: AAVE_POOL, value: 0n, callData: `${sel}${p1}${p2}${p3}${p4}${p5}` as Hex };\n    }\n\n    if (action === 'supply') {\n      // supply(address asset, uint256 amount, address onBehalfOf, uint16 referralCode)\n      // Selector: 0x617ba037\n      const sel = '0x617ba037';\n      const p1 = assetAddr.slice(2).toLowerCase().padStart(64, '0');\n      const p2 = amountWei.toString(16).padStart(64, '0');\n      const p3 = '0'.repeat(64); // onBehalfOf = delegator\n      const p4 = (0n).toString(16).padStart(64, '0'); // referralCode\n      return { target: AAVE_POOL, value: 0n, callData: `${sel}${p1}${p2}${p3}${p4}` as Hex };\n    }\n\n    if (action === 'repay') {\n      // repay(address asset, uint256 amount, uint256 interestRateMode, address onBehalfOf)\n      // Selector: 0x573ade81\n      const sel = '0x573ade81';\n      const p1 = assetAddr.slice(2).toLowerCase().padStart(64, '0');\n      const p2 = amountWei.toString(16).padStart(64, '0');\n      const p3 = (2n).toString(16).padStart(64, '0'); // variable rate\n      const p4 = '0'.repeat(64); // onBehalfOf = delegator\n      return { target: AAVE_POOL, value: 0n, callData: `${sel}${p1}${p2}${p3}${p4}` as Hex };\n    }\n\n    // withdraw(address asset, uint256 amount, address to)\n    // Selector: 0x69328dec\n    const sel = '0x69328dec';\n    const p1 = assetAddr.slice(2).toLowerCase().padStart(64, '0');\n    const p2 = amountWei.toString(16).padStart(64, '0');\n    const p3 = '0'.repeat(64); // to = delegator\n    return { target: AAVE_POOL, value: 0n, callData: `${sel}${p1}${p2}${p3}` as Hex };\n  },\n\n  /**\n   * defi_stake tool — Lido/Rocket Pool staking.\n   * Args: { action: 'stake'|'unstake'|'unwrap', protocol: string, amount: string }\n   */\n  defi_stake: async (args) => {\n    const action = args.action as string | undefined;\n    const protocol = (args.protocol as string | undefined)?.toLowerCase();\n    const amount = args.amount as string | undefined;\n    if (!action || !amount) return null;\n\n    const weiValue = parseTokenAmount(amount, 18);\n    if (weiValue <= 0n) return null;\n\n    // Lido stake: stETH.submit(referral) with ETH value\n    if (action === 'stake' && (!protocol || protocol === 'lido')) {\n      const STETH = '0xae7ab96520DE3A18E5e111B5EaAb095312D7fE84' as Address;\n      // submit(address _referral) — selector: 0xa1903eab\n      const sel = '0xa1903eab';\n      const ref = '0'.repeat(64); // no referral\n      return { target: STETH, value: weiValue, callData: `${sel}${ref}` as Hex };\n    }\n\n    // Rocket Pool stake: depositPool.deposit() with ETH value\n    if (action === 'stake' && protocol === 'rocketpool') {\n      const DEPOSIT_POOL = '0xDD3f50F8A6CafbE9b31a427582963f465E745AF8' as Address;\n      // deposit() — selector: 0xd0e30db0 (same as WETH deposit)\n      return { target: DEPOSIT_POOL, value: weiValue, callData: '0xd0e30db0' as Hex };\n    }\n\n    // Rocket Pool unstake: rETH.burn(amount)\n    if (action === 'unstake' && protocol === 'rocketpool') {\n      const RETH = '0xae78736Cd615f374D3085123A210448E74Fc6393' as Address;\n      // burn(uint256 _rethAmount) — selector: 0x42966c68\n      const sel = '0x42966c68';\n      const p1 = weiValue.toString(16).padStart(64, '0');\n      return { target: RETH, value: 0n, callData: `${sel}${p1}` as Hex };\n    }\n\n    // Lido unwrap: wstETH.unwrap(amount)\n    if (action === 'unwrap') {\n      const WSTETH = '0x7f39C581F595B53c5cb19bD0b3f8dA6c935E2Ca0' as Address;\n      // unwrap(uint256 _wstETHAmount) — selector: 0xde0e9a3e\n      const sel = '0xde0e9a3e';\n      const p1 = weiValue.toString(16).padStart(64, '0');\n      return { target: WSTETH, value: 0n, callData: `${sel}${p1}` as Hex };\n    }\n\n    return null;\n  },\n\n  /**\n   * governance tool — on-chain voting and token delegation.\n   * Args (vote): { action: 'vote', governor: address, proposal_id: string, support: number }\n   * Args (delegate): { action: 'delegate', token: address, delegatee: address }\n   */\n  governance: async (args) => {\n    const action = args.action as string | undefined;\n\n    if (action === 'vote') {\n      const governor = args.governor as string | undefined;\n      const proposalId = args.proposal_id as string | undefined;\n      const support = args.support as number | undefined;\n      if (!governor || !proposalId || support === undefined) return null;\n      if (!/^0x[0-9a-fA-F]{40}$/.test(governor)) return null;\n\n      // castVote(uint256 proposalId, uint8 support) — selector: 0x56781388\n      const sel = '0x56781388';\n      const p1 = BigInt(proposalId).toString(16).padStart(64, '0');\n      const p2 = BigInt(support).toString(16).padStart(64, '0');\n      return { target: governor as Address, value: 0n, callData: `${sel}${p1}${p2}` as Hex };\n    }\n\n    if (action === 'delegate') {\n      const token = args.token as string | undefined;\n      const delegatee = args.delegatee as string | undefined;\n      if (!token || !delegatee) return null;\n      if (!/^0x[0-9a-fA-F]{40}$/.test(token) || !/^0x[0-9a-fA-F]{40}$/.test(delegatee)) return null;\n\n      // delegate(address delegatee) — selector: 0x5c19a95c\n      const sel = '0x5c19a95c';\n      const p1 = delegatee.slice(2).toLowerCase().padStart(64, '0');\n      return { target: token as Address, value: 0n, callData: `${sel}${p1}` as Hex };\n    }\n\n    return null;\n  },\n\n  // ── Tier 3: Extractable but may need prior approval ──────────────────\n\n  /**\n   * yield tool — ERC-4626 vault deposit/withdraw.\n   * Args: { action: 'deposit'|'withdraw', vault: address, amount: string }\n   *\n   * deposit requires the vault to have ERC-20 approval for the underlying asset.\n   * If approval is missing, gas simulation catches it.\n   */\n  yield: async (args) => {\n    const action = args.action as string | undefined;\n    if (action !== 'withdraw' && action !== 'deposit') return null;\n\n    const vault = args.vault as string | undefined;\n    const amount = args.amount as string | undefined;\n    if (!vault || !amount) return null;\n    if (!/^0x[0-9a-fA-F]{40}$/.test(vault)) return null;\n\n    const amountWei = parseTokenAmount(amount, 18); // ERC-4626 uses share decimals\n    if (amountWei <= 0n) return null;\n\n    if (action === 'deposit') {\n      // deposit(uint256 assets, address receiver) — selector: 0x6e553f65\n      const sel = '0x6e553f65';\n      const p1 = amountWei.toString(16).padStart(64, '0');\n      const p2 = '0'.repeat(64); // receiver = delegator\n      return { target: vault as Address, value: 0n, callData: `${sel}${p1}${p2}` as Hex };\n    }\n\n    // withdraw(uint256 assets, address receiver, address owner) — selector: 0xb460af94\n    const sel = '0xb460af94';\n    const p1 = amountWei.toString(16).padStart(64, '0');\n    const p2 = '0'.repeat(64); // receiver = delegator\n    const p3 = '0'.repeat(64); // owner = delegator\n    return { target: vault as Address, value: 0n, callData: `${sel}${p1}${p2}${p3}` as Hex };\n  },\n\n  // ── Liquidity (Uniswap V3) ─────────────────────────────────────────────\n\n  /**\n   * liquidity tool — Uniswap V3 position management.\n   * Args vary per action: v3_mint, v3_add, v3_remove, v3_collect\n   *\n   * V3 NonfungiblePositionManager on Base: 0x03a520b32C04BF3bEEf7BEb72E919cf822Ed34f1\n   * v3_mint/v3_add require prior ERC-20 approvals to NPM.\n   * v3_remove requires on-chain read (current position liquidity).\n   * v4_mint is NOT supported (needs @uniswap/v4-sdk for multicall encoding).\n   */\n  liquidity: async (args, ctx) => {\n    const action = args.action as string | undefined;\n    const V3_NPM = '0x03a520b32C04BF3bEEf7BEb72E919cf822Ed34f1' as Address;\n    const MAX_UINT128 = (2n ** 128n) - 1n;\n    const deadline = BigInt(Math.floor(Date.now() / 1000) + 1800); // 30 min\n\n    if (action === 'v3_mint') {\n      const token0 = args.token0 as string | undefined;\n      const token1 = args.token1 as string | undefined;\n      const fee = Number(args.fee ?? 3000);\n      const tickLower = Number(args.tick_lower ?? 0);\n      const tickUpper = Number(args.tick_upper ?? 0);\n      const amount0 = args.amount0 as string | undefined;\n      const amount1 = args.amount1 as string | undefined;\n\n      if (!token0 || !token1 || !amount0 || !amount1) return null;\n      if (!/^0x[0-9a-fA-F]{40}$/.test(token0) || !/^0x[0-9a-fA-F]{40}$/.test(token1)) return null;\n\n      const dec0 = getTokenDecimals(token0);\n      const dec1 = getTokenDecimals(token1);\n      const amt0 = parseTokenAmount(amount0, dec0);\n      const amt1 = parseTokenAmount(amount1, dec1);\n      if (amt0 <= 0n && amt1 <= 0n) return null;\n\n      const recipient = ctx?.walletAddress ?? ('0x' + '0'.repeat(40)) as Address;\n\n      // mint((address,address,uint24,int24,int24,uint256,uint256,uint256,uint256,address,uint256))\n      // Selector: 0x88316456\n      const sel = '0x88316456';\n      // ABI encode as a single tuple — offset(32) + 11 fields\n      const fields = [\n        token0.slice(2).toLowerCase().padStart(64, '0'),\n        token1.slice(2).toLowerCase().padStart(64, '0'),\n        fee.toString(16).padStart(64, '0'),\n        toInt24Hex(tickLower),\n        toInt24Hex(tickUpper),\n        amt0.toString(16).padStart(64, '0'),\n        amt1.toString(16).padStart(64, '0'),\n        (0n).toString(16).padStart(64, '0'), // amount0Min\n        (0n).toString(16).padStart(64, '0'), // amount1Min\n        recipient.slice(2).toLowerCase().padStart(64, '0'),\n        deadline.toString(16).padStart(64, '0'),\n      ].join('');\n\n      return { target: V3_NPM, value: 0n, callData: `${sel}${fields}` as Hex };\n    }\n\n    if (action === 'v3_add') {\n      const tokenId = args.token_id as string | undefined;\n      const amount0 = args.amount0 as string | undefined;\n      const amount1 = args.amount1 as string | undefined;\n      if (!tokenId || !amount0 || !amount1) return null;\n\n      const token0 = args.token0 as string | undefined;\n      const token1 = args.token1 as string | undefined;\n      const dec0 = token0 ? getTokenDecimals(token0) : 18;\n      const dec1 = token1 ? getTokenDecimals(token1) : 18;\n      const amt0 = parseTokenAmount(amount0, dec0);\n      const amt1 = parseTokenAmount(amount1, dec1);\n\n      // increaseLiquidity((uint256,uint256,uint256,uint256,uint256,uint256))\n      // Selector: 0x219f5d17\n      const sel = '0x219f5d17';\n      const fields = [\n        BigInt(tokenId).toString(16).padStart(64, '0'),\n        amt0.toString(16).padStart(64, '0'),\n        amt1.toString(16).padStart(64, '0'),\n        (0n).toString(16).padStart(64, '0'), // amount0Min\n        (0n).toString(16).padStart(64, '0'), // amount1Min\n        deadline.toString(16).padStart(64, '0'),\n      ].join('');\n\n      return { target: V3_NPM, value: 0n, callData: `${sel}${fields}` as Hex };\n    }\n\n    if (action === 'v3_collect') {\n      const tokenId = args.token_id as string | undefined;\n      if (!tokenId) return null;\n\n      const recipient = ctx?.walletAddress ?? ('0x' + '0'.repeat(40)) as Address;\n\n      // collect((uint256,address,uint128,uint128))\n      // Selector: 0xfc6f7865\n      const sel = '0xfc6f7865';\n      const fields = [\n        BigInt(tokenId).toString(16).padStart(64, '0'),\n        recipient.slice(2).toLowerCase().padStart(64, '0'),\n        MAX_UINT128.toString(16).padStart(64, '0'),\n        MAX_UINT128.toString(16).padStart(64, '0'),\n      ].join('');\n\n      return { target: V3_NPM, value: 0n, callData: `${sel}${fields}` as Hex };\n    }\n\n    if (action === 'v3_remove') {\n      const tokenId = args.token_id as string | undefined;\n      const percentage = Number(args.percentage ?? 100);\n      if (!tokenId) return null;\n      if (!ctx?.walletAddress) return null;\n\n      // Need on-chain read to get current liquidity\n      try {\n        const { getPublicClient } = await import('./walletconnect-service.js');\n        const pub = getPublicClient();\n        if (!pub) return null;\n\n        // positions(uint256) returns (nonce,operator,token0,token1,fee,tickLower,tickUpper,liquidity,...)\n        // Selector: 0x99fbab88\n        const posData = await pub.call({\n          to: V3_NPM,\n          data: `0x99fbab88${BigInt(tokenId).toString(16).padStart(64, '0')}` as Hex,\n        });\n        if (!posData.data || posData.data.length < 514) return null; // 0x + 256*2 chars minimum\n\n        // liquidity is at offset 7 (8th field, 0-indexed) = bytes 224-256\n        const liquidityHex = posData.data.slice(2 + 7 * 64, 2 + 8 * 64);\n        const totalLiquidity = BigInt('0x' + liquidityHex);\n        if (totalLiquidity <= 0n) return null;\n\n        const liquidityToRemove = (totalLiquidity * BigInt(percentage)) / 100n;\n\n        // Build multicall: decreaseLiquidity + collect (+ optional burn)\n        // decreaseLiquidity((uint256,uint128,uint256,uint256,uint256))\n        const decSel = '0x0c49ccbe';\n        const decFields = [\n          BigInt(tokenId).toString(16).padStart(64, '0'),\n          liquidityToRemove.toString(16).padStart(64, '0'),\n          (0n).toString(16).padStart(64, '0'), // amount0Min\n          (0n).toString(16).padStart(64, '0'), // amount1Min\n          deadline.toString(16).padStart(64, '0'),\n        ].join('');\n        const decreaseCalldata = `${decSel}${decFields}`;\n\n        // collect\n        const colSel = '0xfc6f7865';\n        const colFields = [\n          BigInt(tokenId).toString(16).padStart(64, '0'),\n          ctx.walletAddress.slice(2).toLowerCase().padStart(64, '0'),\n          MAX_UINT128.toString(16).padStart(64, '0'),\n          MAX_UINT128.toString(16).padStart(64, '0'),\n        ].join('');\n        const collectCalldata = `${colSel}${colFields}`;\n\n        // Encode multicall(bytes[])\n        const calls = [decreaseCalldata, collectCalldata];\n        if (percentage >= 100) {\n          // burn(uint256) — 0x42966c68\n          const burnCalldata = `0x42966c68${BigInt(tokenId).toString(16).padStart(64, '0')}`;\n          calls.push(burnCalldata);\n        }\n\n        // multicall ABI encoding: selector + offset + length + [offset_per_call...] + [length+data per call...]\n        // This is complex ABI encoding — use a simpler approach: just return decreaseLiquidity\n        // as the primary action. The collect can happen in a subsequent call.\n        // For delegation, a single action per redemption is the standard pattern.\n        return {\n          target: V3_NPM,\n          value: 0n,\n          callData: `${decSel}${decFields}` as Hex,\n        };\n      } catch {\n        return null;\n      }\n    }\n\n    // v4_mint not supported — needs @uniswap/v4-sdk for multicall encoding\n    return null;\n  },\n\n  // ── P6: API-based extractors (async, fetch calldata from external APIs) ──\n\n  /**\n   * defi_swap tool — fetch swap calldata from Clawnch/0x API.\n   * Args: { action: 'execute', token_in, token_out, amount, slippage? }\n   */\n  defi_swap: async (args, ctx) => {\n    const action = args.action as string | undefined;\n    if (action !== 'execute') return null;\n    if (!ctx?.walletAddress) return null;\n\n    const tokenIn = args.token_in as string | undefined;\n    const tokenOut = args.token_out as string | undefined;\n    const amount = args.amount as string | undefined;\n    const slippage = (args.slippage as number | undefined) ?? 1.0;\n    if (!tokenIn || !tokenOut || !amount) return null;\n\n    // Resolve token symbols to addresses (Base)\n    const inAddr = resolveSwapToken(tokenIn);\n    const outAddr = resolveSwapToken(tokenOut);\n    if (!inAddr || !outAddr) return null;\n\n    // Parse amount to wei\n    const isNativeIn = inAddr.toLowerCase() === NATIVE_TOKEN_ADDRESS.toLowerCase();\n    const decimals = isNativeIn ? 18 : getTokenDecimals(inAddr);\n    const sellAmountWei = parseTokenAmount(amount, decimals);\n    if (sellAmountWei <= 0n) return null;\n\n    try {\n      const apiBase = process.env.CLAWNCHER_API_URL || 'https://clawn.ch';\n      const chainId = ctx.chainId ?? 8453;\n      const params = new URLSearchParams({\n        chainId: String(chainId),\n        sellToken: inAddr,\n        buyToken: outAddr,\n        sellAmount: sellAmountWei.toString(),\n        slippageBps: String(Math.round(slippage * 100)),\n        taker: ctx.walletAddress,\n      });\n\n      const controller = new AbortController();\n      const timeout = setTimeout(() => controller.abort(), 5000);\n      const resp = await fetch(`${apiBase}/api/swap/quote?${params}`, {\n        headers: { Accept: 'application/json' },\n        signal: controller.signal,\n      });\n      clearTimeout(timeout);\n\n      if (!resp.ok) return null;\n      const data = await resp.json() as any;\n      if (!data.transaction?.to || !data.transaction?.data) return null;\n\n      // C1: Validate target is a known swap router (prevent arbitrary execution if API compromised)\n      if (!isKnownSwapTarget(data.transaction.to)) {\n        console.info(`[delegation] Swap API returned unknown target: ${data.transaction.to}`);\n        return null;\n      }\n\n      return {\n        target: data.transaction.to as Address,\n        value: BigInt(data.transaction.value || '0'),\n        callData: data.transaction.data as Hex,\n      };\n    } catch {\n      // API timeout or error — fall through to normal execution\n      return null;\n    }\n  },\n\n  /**\n   * bridge tool — fetch bridge calldata from LI.FI API.\n   * Args: { action: 'execute', from_chain, to_chain, from_token, to_token, amount, slippage? }\n   */\n  bridge: async (args, ctx) => {\n    const action = args.action as string | undefined;\n    if (action !== 'execute') return null;\n    if (!ctx?.walletAddress) return null;\n\n    const fromChain = args.from_chain as string | undefined;\n    const toChain = args.to_chain as string | undefined;\n    const amount = args.amount as string | undefined;\n    if (!toChain || !amount) return null;\n\n    const fromToken = (args.from_token as string | undefined) ?? '0x0000000000000000000000000000000000000000';\n    const toToken = (args.to_token as string | undefined) ?? '0x0000000000000000000000000000000000000000';\n    const slippage = (args.slippage as number | undefined) ?? 0.005;\n\n    // Resolve chain names to IDs\n    const fromChainId = resolveChainId(fromChain ?? 'base') ?? ctx.chainId ?? 8453;\n    const toChainId = resolveChainId(toChain);\n    if (!toChainId) return null;\n\n    try {\n      const params = new URLSearchParams({\n        fromChain: String(fromChainId),\n        toChain: String(toChainId),\n        fromToken,\n        toToken,\n        fromAmount: amount,\n        fromAddress: ctx.walletAddress,\n        slippage: String(slippage),\n      });\n\n      const headers: Record<string, string> = { Accept: 'application/json' };\n      const apiKey = process.env.LIFI_API_KEY;\n      if (apiKey) headers['x-lifi-api-key'] = apiKey;\n\n      const controller = new AbortController();\n      const timeout = setTimeout(() => controller.abort(), 5000);\n      const resp = await fetch(`https://li.quest/v1/quote?${params}`, {\n        headers,\n        signal: controller.signal,\n      });\n      clearTimeout(timeout);\n\n      if (!resp.ok) return null;\n      const data = await resp.json() as any;\n      const txReq = data.transactionRequest;\n      if (!txReq?.to || !txReq?.data) return null;\n\n      // C1: Validate target is a known bridge contract\n      if (!isKnownBridgeTarget(txReq.to)) {\n        console.info(`[delegation] Bridge API returned unknown target: ${txReq.to}`);\n        return null;\n      }\n\n      return {\n        target: txReq.to as Address,\n        value: txReq.value ? BigInt(txReq.value) : 0n,\n        callData: txReq.data as Hex,\n      };\n    } catch {\n      return null;\n    }\n  },\n};\n\n// ─── Token / Chain Resolution Helpers ───────────────────────────────────\n\nconst NATIVE_TOKEN_ADDRESS = '0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE';\n\n/** Resolve token symbol or address for swap API. */\nfunction resolveSwapToken(token: string): string | null {\n  if (/^0x[0-9a-fA-F]{40}$/.test(token)) return token;\n  const SYMBOLS: Record<string, string> = {\n    eth: NATIVE_TOKEN_ADDRESS,\n    weth: '0x4200000000000000000000000000000000000006',\n    usdc: '0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913',\n    usdt: '0xfde4C96c8593536E31F229EA8f37b2ADa2699bb2',\n    dai: '0x50c5725949A6F0c72E6C4a641F24049A917DB0Cb',\n    cbeth: '0x2Ae3F1Ec7F1F5012CFEab0185bfc7aa3cf0DEc22',\n  };\n  return SYMBOLS[token.toLowerCase()] ?? null;\n}\n\n/** Resolve chain name to chain ID. */\nfunction resolveChainId(chain: string): number | null {\n  const n = parseInt(chain, 10);\n  if (!isNaN(n)) return n;\n  const CHAINS: Record<string, number> = {\n    ethereum: 1, eth: 1, mainnet: 1,\n    base: 8453,\n    arbitrum: 42161, arb: 42161,\n    optimism: 10, op: 10,\n    polygon: 137, matic: 137,\n    avalanche: 43114, avax: 43114,\n    bnb: 56, bsc: 56,\n    linea: 59144,\n    zksync: 324,\n    scroll: 534352,\n  };\n  return CHAINS[chain.toLowerCase()] ?? null;\n}\n\n// ─── Core Executor ──────────────────────────────────────────────────────\n\n/**\n * Attempt to execute a tool action via delegation redemption.\n *\n * Called from the policy gate AFTER policies have been evaluated as 'allow'.\n * Returns { executed: true } if the action was successfully redeemed on-chain,\n * or { executed: false, skipReason } if delegation was not applicable.\n *\n * The caller should fall back to normal tool execution when executed === false.\n */\nexport async function tryDelegationExecution(\n  actionCtx: ActionContext,\n  toolArgs: Record<string, unknown>,\n): Promise<DelegationExecutionResult> {\n  // Gate 1: delegation mode must be active\n  if (!isDelegationMode()) {\n    return { executed: false, skipReason: 'Not in delegation mode.' };\n  }\n\n  // Note: EOA check on the DELEGATOR happens after gate 4 (when we know\n  // which delegation will be used). The connected wallet is the DELEGATE\n  // (agent), which is always an EOA. The gas simulation in redeemDelegation()\n  // also catches delegator-is-EOA reverts before spending gas.\n\n  // Gate 2: tool must have a supported action extractor\n  const extractor = SUPPORTED_EXTRACTORS[actionCtx.toolName];\n  if (!extractor) {\n    return { executed: false, skipReason: `Tool \"${actionCtx.toolName}\" does not support delegation execution yet.` };\n  }\n\n  // Gate 3: extract the on-chain action from tool args\n  // Pass wallet context for extractors that need to fetch quotes (swap, bridge)\n  const { getWalletState } = await import('./walletconnect-service.js');\n  const walletState = getWalletState();\n  const extractorCtx: ExtractorContext = {\n    walletAddress: walletState.address as Address | undefined,\n    chainId: walletState.chainId ?? actionCtx.chain,\n  };\n  const executionAction = await extractor(toolArgs, extractorCtx);\n  if (!executionAction) {\n    return { executed: false, skipReason: 'Could not extract execution action from tool args.' };\n  }\n\n  // Gate 3b: check for MetaMask Advanced Permissions (parallel path)\n  // If the user granted permissions via ERC-7715, use those instead of raw delegation.\n  try {\n    const { hasAdvancedPermissions, getPermissionContext } = await import('./advanced-permissions.js');\n    const { getWalletClient } = await import('./walletconnect-service.js');\n\n    // Check all delegated policies for stored Advanced Permissions\n    const policies = getDelegatedPolicies(actionCtx.userId);\n    for (const p of policies) {\n      if (!p.delegation || !hasAdvancedPermissions(p.id)) continue;\n      if (!policyScopeCovers(p, actionCtx.toolName)) continue;\n\n      const permCtx = getPermissionContext(p.id);\n      if (!permCtx) continue;\n\n      // Found Advanced Permissions — redeem via SDK path\n      const wc = getWalletClient();\n      if (!wc) continue;\n\n      try {\n        const { DelegationManager: DM } = await import('@metamask/smart-accounts-kit/contracts');\n        const { createExecution, ExecutionMode } = await import('@metamask/smart-accounts-kit');\n\n        const execution = createExecution({\n          target: executionAction.target,\n          value: executionAction.value,\n          callData: executionAction.callData,\n        });\n\n        // Build and send the redeem tx\n        const redeemCalldata = DM.encode.redeemDelegations({\n          delegations: [[]], // permissionsContext handles the delegation chain\n          modes: [ExecutionMode.SingleDefault],\n          executions: [[execution]],\n        });\n\n        const txHash = await (wc as any).sendTransaction({\n          to: permCtx.delegationManager,\n          data: redeemCalldata,\n        });\n\n        return {\n          executed: true,\n          txHash: txHash as string,\n          chainId: p.delegation.chainId,\n        };\n      } catch (apErr) {\n        // Advanced Permissions redemption failed — continue to raw delegation path\n        const msg = apErr instanceof Error ? apErr.message : String(apErr);\n        console.info(`[delegation] Advanced Permissions failed for ${p.name}: ${msg.slice(0, 100)}`);\n      }\n    }\n  } catch {\n    // advanced-permissions module not available or import failed — continue to raw path\n  }\n\n  // Gate 4: find a matching delegation (raw viem path)\n  const matchResult = findMatchingDelegation(actionCtx);\n  if (!matchResult) {\n    return { executed: false, skipReason: 'No matching delegation found for this action.' };\n  }\n\n  // M4: Fill zero-address placeholders with the delegator address\n  // Extractors use 0x000...0 for onBehalfOf/from/receiver/owner params\n  // because the delegator isn't known until after matching.\n  if (matchResult.delegator && executionAction.callData.length > 10) {\n    const zeroAddr = '0'.repeat(64);\n    const delegatorPadded = matchResult.delegator.slice(2).toLowerCase().padStart(64, '0');\n    if (executionAction.callData.includes(zeroAddr)) {\n      executionAction.callData = executionAction.callData.replaceAll(zeroAddr, delegatorPadded) as Hex;\n    }\n  }\n\n  // Gate 5: delegation must be redeemable\n  const readiness = canRedeem(matchResult.policyId);\n  if (!readiness.ready) {\n    return { executed: false, skipReason: readiness.reason ?? 'Delegation not ready.' };\n  }\n\n  // Check expiry (P2-2: client-side TimestampEnforcer check)\n  if (matchResult.expiresAt) {\n    const expiryMs = new Date(matchResult.expiresAt).getTime();\n    if (!isNaN(expiryMs) && Date.now() > expiryMs) {\n      return { executed: false, skipReason: 'Delegation has expired. Create a new one with /delegate create.' };\n    }\n  }\n\n  // Check chain match (P2-4: delegation must be for the current chain)\n  if (actionCtx.chain && matchResult.chainId && actionCtx.chain !== matchResult.chainId) {\n    return {\n      executed: false,\n      skipReason: `Delegation is for chain ${matchResult.chainId} but wallet is on chain ${actionCtx.chain}. Switch chains or create a new delegation.`,\n    };\n  }\n\n  // Gate 6b: check if the delegation's delegator is a smart account\n  // redeemDelegations calls executeFromExecutor on the delegator — plain EOAs revert.\n  if (matchResult.delegator) {\n    try {\n      const { getPublicClient } = await import('./walletconnect-service.js');\n      const pub = getPublicClient();\n      if (pub) {\n        const code = await pub.getCode({ address: matchResult.delegator as Address });\n        if (!code || code === '0x' || code === '0x0') {\n          return {\n            executed: false,\n            skipReason: `Delegator ${matchResult.delegator} is a plain EOA. On-chain delegation requires a smart account. Use /upgrade to convert.`,\n          };\n        }\n      }\n    } catch {\n      // Non-fatal: if the check fails, let the gas simulation catch it\n    }\n  }\n\n  // Gate 7: rate limiter — prevent gas-burning loops on repeated failures\n  if (isRateLimited(actionCtx.userId, actionCtx.toolName)) {\n    return {\n      executed: false,\n      skipReason: `Delegation rate-limited for ${actionCtx.toolName} (too many recent attempts). Wait before retrying.`,\n    };\n  }\n\n  // All gates passed — attempt redemption\n  recordRedemptionAttempt(actionCtx.userId, actionCtx.toolName);\n  try {\n    const result = await redeemDelegation(matchResult.policyId, executionAction);\n\n    if ('error' in result) {\n      // Redemption failed — caller should fall back to normal execution\n      return { executed: false, error: result.error };\n    }\n\n    // Success — clear rate limit\n    clearRateLimit(actionCtx.userId, actionCtx.toolName);\n\n    return {\n      executed: true,\n      txHash: result.txHash,\n      chainId: result.chainId,\n    };\n  } catch (err) {\n    const msg = err instanceof Error ? err.message : String(err);\n    return { executed: false, error: `Delegation execution error: ${msg}` };\n  }\n}\n\n// ─── Delegation Matching ────────────────────────────────────────────────\n\ninterface DelegationMatch {\n  policyId: string;\n  policyName: string;\n  chainId: number;\n  delegator?: string;\n  expiresAt?: string;\n}\n\n/**\n * Find a delegation that covers the given action.\n *\n * Matching logic:\n * 1. Get all delegated policies for the user\n * 2. Filter to active/signed status (not revoked/expired)\n * 3. Check if the policy's scope covers the tool\n * 4. Return the first match (most recently updated)\n *\n * Future: rank by remaining budget, prefer policies with more headroom.\n */\nfunction findMatchingDelegation(ctx: ActionContext): DelegationMatch | null {\n  const policies = getDelegatedPolicies(ctx.userId);\n\n  for (const policy of policies) {\n    const info = policy.delegation;\n    if (!info) continue;\n\n    // Must be signed or active (not revoked/expired/unsigned)\n    if (info.status !== 'signed' && info.status !== 'active') continue;\n\n    // Check scope — does this policy cover the tool?\n    if (!policyScopeCovers(policy, ctx.toolName)) continue;\n\n    return {\n      policyId: policy.id,\n      policyName: policy.name,\n      chainId: info.chainId,\n      delegator: info.delegator,\n      expiresAt: info.expiresAt,\n    };\n  }\n\n  return null;\n}\n\n/**\n * Check if a policy's scope covers a specific tool.\n * Mirrors policyApplies() in policy-evaluator.ts.\n */\nfunction policyScopeCovers(policy: { scope: { type: string; tools?: string[]; categories?: string[] } }, toolName: string): boolean {\n  const { scope } = policy;\n  if (scope.type === 'all_write') return true;\n  if (scope.type === 'tools') return (scope.tools ?? []).includes(toolName);\n  if (scope.type === 'categories') {\n    // Inline category lookup to avoid circular import\n    const TOOL_CATEGORIES: Record<string, string[]> = {\n      defi: ['defi_swap', 'defi_lend', 'defi_stake', 'liquidity', 'yield', '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    let toolCategory: string | undefined;\n    for (const [cat, tools] of Object.entries(TOOL_CATEGORIES)) {\n      if (tools.includes(toolName)) { toolCategory = cat; break; }\n    }\n    if (!toolCategory) return false;\n    return (scope.categories ?? []).includes(toolCategory);\n  }\n  return false;\n}\n\n// ─── Helpers ────────────────────────────────────────────────────────────\n\n/**\n * Check if delegation execution is available for a tool.\n * Quick sync check — verifies the tool has an extractor and a matching\n * delegation exists. Does NOT verify the extractor can parse the specific\n * args (that's async and happens in tryDelegationExecution).\n */\nexport function isDelegationExecutionAvailable(toolName: string, userId: string): boolean {\n  if (!isDelegationMode()) return false;\n  if (!SUPPORTED_EXTRACTORS[toolName]) return false;\n\n  const policies = getDelegatedPolicies(userId);\n  return policies.some(p => {\n    const info = p.delegation;\n    if (!info) return false;\n    if (info.status !== 'signed' && info.status !== 'active') return false;\n    return policyScopeCovers(p, toolName);\n  });\n}\n\n/**\n * Get the list of tools that support delegation execution.\n */\nexport function getDelegationSupportedTools(): string[] {\n  return Object.keys(SUPPORTED_EXTRACTORS);\n}\n"],"mappings":"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"}