{"version":3,"sources":["../src/index.ts","../src/schemas.ts","../src/lib/browser-service-env.ts","../src/driver/BrowserDriver.ts","../src/selectors.ts","../src/errors.ts","../src/api/cost-telemetry.ts","../src/api/db.ts","../src/api/cost-context.ts","../src/api/cost-rates.ts","../src/lib/browser-user-agent.ts","../src/locations.ts","../src/uule.ts","../src/serp-location-query.ts","../src/serp-location-debug.ts","../src/lib/paa-answer-cleanup.ts","../src/extractor/ai-surfaces.ts","../src/extractor/PAAExtractor.ts","../src/output/OutputSerializer.ts","../src/output/memory-library-sink.ts","../src/output/ProgressReporter.ts","../src/kernel-proxy-resolver.ts","../src/backup-proxy.ts","../src/harvest.ts","../src/video/VideoGenerator.ts","../src/video/promptBuilder.ts","../src/video/AudioGenerator.ts","../src/video/VideoMixer.ts"],"sourcesContent":["export { harvest } from './harvest.js'\nexport type { HarvestOptions, HarvestResult, HarvestDiagnostics, HarvestCompletionStatus, PAANode, FlatRow, HarvestStats, AIOverviewResult, AIModeResult, AICitation, GoogleSurface, WhatPeopleSayingCard } from './types.js'\nexport { VideoGenerator } from './video/VideoGenerator.js'\nexport type { ClipPairOptions, ClipPairResult } from './video/VideoGenerator.js'\nexport { buildClipPrompts } from './video/promptBuilder.js'\n","import { z } from 'zod'\n\nexport const DEFAULT_PROXY_MODE = 'none' as const\nexport const DEFAULT_MAPS_PROXY_MODE = 'none' as const\n\nexport const HarvestOptionsSchema = z.object({\n  query:        z.string().min(1),\n  location:     z.string().optional(),\n  gl:           z.string().length(2).default('us'),\n  hl:           z.string().length(2).default('en'),\n  device:       z.enum(['desktop', 'mobile']).default('desktop'),\n  proxyMode:    z.enum(['location', 'configured', 'none']).default(DEFAULT_PROXY_MODE),\n  proxyZip:     z.string().regex(/^\\d{5}$/).optional(),\n  keepDefaultProxy: z.boolean().optional(),\n  requireUsEgress: z.boolean().optional(),\n  maxAttempts:  z.number().int().min(1).max(12).optional(),\n  debug:        z.boolean().default(false),\n  depth:        z.number().int().min(1).max(30).default(3),\n  maxQuestions: z.number().int().min(1).max(1000).default(100),\n  headless:     z.boolean().default(false),\n  profileDir:   z.string().optional(),\n  proxy:        z.string().url().optional(),\n  kernelApiKey: z.string().optional(),\n  kernelProxyId: z.string().optional(),\n  kernelProxyResolution: z.unknown().optional(),\n  outputDir:    z.string().default('./paa-output'),\n  format:       z.enum(['json', 'csv', 'both']).default('both'),\n  serpOnly:     z.boolean().default(false),\n  pages:        z.number().int().min(1).max(2).default(1),\n  recency:      z.enum(['day', 'week', 'month', 'year']).optional(),\n  softDeadlineMs: z.number().optional(),\n})\n\nexport function recencyToTbs(recency: 'day' | 'week' | 'month' | 'year' | undefined): string | undefined {\n  switch (recency) {\n    case 'day': return 'qdr:d'\n    case 'week': return 'qdr:w'\n    case 'month': return 'qdr:m'\n    case 'year': return 'qdr:y'\n    default: return undefined\n  }\n}\n\nexport const MapsPlaceOptionsSchema = z.object({\n  businessName:  z.string().min(1),\n  location:      z.string().min(1),\n  gl:            z.string().length(2).default('us'),\n  hl:            z.string().length(2).default('en'),\n  includeReviews: z.boolean().default(false),\n  maxReviews:    z.number().int().min(1).max(500).default(50),\n  includeServices: z.boolean().default(false),\n  includeImages: z.boolean().default(false),\n  imageScope: z.enum(['owner', 'all']).default('all'),\n  maxImages: z.number().int().min(1).max(250).default(100),\n  maxInlineImages: z.number().int().min(0).max(5).default(3),\n  kernelApiKey:  z.string().optional(),\n  kernelProxyId: z.string().optional(),\n  headless:      z.boolean().default(true),\n})\n\nexport const MapsSearchOptionsSchema = z.object({\n  query:         z.string().min(1),\n  location:      z.string().optional(),\n  gl:            z.string().length(2).default('us'),\n  hl:            z.string().length(2).default('en'),\n  maxResults:    z.number().int().min(1).max(50).default(10),\n  includeServices: z.boolean().default(false),\n  proxyMode:     z.enum(['location', 'configured', 'none']).default(DEFAULT_MAPS_PROXY_MODE),\n  proxyZip:      z.string().regex(/^\\d{5}$/).optional(),\n  serpRedirect:  z.boolean().optional(),\n  forceDirectEgress: z.boolean().optional(),\n  debug:         z.boolean().default(false),\n  kernelApiKey:  z.string().optional(),\n  kernelProxyId: z.string().optional(),\n  kernelProxyResolution: z.unknown().optional(),\n  headless:      z.boolean().default(true),\n})\n\nexport const RawPAAItemSchema = z.object({\n  question:    z.string().min(1),\n  answer:      z.string().optional(),\n  sourceTitle: z.string().optional(),\n  sourceSite:  z.string().optional(),\n  sourceCite:  z.string().optional(),\n})\n\nexport const RawMapsOverviewSchema = z.object({\n  name:         z.string().nullable(),\n  rating:       z.string().nullable(),\n  reviewCount:  z.string().nullable(),\n  category:     z.string().nullable(),\n  address:      z.string().nullable(),\n  hoursSummary: z.string().nullable(),\n  phone:        z.string().nullable(),\n  phoneDisplay: z.string().nullable(),\n  website:      z.string().nullable(),\n  plusCode:     z.string().nullable(),\n  bookingUrl:   z.string().nullable(),\n})\n\nexport const RawMapsHoursRowSchema = z.object({\n  day:   z.string(),\n  hours: z.string(),\n})\n\nexport const RawMapsReviewStatsSchema = z.object({\n  reviewHistogram: z.array(z.object({\n    stars: z.number(),\n    count: z.string(),\n  })),\n  reviewTopics: z.array(z.object({\n    label: z.string(),\n    count: z.string(),\n  })),\n})\n\nexport const RawMapsReviewCardSchema = z.object({\n  reviewId:      z.string(),\n  author:        z.string().nullable(),\n  stars:         z.string().nullable(),\n  date:          z.string().nullable(),\n  text:          z.string().nullable(),\n  ownerResponse: z.string().nullable(),\n})\n\nexport const RawMapsAboutAttributeSchema = z.object({\n  section:   z.string(),\n  attribute: z.string(),\n})\n","export function browserServiceApiKey(): string | undefined {\n  const value = (process.env.BROWSER_SERVICE_API_KEY ?? process.env.KERNEL_API_KEY)?.trim()\n  return value || undefined\n}\n\nexport function browserServiceProxyId(): string | undefined {\n  return undefined\n}\n\nexport function browserServiceProfileName(): string | undefined {\n  const value = (\n    process.env.BROWSER_AGENT_PROFILE_NAME ??\n    process.env.BROWSER_SERVICE_PROFILE_NAME ??\n    process.env.KERNEL_BROWSER_PROFILE_NAME ??\n    process.env.KERNEL_PROFILE_NAME\n  )?.trim()\n  return value || undefined\n}\n\nexport function browserServiceProfileSaveChanges(): boolean | undefined {\n  const value = (\n    process.env.BROWSER_AGENT_PROFILE_SAVE_CHANGES ??\n    process.env.BROWSER_SERVICE_PROFILE_SAVE_CHANGES ??\n    process.env.KERNEL_BROWSER_PROFILE_SAVE_CHANGES ??\n    process.env.KERNEL_PROFILE_SAVE_CHANGES\n  )?.trim().toLowerCase()\n  if (!value) return undefined\n  if (['1', 'true', 'yes', 'on'].includes(value)) return true\n  if (['0', 'false', 'no', 'off'].includes(value)) return false\n  return undefined\n}\n","import { chromium } from 'playwright-extra'\nimport StealthPlugin from 'puppeteer-extra-plugin-stealth'\nimport { chromium as playwrightChromium } from 'playwright'\nimport type { Browser, BrowserContext, Page } from 'playwright'\nimport Kernel from '@onkernel/sdk'\nimport { PAASelectors } from '../selectors.js'\nimport { CaptchaError, ExtractionError, RECAPTCHA_INSTRUCTIONS } from '../errors.js'\nimport type { BrowserDebugSnapshot, BrowserNetworkLocationDebug, DriverConfig, KernelSessionDebug, ProxyMode, SerpDevice, SerpNavigationDebug, UuleString } from '../types.js'\nimport type { BrowserCloseResult, IBrowserDriver } from './IBrowserDriver.js'\nimport { recordKernelSession } from '../api/cost-telemetry.js'\nimport { currentCostContext } from '../api/cost-context.js'\nimport { HEADLESS_OPS } from '../api/cost-rates.js'\nimport { detectPageUserAgent, detectRuntimeUserAgent } from '../lib/browser-user-agent.js'\n\nexport { detectRuntimeUserAgent } from '../lib/browser-user-agent.js'\n\nchromium.use(StealthPlugin())\n\nconst DEFAULT_KERNEL_BROWSER_TIMEOUT_SECONDS = 180\nconst KERNEL_BROWSER_CLOSE_TIMEOUT_MS = 3_000\nconst KERNEL_SESSION_DELETE_TIMEOUT_MS = 5_000\n\nfunction positiveIntFromEnv(name: string, fallback: number): number {\n  const raw = process.env[name]\n  if (!raw) return fallback\n  const parsed = Number(raw)\n  return Number.isInteger(parsed) && parsed > 0 ? parsed : fallback\n}\n\nfunction proxyIdSuffix(proxyId: string | null | undefined): string | null {\n  return proxyId ? proxyId.slice(-6) : null\n}\n\nexport function serpArrivalUrl(googleUrl: string, baseOverride?: string): string {\n  const base = (baseOverride ?? process.env.SERP_REDIRECT_BASE)?.trim().replace(/\\/+$/, '')\n  if (!base) return googleUrl\n  return `${base}/g/${Buffer.from(googleUrl, 'utf8').toString('base64url')}`\n}\n\nexport function isUsCountry(country: string | null | undefined): boolean {\n  if (!country) return false\n  return /united states|u\\.?s\\.?a?\\.?|^us$/i.test(country.trim())\n}\n\nfunction errorText(err: unknown): string {\n  return err instanceof Error ? err.message : String(err)\n}\n\nfunction isKernelProfileConflict(err: unknown): boolean {\n  return /\\b409\\b|conflict|already exists/i.test(errorText(err))\n}\n\nasync function ensureKernelProfile(kernelClient: Kernel, name: string): Promise<void> {\n  try {\n    await kernelClient.profiles.create({ name })\n  } catch (err) {\n    if (!isKernelProfileConflict(err)) throw err\n  }\n}\n\nexport function rankCheckContextOptions(config: DriverConfig, runtimeUserAgent?: string | null) {\n  return {\n    viewport: config.viewport,\n    locale: config.locale,\n    ...(runtimeUserAgent ? { userAgent: runtimeUserAgent } : {}),\n    ...(config.deviceScaleFactor ? { deviceScaleFactor: config.deviceScaleFactor } : {}),\n    ...(config.isMobile !== undefined ? { isMobile: config.isMobile } : {}),\n    ...(config.hasTouch !== undefined ? { hasTouch: config.hasTouch } : {}),\n  }\n}\n\nasync function withTimeout<T>(promise: Promise<T>, timeoutMs: number, label: string): Promise<T> {\n  let timeout: ReturnType<typeof setTimeout> | undefined\n  try {\n    return await Promise.race([\n      promise,\n      new Promise<never>((_, reject) => {\n        timeout = setTimeout(() => reject(new Error(`${label} timed out after ${timeoutMs}ms`)), timeoutMs)\n      }),\n    ])\n  } finally {\n    if (timeout) clearTimeout(timeout)\n  }\n}\n\nexport function buildYouTubeChannelVideosUrl(channelInput: string): string {\n  const raw = channelInput.trim()\n  if (!raw) throw new Error('channelHandle is required')\n\n  const urlLike = /^https?:\\/\\//i.test(raw) || /^(www\\.|m\\.)?youtube\\.com\\//i.test(raw)\n  if (urlLike) {\n    const parsed = new URL(/^https?:\\/\\//i.test(raw) ? raw : `https://${raw}`)\n    const host = parsed.hostname.replace(/^www\\./, '').replace(/^m\\./, '').toLowerCase()\n    if (host !== 'youtube.com') throw new Error('channel URL must be on youtube.com')\n\n    const segments = parsed.pathname.split('/').filter(Boolean)\n    const first = segments[0] ?? ''\n    const second = segments[1] ?? ''\n\n    if (first.startsWith('@')) return `https://www.youtube.com/${first}/videos`\n    if (first === 'channel' && second) return `https://www.youtube.com/channel/${second}/videos`\n    if ((first === 'c' || first === 'user') && second) return `https://www.youtube.com/${first}/${second}/videos`\n\n    throw new Error('channel URL must be a YouTube handle, /channel/UC..., /c/..., or /user/... URL')\n  }\n\n  const stripped = raw.replace(/^\\/+/, '').replace(/\\/+$/, '')\n  const withoutVideos = stripped.replace(/\\/videos$/i, '')\n  if (/^UC[\\w-]{20,}$/.test(withoutVideos)) {\n    return `https://www.youtube.com/channel/${withoutVideos}/videos`\n  }\n\n  const handle = withoutVideos.startsWith('@') ? withoutVideos : `@${withoutVideos}`\n  if (!/^@[\\w.-]+$/.test(handle)) {\n    throw new Error('channelHandle must be an @handle, UC channel ID, or YouTube channel URL')\n  }\n  return `https://www.youtube.com/${handle}/videos`\n}\n\nexport class BrowserDriver implements IBrowserDriver {\n  private browser: Browser | null = null\n  private context: BrowserContext | null = null\n  private page: Page | null = null\n  private kernelClient: Kernel | null = null\n  private kernelSessionId: string | null = null\n  private kernelOpenedAtMs: number | null = null\n  private kernelStealth: boolean | null = null\n  private kernelProxyUsed: boolean | null = null\n  private kernelProxySource: string | null = null\n  private kernelProxyType: string | null = null\n  private kernelHeadlessSent: boolean | null = null\n  private debugEnabled = false\n  private debugSnapshot: BrowserDebugSnapshot = {\n    kernel: null,\n    context: null,\n    networkLocation: null,\n    serpNavigation: null,\n  }\n\n  async launch(config: DriverConfig): Promise<void> {\n    this.debugEnabled = config.debug === true\n    const proxyMode: ProxyMode = config.proxyMode ?? (config.kernelProxyId ? 'configured' : 'none')\n    const device: SerpDevice = config.isMobile ? 'mobile' : 'desktop'\n    this.debugSnapshot = {\n      kernel: null,\n      context: {\n        viewport: config.viewport,\n        locale: config.locale,\n        device,\n        userAgent: null,\n        deviceScaleFactor: config.deviceScaleFactor ?? null,\n        isMobile: config.isMobile === true,\n        hasTouch: config.hasTouch === true,\n      },\n      networkLocation: null,\n      serpNavigation: null,\n    }\n\n    if (config.kernelApiKey) {\n      this.kernelClient = new Kernel({ apiKey: config.kernelApiKey })\n      const timeoutSeconds = positiveIntFromEnv('KERNEL_BROWSER_TIMEOUT_SECONDS', DEFAULT_KERNEL_BROWSER_TIMEOUT_SECONDS)\n      if (config.kernelProfileName && config.kernelProfileSaveChanges === true) {\n        await ensureKernelProfile(this.kernelClient, config.kernelProfileName)\n      }\n      const costCtx = currentCostContext()\n      let useHeadless: boolean\n      if (config.headlessMode === 'headful' || costCtx?.forceHeadful === true) useHeadless = false\n      else if (config.headlessMode === 'headless') useHeadless = true\n      else useHeadless = costCtx?.forceHeadless === true\n        || (process.env.KERNEL_HEADLESS_DISABLED !== 'true' && !!costCtx?.op && HEADLESS_OPS.has(costCtx.op))\n      this.kernelHeadlessSent = useHeadless ? true : null\n      if (costCtx?.headlessSentOut) costCtx.headlessSentOut.value = this.kernelHeadlessSent\n      const kernelBrowser = await this.kernelClient.browsers.create({\n        stealth: config.kernelStealth === true,\n        timeout_seconds: timeoutSeconds,\n        ...(useHeadless ? { headless: true } : {}),\n        ...(config.kernelProxyId ? { proxy_id: config.kernelProxyId } : {}),\n        ...(config.kernelProfileName\n          ? {\n              profile: {\n                name: config.kernelProfileName,\n                ...(typeof config.kernelProfileSaveChanges === 'boolean' ? { save_changes: config.kernelProfileSaveChanges } : {}),\n              },\n            }\n          : {}),\n      })\n      this.kernelSessionId = kernelBrowser.session_id\n      this.kernelOpenedAtMs = Date.now()\n      this.kernelStealth = typeof kernelBrowser.stealth === 'boolean' ? kernelBrowser.stealth : null\n      this.kernelProxyUsed = typeof kernelBrowser.proxy_id === 'string' ? Boolean(kernelBrowser.proxy_id) : Boolean(config.kernelProxyId)\n      this.kernelProxySource = config.kernelProxyResolution?.source ?? null\n      this.kernelProxyType = config.kernelProxyResolution?.proxyType ?? null\n      let defaultProxyDisabled: boolean | null = null\n      let defaultProxyDisableError: string | null = null\n      if (!config.kernelProxyId && !config.keepDefaultProxy) {\n        try {\n          await withTimeout(\n            this.kernelClient.browsers.update(this.kernelSessionId, { disable_default_proxy: true }),\n            5_000,\n            `Kernel session ${this.kernelSessionId} disable default proxy`,\n          )\n          defaultProxyDisabled = true\n        } catch (err) {\n          defaultProxyDisabled = false\n          defaultProxyDisableError = errorText(err)\n        }\n      }\n      const kernelDebug: KernelSessionDebug = {\n        sessionId: this.kernelSessionId,\n        proxyMode,\n        requestedProxyIdPresent: Boolean(config.kernelProxyId),\n        requestedProxyIdSuffix: proxyIdSuffix(config.kernelProxyId),\n        createdProxyIdPresent: typeof kernelBrowser.proxy_id === 'string' ? Boolean(kernelBrowser.proxy_id) : null,\n        createdProxyIdSuffix: proxyIdSuffix(kernelBrowser.proxy_id),\n        retrievedProxyIdPresent: null,\n        retrievedProxyIdSuffix: null,\n        retrievedProxyIdMatchesRequested: null,\n        defaultProxyDisabled,\n        defaultProxyDisableError,\n        proxyResolution: config.kernelProxyResolution ?? null,\n        timeoutSeconds,\n        stealth: typeof kernelBrowser.stealth === 'boolean' ? kernelBrowser.stealth : null,\n        profilePresent: null,\n        poolPresent: null,\n        retrieveError: null,\n      }\n      this.debugSnapshot.kernel = kernelDebug\n      console.info(JSON.stringify({\n        event: 'kernel_browser_created',\n        kernel_session_id: this.kernelSessionId,\n        timeout_seconds: timeoutSeconds,\n        proxy_mode: proxyMode,\n        proxy_id_present: Boolean(config.kernelProxyId),\n        profile_name_present: Boolean(config.kernelProfileName),\n        proxy_resolution_source: config.kernelProxyResolution?.source,\n      }))\n      if (this.debugEnabled) {\n        await this.populateKernelRetrieveDebug(kernelDebug, config.kernelProxyId)\n      }\n      this.browser = await playwrightChromium.connectOverCDP(kernelBrowser.cdp_ws_url)\n      const runtimeUserAgent = await detectRuntimeUserAgent(this.browser)\n      this.debugSnapshot.context!.userAgent = runtimeUserAgent\n      this.context = await this.browser.newContext(rankCheckContextOptions(config, runtimeUserAgent))\n      await this.installEsbuildHelperShims(this.context)\n      this.page = await this.context.newPage()\n      await this.page.setViewportSize(config.viewport)\n      if (this.debugEnabled || config.requireUsEgress) {\n        this.debugSnapshot.networkLocation = await this.captureBrowserNetworkLocation()\n      }\n      if (config.requireUsEgress) {\n        const country = this.debugSnapshot.networkLocation?.country ?? null\n        if (!isUsCountry(country)) {\n          throw new CaptchaError(`Non-US egress (${country ?? 'unknown'}) — retrying with a fresh session`)\n        }\n      }\n      return\n    }\n\n    const launchOpts = {\n      headless: config.headless,\n      proxy: config.proxy ? { server: config.proxy } : undefined,\n      ...(config.browserExecutablePath ? { executablePath: config.browserExecutablePath } : {}),\n      ...(config.ignoreDefaultArgs ? { ignoreDefaultArgs: config.ignoreDefaultArgs } : {}),\n    }\n    if (config.profileDir) {\n      this.context = await chromium.launchPersistentContext(config.profileDir, {\n        ...launchOpts,\n        ...rankCheckContextOptions(config),\n      })\n      await this.installEsbuildHelperShims(this.context)\n      this.page = await this.context.newPage()\n      this.debugSnapshot.context!.userAgent = await detectPageUserAgent(this.page)\n    } else {\n      this.browser = await chromium.launch(launchOpts)\n      const runtimeUserAgent = await detectRuntimeUserAgent(this.browser)\n      this.debugSnapshot.context!.userAgent = runtimeUserAgent\n      this.context = await this.browser.newContext(rankCheckContextOptions(config, runtimeUserAgent))\n      await this.installEsbuildHelperShims(this.context)\n      this.page = await this.context.newPage()\n    }\n    if (this.debugEnabled) {\n      this.debugSnapshot.networkLocation = await this.captureBrowserNetworkLocation()\n    }\n  }\n\n  private async populateKernelRetrieveDebug(kernelDebug: KernelSessionDebug, requestedProxyId: string | undefined): Promise<void> {\n    if (!this.kernelClient || !this.kernelSessionId) return\n    try {\n      const retrieved = await withTimeout(\n        this.kernelClient.browsers.retrieve(this.kernelSessionId),\n        5_000,\n        `Kernel session ${this.kernelSessionId} retrieve`,\n      ) as { proxy_id?: string; timeout_seconds?: number; stealth?: boolean; profile?: unknown; pool?: unknown }\n      kernelDebug.retrievedProxyIdPresent = typeof retrieved.proxy_id === 'string' ? Boolean(retrieved.proxy_id) : false\n      kernelDebug.retrievedProxyIdSuffix = proxyIdSuffix(retrieved.proxy_id)\n      kernelDebug.retrievedProxyIdMatchesRequested = requestedProxyId\n        ? retrieved.proxy_id === requestedProxyId\n        : !retrieved.proxy_id\n      kernelDebug.timeoutSeconds = typeof retrieved.timeout_seconds === 'number' ? retrieved.timeout_seconds : kernelDebug.timeoutSeconds\n      kernelDebug.stealth = typeof retrieved.stealth === 'boolean' ? retrieved.stealth : kernelDebug.stealth\n      kernelDebug.profilePresent = Boolean(retrieved.profile)\n      kernelDebug.poolPresent = Boolean(retrieved.pool)\n    } catch (err) {\n      kernelDebug.retrieveError = errorText(err)\n    }\n  }\n\n  private async captureBrowserNetworkLocation(): Promise<BrowserNetworkLocationDebug> {\n    const fallback = (message: string, source: BrowserNetworkLocationDebug['source'] = 'ipapi.co'): BrowserNetworkLocationDebug => ({\n      source,\n      ip: null,\n      city: null,\n      region: null,\n      country: null,\n      org: null,\n      timezone: null,\n      error: message,\n    })\n    if (!this.context) return fallback('browser context is not available')\n    let debugPage: Page | null = null\n    try {\n      debugPage = await this.context.newPage()\n      const ipwho = await this.loadJsonInDebugPage(debugPage, 'https://ipwho.is/')\n      if (ipwho) {\n        const connection = typeof ipwho.connection === 'object' && ipwho.connection !== null\n          ? ipwho.connection as Record<string, unknown>\n          : {}\n        return {\n          source: 'ipwho.is',\n          ip: typeof ipwho.ip === 'string' ? ipwho.ip : null,\n          city: typeof ipwho.city === 'string' ? ipwho.city : null,\n          region: typeof ipwho.region === 'string' ? ipwho.region : null,\n          country: typeof ipwho.country === 'string' ? ipwho.country : null,\n          org: typeof connection.org === 'string' ? connection.org : null,\n          timezone: typeof ipwho.timezone === 'object' && ipwho.timezone !== null && typeof (ipwho.timezone as Record<string, unknown>).id === 'string'\n            ? (ipwho.timezone as Record<string, unknown>).id as string\n            : null,\n          error: null,\n        }\n      }\n\n      const ipify = await this.loadJsonInDebugPage(debugPage, 'https://api64.ipify.org?format=json')\n      if (ipify) {\n        return {\n          source: 'api64.ipify.org',\n          ip: typeof ipify.ip === 'string' ? ipify.ip : null,\n          city: null,\n          region: null,\n          country: null,\n          org: null,\n          timezone: null,\n          error: null,\n        }\n      }\n\n      await withTimeout(\n        debugPage.goto('https://ipapi.co/json/', { waitUntil: 'domcontentloaded', timeout: 7_000 }),\n        8_000,\n        'browser network location navigation',\n      )\n      const body = await debugPage.locator('body').innerText({ timeout: 2_000 })\n      const data = JSON.parse(body) as Record<string, unknown>\n      return {\n        source: 'ipapi.co',\n        ip: typeof data.ip === 'string' ? data.ip : null,\n        city: typeof data.city === 'string' ? data.city : null,\n        region: typeof data.region === 'string' ? data.region : null,\n        country: typeof data.country_name === 'string' ? data.country_name : typeof data.country === 'string' ? data.country : null,\n        org: typeof data.org === 'string' ? data.org : null,\n        timezone: typeof data.timezone === 'string' ? data.timezone : null,\n        error: null,\n      }\n    } catch (err) {\n      return fallback(errorText(err))\n    } finally {\n      await debugPage?.close().catch(() => {})\n    }\n  }\n\n  private async loadJsonInDebugPage(debugPage: Page, url: string): Promise<Record<string, unknown> | null> {\n    try {\n      await withTimeout(\n        debugPage.goto(url, { waitUntil: 'domcontentloaded', timeout: 7_000 }),\n        8_000,\n        `browser network location navigation ${url}`,\n      )\n      const body = await debugPage.locator('body').innerText({ timeout: 2_000 })\n      return JSON.parse(body) as Record<string, unknown>\n    } catch {\n      return null\n    }\n  }\n\n  private async installEsbuildHelperShims(context: BrowserContext): Promise<void> {\n    await context.addInitScript(() => {\n      const g = globalThis as Record<string, unknown>\n      if (typeof g.__name !== 'function')        g.__name        = (fn: unknown) => fn\n      if (typeof g.__publicField !== 'function') g.__publicField = (obj: Record<string, unknown>, key: string, value: unknown) => { obj[key] = value; return value }\n    })\n  }\n\n  async navigateToSERP(\n    query: string,\n    uule: UuleString | null,\n    gl: string,\n    hl: string,\n    options?: { num?: number; debug?: boolean; tbs?: string },\n  ): Promise<{ hasPaa: boolean }> {\n    const params = new URLSearchParams({ q: query, gl, hl, pws: '0' })\n    if (options?.num) params.set('num', String(options.num))\n    if (options?.tbs) params.set('tbs', options.tbs)\n    if (uule) params.set('uule', uule)\n    const url = 'https://www.google.com/search?' + params.toString()\n    const navUrl = serpArrivalUrl(url)\n    const navDebug: SerpNavigationDebug | null = options?.debug\n      ? {\n          requestedUrl: url,\n          finalUrl: null,\n          title: null,\n          bodySnippet: null,\n          hasPaa: null,\n          captchaDetected: null,\n          googleSorryUrl: null,\n          redirected: null,\n        }\n      : null\n    if (navDebug) this.debugSnapshot.serpNavigation = navDebug\n\n    try {\n      await this.page!.goto(navUrl, { waitUntil: 'domcontentloaded', timeout: 45_000 })\n    } catch (err) {\n      await this.updateSerpNavigationDebug(navDebug, url, { hasPaa: null, captchaDetected: null })\n      const diag = await this.captureDiagnostics(url)\n      throw new ExtractionError(`page.goto failed: ${(err as Error).message} | ${diag}`)\n    }\n\n    if (await this.isSerpChallengeVisible()) {\n      await this.updateSerpNavigationDebug(navDebug, url, { hasPaa: false, captchaDetected: true })\n      throw new CaptchaError(this.captchaMessage())\n    }\n\n    const fastFound = await this.page!.waitForSelector(PAASelectors.item, { timeout: 4_000 }).catch(() => null)\n    if (fastFound) {\n      await this.updateSerpNavigationDebug(navDebug, url, { hasPaa: true, captchaDetected: false })\n      return { hasPaa: true }\n    }\n\n    if (await this.isSerpChallengeVisible()) {\n      await this.updateSerpNavigationDebug(navDebug, url, { hasPaa: false, captchaDetected: true })\n      throw new CaptchaError(this.captchaMessage())\n    }\n\n    for (let i = 1; i <= 6; i++) {\n      await this.page!.evaluate((f: number) => { window.scrollTo(0, document.body.scrollHeight * f) }, i / 6)\n      await this.page!.waitForTimeout(600)\n      const count = await this.page!.locator(PAASelectors.item).count()\n      if (count > 0) {\n        await this.updateSerpNavigationDebug(navDebug, url, { hasPaa: true, captchaDetected: false })\n        return { hasPaa: true }\n      }\n    }\n\n    await this.updateSerpNavigationDebug(navDebug, url, { hasPaa: false, captchaDetected: false })\n    return { hasPaa: false }\n  }\n\n  private async isSerpChallengeVisible(): Promise<boolean> {\n    if (!this.page) return false\n    const captchaCount = await this.page.locator(PAASelectors.captchaMarker).count().catch(() => 0)\n    if (captchaCount > 0) return true\n    const finalUrl = this.page.url()\n    if (/google\\.[^/]+\\/sorry\\//i.test(finalUrl)) return true\n    const bodySnippet = await this.page.evaluate(() => {\n      const text = (document.body?.innerText ?? '').replace(/\\s+/g, ' ').trim()\n      return text.slice(0, 1000)\n    }).catch(() => '')\n    return /recaptcha|unusual traffic|are you a robot|about this page|detected unusual traffic/i.test(bodySnippet)\n  }\n\n  private async updateSerpNavigationDebug(\n    navDebug: SerpNavigationDebug | null,\n    requestedUrl: string,\n    state: { hasPaa: boolean | null; captchaDetected: boolean | null },\n  ): Promise<void> {\n    if (!navDebug || !this.page) return\n    try {\n      const finalUrl = this.page.url()\n      const title = await this.page.title().catch(() => '')\n      const bodySnippet = await this.page.evaluate(() => {\n        const text = (document.body?.innerText ?? '').replace(/\\s+/g, ' ').trim()\n        return text.slice(0, 500)\n      }).catch(() => '')\n      const textCaptcha = /recaptcha|unusual traffic|are you a robot/i.test(bodySnippet)\n      navDebug.finalUrl = finalUrl\n      navDebug.title = title\n      navDebug.bodySnippet = bodySnippet\n      navDebug.hasPaa = state.hasPaa\n      navDebug.captchaDetected = state.captchaDetected ?? textCaptcha\n      navDebug.googleSorryUrl = /google\\.[^/]+\\/sorry\\//i.test(finalUrl)\n      navDebug.redirected = finalUrl !== requestedUrl\n    } catch (err) {\n      navDebug.bodySnippet = `debug capture failed: ${errorText(err)}`\n    }\n  }\n\n  private async captureDiagnostics(intendedUrl: string): Promise<string> {\n    try {\n      const finalUrl = this.page!.url()\n      const title = await this.page!.title().catch(() => '')\n      const bodySnippet = await this.page!.evaluate(() => {\n        const t = (document.body?.innerText ?? '').replace(/\\s+/g, ' ').trim()\n        return t.slice(0, 400)\n      }).catch(() => '')\n      const consent = /consent\\.google\\./.test(finalUrl) || /before you continue/i.test(bodySnippet)\n      const recaptcha = /recaptcha|unusual traffic|are you a robot/i.test(bodySnippet)\n      const flags = [\n        consent ? 'CONSENT_WALL' : '',\n        recaptcha ? 'BOT_CHALLENGE' : '',\n        finalUrl !== intendedUrl ? 'REDIRECTED' : '',\n      ].filter(Boolean).join(',')\n      return `intended=${intendedUrl} | final=${finalUrl} | title=\"${title}\" | flags=[${flags}] | body=\"${bodySnippet}\"`\n    } catch (e) {\n      return `diagnostics-failed: ${(e as Error).message}`\n    }\n  }\n\n  private captchaMessage(): string {\n    return this.kernelClient\n      ? 'Google returned a CAPTCHA on this session — retrying with a fresh session.'\n      : RECAPTCHA_INSTRUCTIONS\n  }\n\n  async navigateTo(url: string): Promise<void> {\n    const arrivalBase = process.env.ADS_ARRIVAL_REDIRECT_BASE?.trim()\n    const navUrl = arrivalBase ? serpArrivalUrl(url, arrivalBase) : url\n    try {\n      await this.page!.goto(navUrl, { waitUntil: 'domcontentloaded', timeout: 45_000 })\n      if (/google\\.[^/]+\\/search\\?/i.test(url) && await this.isSerpChallengeVisible()) {\n        throw new CaptchaError(this.captchaMessage())\n      }\n    } catch (err) {\n      if (err instanceof CaptchaError) throw err\n      const diag = await this.captureDiagnostics(url)\n      throw new ExtractionError(`page.goto failed: ${(err as Error).message} | ${diag}`)\n    }\n  }\n\n  async navigateToChannel(channelHandle: string): Promise<void> {\n    const url = buildYouTubeChannelVideosUrl(channelHandle)\n    try {\n      await this.page!.goto(url, { waitUntil: 'networkidle', timeout: 30_000 })\n    } catch (err) {\n      const diag = await this.captureDiagnostics(url)\n      throw new ExtractionError(`navigateToChannel failed: ${(err as Error).message} | ${diag}`)\n    }\n  }\n\n  async evaluate<T>(fn: (...args: unknown[]) => T | Promise<T>, arg?: unknown): Promise<T> {\n    return (this.page! as { evaluate(fn: unknown, arg?: unknown): Promise<T> }).evaluate(fn, arg)\n  }\n\n  getPage(): Page {\n    return this.page!\n  }\n\n  getKernelSessionId(): string | null {\n    return this.kernelSessionId\n  }\n\n  getDebugSnapshot(): BrowserDebugSnapshot {\n    return this.debugSnapshot\n  }\n\n  async close(): Promise<BrowserCloseResult> {\n    if (this.browser) {\n      const b = this.browser\n      const sessionId = this.kernelSessionId\n      const client = this.kernelClient\n      const openedAtMs = this.kernelOpenedAtMs\n      const stealth = this.kernelStealth\n      const proxyUsed = this.kernelProxyUsed\n      const proxySource = this.kernelProxySource\n      const proxyType = this.kernelProxyType\n      const headlessSent = this.kernelHeadlessSent\n      this.browser = null\n      this.context = null\n      this.page = null\n      this.kernelSessionId = null\n      this.kernelClient = null\n      this.kernelOpenedAtMs = null\n      this.kernelHeadlessSent = null\n      this.kernelProxySource = null\n      this.kernelProxyType = null\n      if (client && sessionId && openedAtMs != null) {\n        void recordKernelSession({ kernelSessionId: sessionId, source: 'browser_driver', stealth, headlessSent, proxyUsed, proxySource, proxyType, openedAtMs, closedAtMs: Date.now() })\n      }\n      if (client && sessionId) {\n        console.info(JSON.stringify({\n          event: 'kernel_browser_delete_started',\n          kernel_session_id: sessionId,\n        }))\n        const deleteSession = withTimeout(\n          client.browsers.deleteByID(sessionId),\n          KERNEL_SESSION_DELETE_TIMEOUT_MS,\n          `Kernel session ${sessionId} delete`,\n        )\n        const closeBrowser = withTimeout(\n          b.close(),\n          KERNEL_BROWSER_CLOSE_TIMEOUT_MS,\n          `Kernel browser ${sessionId} close`,\n        )\n        const [deleteResult, closeResult] = await Promise.allSettled([deleteSession, closeBrowser])\n        const result: BrowserCloseResult = {\n          kernelSessionId: sessionId,\n          kernelDeleteStarted: true,\n          kernelDeleteSucceeded: deleteResult.status === 'fulfilled',\n          kernelDeleteError: deleteResult.status === 'rejected'\n            ? (deleteResult.reason instanceof Error ? deleteResult.reason.message : String(deleteResult.reason))\n            : null,\n          browserCloseSucceeded: closeResult.status === 'fulfilled',\n          browserCloseError: closeResult.status === 'rejected'\n            ? (closeResult.reason instanceof Error ? closeResult.reason.message : String(closeResult.reason))\n            : null,\n        }\n        if (deleteResult.status === 'rejected') {\n          console.warn(JSON.stringify({\n            event: 'kernel_browser_delete_failed',\n            kernel_session_id: sessionId,\n            message: result.kernelDeleteError,\n          }))\n          console.warn(`Kernel session cleanup failed for ${sessionId}:`, deleteResult.reason)\n        } else {\n          console.info(JSON.stringify({\n            event: 'kernel_browser_delete_succeeded',\n            kernel_session_id: sessionId,\n          }))\n        }\n        if (closeResult.status === 'rejected') {\n          console.warn(JSON.stringify({\n            event: 'kernel_browser_close_failed',\n            kernel_session_id: sessionId,\n            message: result.browserCloseError,\n          }))\n          console.warn(`Kernel browser close failed for ${sessionId}:`, closeResult.reason)\n        }\n        return result\n      }\n      await b.close()\n      return {\n        kernelSessionId: null,\n        kernelDeleteStarted: false,\n        kernelDeleteSucceeded: null,\n        kernelDeleteError: null,\n        browserCloseSucceeded: true,\n        browserCloseError: null,\n      }\n    } else if (this.context) {\n      const ctx = this.context\n      this.context = null\n      this.page = null\n      await ctx.close()\n      return {\n        kernelSessionId: null,\n        kernelDeleteStarted: false,\n        kernelDeleteSucceeded: null,\n        kernelDeleteError: null,\n        browserCloseSucceeded: true,\n        browserCloseError: null,\n      }\n    }\n    return {\n      kernelSessionId: null,\n      kernelDeleteStarted: false,\n      kernelDeleteSucceeded: null,\n      kernelDeleteError: null,\n      browserCloseSucceeded: null,\n      browserCloseError: null,\n    }\n  }\n}\n","export const PAASelectors = {\n  container:       '.eJH8qe.adDDi',\n  dataInitq:       '[data-initq]',\n  item:            '.related-question-pair',\n  itemDataQ:       'data-q',\n  itemDataInitQ:   'data-initq',\n  itemQuestionEl:  '.JlqpRe',\n  answerContainer: '.bCOlv, .hgKElc, .wDYxhc, .LGOjhe, .fo7IQd, .fmW3u',\n  aiAnswerBody:    '.pWvJNd',\n  aiCitation:      'a.PMDqCb[href]',\n  aiSourceCard:    'a.vIWmYe[href], a.Zbfntb[href]',\n  sourceTitle:     'h3',\n  sourceSite:      '.VuuXrf',\n  sourceCite:      'cite',\n  clickTarget:     '.JlqpRe',\n  expandedClass:   'aoRk1c',\n  captchaMarker:   '#captcha-form, #recaptcha, form[action*=\"/sorry/\"], .g-recaptcha, [data-sitekey]',\n} as const\n\nexport const VideoSelectors = {\n  container:        'div[jscontroller=\"HWk0Gf\"]',\n  sectionHeading:   '.mgAbYb[role=\"heading\"]',\n  item:             'a.rIRoqf',\n} as const\n\nexport const ShortVideoSelectors = {\n  udm:              '39',\n  item:             'a.rIRoqf',\n  durationPattern:  /^\\d+:\\d+$/,\n  platforms:        ['YouTube', 'TikTok', 'Instagram', 'Facebook', 'X'],\n} as const\n\nexport const ForumSelectors = {\n  section:  '.ULSxyf',\n  item:     'a.KYg7td.INpicf',\n  title:    '.hyYc0c',\n  source:   '.K4ETW',\n} as const\n\nexport const WhatPeopleSayingSelectors = {\n  sectionTag:          'g-section-with-header',\n  sectionHeadingText:  'What people are saying',\n  card:                '.dRzkFf[role=\"listitem\"]',\n  cardLink:            'a.WlydOe[jsname=\"YKoRaf\"]',\n  titleH1:             'h1.WQWxe',\n  titleDiv:            '.eAaXgc',\n  popularCommentLabel: '.qgdis',\n  source:              '.sTl1Td',\n  platformBadge:       '.appd0, .KrMNbf',\n  ytChannel:           '.sjVJQd',\n  ytDate:              '.PLq9Je',\n  authorNote:          '.nDgy9d',\n} as const\n\nexport const AIOverviewSelectors = {\n  root:           '[data-lhcontainer][data-streaming-container][eid]',\n  legacyRoot:     '[data-hveid=\"CBMQAA\"]',\n  wrapper:        '.Fgyi2e',\n  controller:     '[jscontroller=\"AkrxPe\"]',\n  contentSubtree: '[data-subtree=\"mfc\"]',\n  header:         '.heWuVc',\n  heading:        '.Fzsovc.cwYVJe.RJPOee',\n  showMoreButton: '[aria-label=\"Show more AI Overview\"]',\n  sourcesPanel:   '.OZ9ddf.WAUd4',\n  disclaimer:     '.DuQANe.MSJHRb',\n} as const\n\nexport const AIModeSelectors = {\n  root:      '[data-hveid=\"CAUQAA\"]',\n  wrapper:   '.Fgyi2e',\n  citations: '.Fgyi2e [data-hveid] a[jsname=\"pxBnId\"]',\n} as const\n\nexport const OrganicSelectors = {\n  result:       '.wHYlTd.tF2Cxc',\n  title:        'h3.LC20lb',\n  siteName:     '.VuuXrf',\n  cite:         'cite.tjvcx',\n  snippet:      '.VwiC3b',\n  redditCite:   'cite.qLRx3b',\n  ratingWrap:   '.Y0A0hc',\n  ratingValue:  '.yi40Hd',\n  reviewCount:  '.RDApEe',\n} as const\n\nexport const LocalPackSelectors = {\n  headingText:  'Businesses',\n  card:         '.w7Dbne',\n  name:         '.OSrXXb',\n  ratingValue:  '.yi40Hd',\n  reviewCount:  '.RDApEe',\n} as const\n\nexport const SELECTORS_VERSION = '2026-05-07'\n\nexport const MapsSelectors = {\n  ratingAndCount:       'div.F7nice',\n  hoursTable:           'table.eK4R0e',\n  reviewScrollPane:         'div.m6QErb[tabindex=\"-1\"]',\n  reviewScrollPaneFallback: '[role=\"main\"] div[tabindex=\"-1\"]',\n  reviewCardAuthor:     'div.d4r55, span.d4r55, span.RPZfBb',\n  reviewCardDate:       'span.rsqaWe',\n  reviewCardText:       'span.wiI7pd',\n  reviewCardOwnerBlock: 'div.CDe7pd',\n  hoursTableAlt:        'table[aria-label*=\"Hour\"]',\n  reviewCard:           '[data-review-id]',\n  reviewStars:          '[role=\"img\"][aria-label*=\"star\"]',\n  reviewTab:            'button[role=\"tab\"][aria-label*=\"Review\"]',\n  aboutTab:             'button[role=\"tab\"][aria-label*=\"About\"]',\n  expandReview:         '[data-review-id] button[aria-label*=\"See more\"], [data-review-id] button.w8nwRe',\n} as const\n\nexport const MAPS_SELECTORS_VERSION = '2026-05-23'\n","export const RECAPTCHA_INSTRUCTIONS = 'Google returned a CAPTCHA. Run with --headless=false to re-warm the browser profile, then retry.'\nexport const PUBLIC_SERVICE_UNAVAILABLE_MESSAGE = 'This operation is temporarily unavailable. Please retry later.'\n\nconst PUBLIC_ERROR_CODES = new Set([\n  'request_aborted',\n  'captcha_exhausted',\n  'captcha_or_blocked',\n  'location_mismatch',\n  'proxy_tunnel_failed',\n  'proxy_unavailable',\n  'harvest_timeout',\n  'mcp_request_timeout',\n  'service_unavailable',\n])\n\nexport function normalizePublicErrorCode(code: unknown): string {\n  return typeof code === 'string' && PUBLIC_ERROR_CODES.has(code)\n    ? code\n    : 'service_unavailable'\n}\n\nexport function publicErrorMessage(code: unknown): string {\n  switch (normalizePublicErrorCode(code)) {\n    case 'request_aborted':\n      return 'The request was cancelled before it completed.'\n    case 'captcha_exhausted':\n    case 'captcha_or_blocked':\n      return 'The target site returned a verification challenge. Please retry later.'\n    case 'location_mismatch':\n      return 'The target site returned results for a different location. Please retry.'\n    case 'proxy_tunnel_failed':\n    case 'proxy_unavailable':\n      return 'The web request could not establish a working connection. Please retry later.'\n    case 'harvest_timeout':\n    case 'mcp_request_timeout':\n      return 'The request timed out before it completed. Please retry with a smaller request.'\n    default:\n      return PUBLIC_SERVICE_UNAVAILABLE_MESSAGE\n  }\n}\n\nexport function sanitizeVendorName(message: string): string {\n  return message\n    .replace(/kernel\\.sh\\s+sessions?/gi, 'sessions')\n    .replace(/kernel\\.sh\\s+session/gi, 'this session')\n    .replace(/kernel\\.sh/gi, 'the service')\n    .replace(/kernel\\s+sessions?/gi, 'sessions')\n    .replace(/kernel\\s+session/gi, 'this session')\n    .replace(/\\bkernel\\b/gi, 'the service')\n    .replace(/  +/g, ' ')\n    .trim()\n}\n\nexport class CaptchaError extends Error {\n  readonly name = 'CaptchaError'\n  constructor(public readonly instructions: string) {\n    super(`CAPTCHA detected. ${instructions}`)\n  }\n}\n\nexport class ExtractionError extends Error {\n  readonly name = 'ExtractionError'\n  constructor(message: string, public readonly cause?: unknown) {\n    super(message)\n  }\n}\n\n/**\n * Google Maps sometimes finishes navigation without rendering either result\n * cards or its explicit no-results message. Treating that page as a genuine\n * empty search loses data, so callers should retry it with a fresh browser (and\n * a fresh proxy when location rotation is enabled).\n */\nexport class MapsSearchIncompletePageError extends Error {\n  readonly name = 'MapsSearchIncompletePageError'\n\n  constructor(public readonly pageState: string) {\n    super(`Google Maps search did not reach a complete result state (${pageState})`)\n  }\n}\n\nexport class RequestAbortedError extends Error {\n  readonly name = 'RequestAbortedError'\n  constructor(message = 'Request aborted before harvest completed') {\n    super(message)\n  }\n}\n\nexport class LocationMismatchError extends Error {\n  readonly name = 'LocationMismatchError'\n  constructor(message = 'Google returned results for a different location than requested') {\n    super(message)\n  }\n}\n","import { randomUUID } from 'node:crypto'\nimport { getDb } from './db.js'\nimport { currentCostContext } from './cost-context.js'\nimport { kernelCostUsd, vendorCostUsd } from './cost-rates.js'\n\nlet _ready = false\nlet _migrationPromise: Promise<void> | null = null\n\nexport async function migrateCostTelemetry(): Promise<void> {\n  if (_ready) return\n  if (_migrationPromise) return _migrationPromise\n  _migrationPromise = runCostTelemetryMigration().finally(() => {\n    _migrationPromise = null\n  })\n  return _migrationPromise\n}\n\nasync function runCostTelemetryMigration(): Promise<void> {\n  const db = getDb()\n  const existing = await db.execute(`\n    SELECT\n      (SELECT COUNT(*) FROM sqlite_master WHERE type = 'table' AND name IN ('kernel_session_log', 'vendor_usage_log', 'cost_probe_runs')) = 3\n      AND (SELECT COUNT(*) FROM pragma_table_info('kernel_session_log') WHERE name IN ('proxy_source', 'proxy_type', 'method')) = 3\n      AND (SELECT COUNT(*) FROM pragma_table_info('vendor_usage_log') WHERE name = 'method') = 1\n      AND (SELECT COUNT(*) FROM pragma_table_info('cost_probe_runs') WHERE name IN ('units', 'unit_type', 'mode')) = 3\n      AS ready\n  `)\n  if (Number(existing.rows[0]?.ready ?? 0) === 1) {\n    _ready = true\n    return\n  }\n\n  await db.execute(`\n    CREATE TABLE IF NOT EXISTS kernel_session_log (\n      id                    TEXT PRIMARY KEY,\n      kernel_session_id     TEXT,\n      op                    TEXT,\n      source                TEXT NOT NULL,\n      probe_run_id          TEXT,\n      user_id               INTEGER,\n      stealth               INTEGER,\n      headless_sent         INTEGER,\n      proxy_used            INTEGER,\n      fallback              INTEGER NOT NULL DEFAULT 0,\n      opened_at             TEXT,\n      closed_at             TEXT,\n      duration_ms           INTEGER NOT NULL DEFAULT 0,\n      est_cost_usd_headless REAL NOT NULL DEFAULT 0,\n      est_cost_usd_headful  REAL NOT NULL DEFAULT 0,\n      error                 TEXT,\n      created_at            TEXT NOT NULL DEFAULT (datetime('now'))\n    )\n  `)\n  await db.execute(`CREATE INDEX IF NOT EXISTS kernel_session_log_op ON kernel_session_log(op)`)\n  await db.execute(`CREATE INDEX IF NOT EXISTS kernel_session_log_probe ON kernel_session_log(probe_run_id)`)\n  await db.execute(`CREATE INDEX IF NOT EXISTS kernel_session_log_created ON kernel_session_log(created_at)`)\n  try { await db.execute(`ALTER TABLE kernel_session_log ADD COLUMN proxy_source TEXT`) } catch { void 0 }\n  try { await db.execute(`ALTER TABLE kernel_session_log ADD COLUMN proxy_type TEXT`) } catch { void 0 }\n  try { await db.execute(`ALTER TABLE kernel_session_log ADD COLUMN method TEXT`) } catch { void 0 }\n  await db.execute(`\n    CREATE TABLE IF NOT EXISTS vendor_usage_log (\n      id           TEXT PRIMARY KEY,\n      op           TEXT,\n      probe_run_id TEXT,\n      user_id      INTEGER,\n      vendor       TEXT NOT NULL,\n      model        TEXT,\n      units        REAL NOT NULL DEFAULT 0,\n      unit_type    TEXT,\n      est_cost_usd REAL NOT NULL DEFAULT 0,\n      error        TEXT,\n      created_at   TEXT NOT NULL DEFAULT (datetime('now'))\n    )\n  `)\n  await db.execute(`CREATE INDEX IF NOT EXISTS vendor_usage_log_op ON vendor_usage_log(op)`)\n  await db.execute(`CREATE INDEX IF NOT EXISTS vendor_usage_log_probe ON vendor_usage_log(probe_run_id)`)\n  await db.execute(`CREATE INDEX IF NOT EXISTS vendor_usage_log_created ON vendor_usage_log(created_at)`)\n  try { await db.execute(`ALTER TABLE vendor_usage_log ADD COLUMN method TEXT`) } catch { void 0 }\n  await db.execute(`\n    CREATE TABLE IF NOT EXISTS cost_probe_runs (\n      id                            TEXT PRIMARY KEY,\n      tool                          TEXT NOT NULL,\n      target                        TEXT,\n      started_at                    TEXT NOT NULL DEFAULT (datetime('now')),\n      wall_ms                       INTEGER NOT NULL DEFAULT 0,\n      success                       INTEGER NOT NULL DEFAULT 0,\n      error                         TEXT,\n      http_only                     INTEGER NOT NULL DEFAULT 0,\n      kernel_used                   INTEGER NOT NULL DEFAULT 0,\n      kernel_sessions               INTEGER NOT NULL DEFAULT 0,\n      kernel_seconds_total          REAL NOT NULL DEFAULT 0,\n      kernel_tier_observed          TEXT,\n      est_kernel_cost_usd_headless  REAL NOT NULL DEFAULT 0,\n      est_kernel_cost_usd_headful   REAL NOT NULL DEFAULT 0,\n      vendor_cost_usd               REAL NOT NULL DEFAULT 0,\n      charged_credits               REAL NOT NULL DEFAULT 0,\n      margin_usd_headless_starter   REAL NOT NULL DEFAULT 0,\n      margin_usd_headful_starter    REAL NOT NULL DEFAULT 0,\n      notes                         TEXT\n    )\n  `)\n  await db.execute(`CREATE INDEX IF NOT EXISTS cost_probe_runs_tool ON cost_probe_runs(tool)`)\n  try { await db.execute(`ALTER TABLE cost_probe_runs ADD COLUMN units REAL`) } catch { void 0 }\n  try { await db.execute(`ALTER TABLE cost_probe_runs ADD COLUMN unit_type TEXT`) } catch { void 0 }\n  try { await db.execute(`ALTER TABLE cost_probe_runs ADD COLUMN mode TEXT`) } catch { void 0 }\n  _ready = true\n}\n\nexport interface KernelSessionRecord {\n  kernelSessionId?: string | null\n  source: string\n  stealth?: boolean | null\n  headlessSent?: boolean | null\n  proxyUsed?: boolean | null\n  proxySource?: string | null\n  proxyType?: string | null\n  fallback?: boolean\n  openedAtMs: number\n  closedAtMs: number\n  error?: string | null\n}\n\nexport async function recordKernelSession(r: KernelSessionRecord): Promise<void> {\n  try {\n    await migrateCostTelemetry()\n    const ctx = currentCostContext()\n    const durationMs = Math.max(0, r.closedAtMs - r.openedAtMs)\n    const db = getDb()\n    await db.execute({\n      sql: `INSERT INTO kernel_session_log\n        (id, kernel_session_id, op, source, probe_run_id, user_id, stealth, headless_sent, proxy_used, proxy_source, proxy_type, fallback, opened_at, closed_at, duration_ms, est_cost_usd_headless, est_cost_usd_headful, error, method)\n        VALUES (?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?)`,\n      args: [\n        randomUUID(),\n        r.kernelSessionId ?? null,\n        ctx?.op ?? null,\n        r.source,\n        ctx?.probeRunId ?? null,\n        ctx?.userId ?? null,\n        boolToInt(r.stealth),\n        boolToInt(r.headlessSent),\n        boolToInt(r.proxyUsed),\n        r.proxySource ?? null,\n        r.proxyType ?? null,\n        r.fallback ? 1 : 0,\n        new Date(r.openedAtMs).toISOString(),\n        new Date(r.closedAtMs).toISOString(),\n        durationMs,\n        kernelCostUsd(durationMs, false),\n        kernelCostUsd(durationMs, true),\n        r.error ?? null,\n        ctx?.subOp ?? null,\n      ],\n    })\n  } catch (err) {\n    console.warn('[cost-telemetry] recordKernelSession failed:', err instanceof Error ? err.message : String(err))\n  }\n}\n\nexport interface VendorUsageRecord {\n  vendor: string\n  model?: string | null\n  units: number\n  unitType: string\n  error?: string | null\n}\n\nexport async function recordVendorUsage(r: VendorUsageRecord): Promise<void> {\n  try {\n    await migrateCostTelemetry()\n    const ctx = currentCostContext()\n    const db = getDb()\n    await db.execute({\n      sql: `INSERT INTO vendor_usage_log\n        (id, op, probe_run_id, user_id, vendor, model, units, unit_type, est_cost_usd, error, method)\n        VALUES (?,?,?,?,?,?,?,?,?,?,?)`,\n      args: [\n        randomUUID(),\n        ctx?.op ?? null,\n        ctx?.probeRunId ?? null,\n        ctx?.userId ?? null,\n        r.vendor,\n        r.model ?? null,\n        r.units,\n        r.unitType,\n        vendorCostUsd(r.vendor, r.units),\n        r.error ?? null,\n        ctx?.subOp ?? null,\n      ],\n    })\n  } catch (err) {\n    console.warn('[cost-telemetry] recordVendorUsage failed:', err instanceof Error ? err.message : String(err))\n  }\n}\n\nexport interface ProbeRunRecord {\n  id: string\n  tool: string\n  target?: string | null\n  wallMs: number\n  success: boolean\n  error?: string | null\n  httpOnly: boolean\n  kernelUsed: boolean\n  kernelSessions: number\n  kernelSecondsTotal: number\n  kernelTierObserved?: string | null\n  estKernelCostUsdHeadless: number\n  estKernelCostUsdHeadful: number\n  vendorCostUsd: number\n  chargedCredits: number\n  marginUsdHeadlessStarter: number\n  marginUsdHeadfulStarter: number\n  units?: number | null\n  unitType?: string | null\n  mode?: string | null\n  notes?: string | null\n}\n\nexport async function recordProbeRun(r: ProbeRunRecord): Promise<void> {\n  await migrateCostTelemetry()\n  const db = getDb()\n  await db.execute({\n    sql: `INSERT INTO cost_probe_runs\n      (id, tool, target, wall_ms, success, error, http_only, kernel_used, kernel_sessions, kernel_seconds_total, kernel_tier_observed, est_kernel_cost_usd_headless, est_kernel_cost_usd_headful, vendor_cost_usd, charged_credits, margin_usd_headless_starter, margin_usd_headful_starter, units, unit_type, mode, notes)\n      VALUES (?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?)`,\n    args: [\n      r.id, r.tool, r.target ?? null, r.wallMs, r.success ? 1 : 0, r.error ?? null,\n      r.httpOnly ? 1 : 0, r.kernelUsed ? 1 : 0, r.kernelSessions, r.kernelSecondsTotal,\n      r.kernelTierObserved ?? null, r.estKernelCostUsdHeadless, r.estKernelCostUsdHeadful,\n      r.vendorCostUsd, r.chargedCredits, r.marginUsdHeadlessStarter, r.marginUsdHeadfulStarter,\n      r.units ?? null, r.unitType ?? null, r.mode ?? null, r.notes ?? null,\n    ],\n  })\n}\n\nfunction boolToInt(v: boolean | null | undefined): number | null {\n  if (v === null || v === undefined) return null\n  return v ? 1 : 0\n}\n","import { createClient, type Client } from '@libsql/client/http'\nimport { createHash, randomBytes, randomUUID, scryptSync, timingSafeEqual } from 'node:crypto'\nimport { z } from 'zod'\nimport { CREDIT_LOTS_EPOCH } from './credit-lot-integrity.js'\n\nexport { CREDIT_LOTS_EPOCH } from './credit-lot-integrity.js'\n\nconst DB_URL = process.env.TURSO_DATABASE_URL ?? 'file:./paa-api.db'\nconst DB_TOKEN = process.env.TURSO_AUTH_TOKEN\n\nlet _db: Client | null = null\nlet _rateLimitSchemaReady = false\n\nexport const CORE_SCHEMA_VERSION = '2026-07-30.1'\nexport const SITE_EXTRACT_COUNTER_REPAIR_VERSION = 'data-2026-07-28-site-extract-counters.1'\nconst CORE_SCHEMA_TABLES = [\n  'admin_credit_adjustment_events',\n  'admin_credit_adjustments',\n  'billing_authorizations',\n  'billing_credits',\n  'billing_debits',\n  'billing_debit_allocations',\n  'billing_events',\n  'browser_profile_labels',\n  'connected_account_billing',\n  'concurrency_locks',\n  'credit_lot_expirations',\n  'credit_lots',\n  'credit_lot_repairs',\n  'free_credit_refreshes',\n  'harvest_attempts',\n  'inbox_messages',\n  'jobs',\n  'kpo_jobs',\n  'kpo_phase_log',\n  'ledger',\n  'oauth_clients',\n  'oauth_codes',\n  'oauth_tokens',\n  'page_snapshots',\n  'password_reset_tokens',\n  'rate_limits',\n  'request_events',\n  'scheduled_run_view_links',\n  'site_audit_jobs',\n  'site_audit_phase_log',\n  'site_extract_jobs',\n  'site_extract_pages',\n  'stripe_events',\n  'users',\n  'workflow_artifacts',\n  'workflow_runs',\n  'workflow_schedules',\n  'workflow_webhook_deliveries',\n  'workshop_registrations',\n] as const\n\nconst CORE_SCHEMA_INDEXES = [\n  'admin_credit_adjustment_events_reference',\n  'admin_credit_adjustments_user_created',\n  'billing_authorizations_user_status',\n  'billing_credits_user_status',\n  'billing_debits_user_status',\n  'billing_events_authorization',\n  'billing_events_user_created_at',\n  'concurrency_locks_active_user',\n  'concurrency_locks_operation',\n  'concurrency_locks_user_status',\n  'credit_lots_user_active',\n  'credit_lot_repairs_user_applied',\n  'harvest_attempts_job_id',\n  'harvest_attempts_user_created_at',\n  'inbox_messages_user_sent_at',\n  'jobs_created_at_status_user',\n  'jobs_status',\n  'jobs_user_created_at_status',\n  'jobs_user_id',\n  'kpo_jobs_status',\n  'kpo_jobs_user_id',\n  'kpo_phase_log_job_id',\n  'ledger_stripe_pi_unique',\n  'ledger_user_id',\n  'oauth_tokens_identity',\n  'page_snapshots_user_checked_at',\n  'prt_user_id',\n  'request_events_created_at',\n  'request_events_user_created_at',\n  'scheduled_run_view_links_expiry',\n  'scheduled_run_view_links_owner_run',\n  'site_audit_jobs_status',\n  'site_audit_jobs_user_id',\n  'site_audit_phase_log_job_id',\n  'site_extract_jobs_status',\n  'site_extract_jobs_idempotency',\n  'site_extract_jobs_user_id',\n  'site_extract_pages_job_id',\n  'workflow_artifacts_run',\n  'workflow_runs_idempotency',\n  'workflow_runs_schedule',\n  'workflow_runs_user_created',\n  'workflow_schedules_due',\n  'workflow_schedules_user_status',\n  'workflow_webhook_deliveries_run',\n  'workshop_registrations_event_status',\n] as const\n\nconst CORE_SCHEMA_TRIGGERS = [\n  'credit_lots_bounds_insert',\n  'credit_lots_bounds_update',\n  'credit_lots_one_migration_insert',\n  'credit_lots_one_migration_update',\n] as const\n\nconst CORE_SCHEMA_COLUMNS = {\n  credit_lots: ['never_expires'],\n  harvest_attempts: ['debug_json'],\n  oauth_tokens: ['replaced_by', 'rotated_at'],\n  site_extract_jobs: [\n    'attempted_urls', 'successful_urls', 'failed_urls', 'idempotency_key', 'request_fingerprint',\n    'dispatch_attempts', 'next_dispatch_at', 'dispatch_error',\n    'settlement_attempts', 'next_settlement_at', 'settlement_error',\n  ],\n  stripe_events: ['attempt_count', 'claim_token', 'last_error', 'status', 'updated_at'],\n  users: [\n    'balance_mc',\n    'concurrency_stripe_sub_id',\n    'extra_concurrency_slots',\n    'key_active',\n    'memory_identity',\n    'memory_key',\n    'memory_plan',\n    'memory_provisioned',\n    'memory_subscription_id',\n    'password_hash',\n    'stripe_customer_id',\n    'subscription_concurrency',\n    'subscription_id',\n    'subscription_tier',\n  ],\n  workshop_registrations: ['field_notes_email_id', 'reminder_48h_email_id', 'reminder_24h_email_id', 'reminder_1h_email_id'],\n  workflow_artifacts: ['content'],\n  workflow_runs: ['step_index', 'step_log_json', 'step_state_json', 'total_steps', 'webhook_url'],\n} as const\n\nfunction sqlStringList(values: readonly string[]): string {\n  return values.map(value => `'${value.replaceAll(\"'\", \"''\")}'`).join(', ')\n}\n\nasync function hasCurrentCoreSchema(db: Client): Promise<boolean> {\n  const checks = [\n    `(SELECT COUNT(*) FROM sqlite_master WHERE type = 'table' AND name IN (${sqlStringList(CORE_SCHEMA_TABLES)})) = ${CORE_SCHEMA_TABLES.length}`,\n    `(SELECT COUNT(*) FROM sqlite_master WHERE type = 'index' AND name IN (${sqlStringList(CORE_SCHEMA_INDEXES)})) = ${CORE_SCHEMA_INDEXES.length}`,\n    `(SELECT COUNT(*) FROM sqlite_master WHERE type = 'trigger' AND name IN (${sqlStringList(CORE_SCHEMA_TRIGGERS)})) = ${CORE_SCHEMA_TRIGGERS.length}`,\n    ...Object.entries(CORE_SCHEMA_COLUMNS).map(([table, columns]) =>\n      `(SELECT COUNT(*) FROM pragma_table_info('${table}') WHERE name IN (${sqlStringList(columns)})) = ${columns.length}`,\n    ),\n  ]\n  const result = await db.execute(`SELECT ${checks.join(' AND ')} AS ready`)\n  return Number(result.rows[0]?.ready ?? 0) === 1\n}\n\n/** One-time repair retained behind its own migration receipt for controlled releases. */\nasync function runSiteExtractCounterRepair(db: Client): Promise<void> {\n  await db.execute(`\n    UPDATE site_extract_jobs SET\n      attempted_urls = done_urls,\n      successful_urls = (\n        SELECT COUNT(*) FROM site_extract_pages\n        WHERE job_id = site_extract_jobs.id\n          AND CASE\n            WHEN json_valid(page) = 0 THEN 0\n            WHEN json_extract(page, '$.extractionStatus') IS NOT NULL\n              THEN json_extract(page, '$.extractionStatus') = 'successful'\n            ELSE COALESCE(\n              json_extract(page, '$.status') >= 200\n              AND json_extract(page, '$.status') < 300\n              AND json_extract(page, '$.wordCount') > 0,\n              0\n            )\n          END\n      ),\n      failed_urls = MAX(0, done_urls - (\n        SELECT COUNT(*) FROM site_extract_pages\n        WHERE job_id = site_extract_jobs.id\n          AND CASE\n            WHEN json_valid(page) = 0 THEN 0\n            WHEN json_extract(page, '$.extractionStatus') IS NOT NULL\n              THEN json_extract(page, '$.extractionStatus') = 'successful'\n            ELSE COALESCE(\n              json_extract(page, '$.status') >= 200\n              AND json_extract(page, '$.status') < 300\n              AND json_extract(page, '$.wordCount') > 0,\n              0\n            )\n          END\n      ))\n    WHERE done_urls > 0\n      AND attempted_urls = 0\n      AND successful_urls = 0\n      AND failed_urls = 0\n  `)\n}\n\nasync function runPendingDataRepairs(db: Client): Promise<void> {\n  const applied = await db.execute({\n    sql: 'SELECT 1 AS applied FROM schema_migrations WHERE version = ? LIMIT 1',\n    args: [SITE_EXTRACT_COUNTER_REPAIR_VERSION],\n  })\n  if (applied.rows.length > 0) return\n\n  await runSiteExtractCounterRepair(db)\n  await db.execute({\n    sql: 'INSERT OR IGNORE INTO schema_migrations (version) VALUES (?)',\n    args: [SITE_EXTRACT_COUNTER_REPAIR_VERSION],\n  })\n}\n\nexport function getDb(): Client {\n  if (!_db) _db = createClient({ url: DB_URL, authToken: DB_TOKEN })\n  return _db\n}\n\nexport function setDbForTesting(client: Client | null): void {\n  _db = client\n}\n\nexport interface MigrationOptions {\n  verifyCurrentSchema?: boolean\n  runDataRepairs?: boolean\n}\n\nexport async function migrate(options: MigrationOptions = {}): Promise<void> {\n  const db = getDb()\n  let applied\n  try {\n    applied = await db.execute({\n      sql: 'SELECT 1 AS applied FROM schema_migrations WHERE version = ? LIMIT 1',\n      args: [CORE_SCHEMA_VERSION],\n    })\n  } catch (error) {\n    const message = error instanceof Error ? error.message.toLowerCase() : String(error).toLowerCase()\n    if (!message.includes('no such table') || !message.includes('schema_migrations')) throw error\n    await db.execute(`\n      CREATE TABLE IF NOT EXISTS schema_migrations (\n        version    TEXT PRIMARY KEY,\n        applied_at TEXT NOT NULL DEFAULT (datetime('now'))\n      )\n    `)\n    applied = { rows: [] }\n  }\n  if (applied.rows.length > 0) {\n    if (!options.verifyCurrentSchema) return\n    if (await hasCurrentCoreSchema(db)) {\n      if (options.runDataRepairs) await runPendingDataRepairs(db)\n      return\n    }\n  }\n\n  // Existing production databases predate the migration registry. Verify the\n  // complete legacy schema in one query and register it without replaying 84\n  // remote DDL statements on every serverless cold start.\n  if (await hasCurrentCoreSchema(db)) {\n    await runPendingDataRepairs(db)\n    await db.execute({\n      sql: 'INSERT OR IGNORE INTO schema_migrations (version) VALUES (?)',\n      args: [CORE_SCHEMA_VERSION],\n    })\n    return\n  }\n\n  await db.execute(`\n    CREATE TABLE IF NOT EXISTS users (\n      id            INTEGER PRIMARY KEY AUTOINCREMENT,\n      email         TEXT UNIQUE NOT NULL,\n      name          TEXT,\n      api_key       TEXT UNIQUE NOT NULL,\n      password_hash TEXT,\n      created_at    TEXT NOT NULL DEFAULT (datetime('now')),\n      active        INTEGER NOT NULL DEFAULT 1\n    )\n  `)\n  await db.execute(`\n    CREATE TABLE IF NOT EXISTS jobs (\n      id            TEXT PRIMARY KEY,\n      user_id       INTEGER NOT NULL REFERENCES users(id),\n      status        TEXT NOT NULL DEFAULT 'pending',\n      query         TEXT NOT NULL,\n      options       TEXT NOT NULL,\n      callback_url  TEXT,\n      result        TEXT,\n      error         TEXT,\n      created_at    TEXT NOT NULL DEFAULT (datetime('now')),\n      started_at    TEXT,\n      completed_at  TEXT\n    )\n  `)\n  await db.execute(`CREATE INDEX IF NOT EXISTS jobs_user_id ON jobs(user_id)`)\n  await db.execute(`CREATE INDEX IF NOT EXISTS jobs_status ON jobs(status)`)\n  await db.execute(\n    `CREATE INDEX IF NOT EXISTS jobs_created_at_status_user ON jobs(created_at, status, user_id)`,\n  )\n  await db.execute(\n    `CREATE INDEX IF NOT EXISTS jobs_user_created_at_status ON jobs(user_id, created_at, status)`,\n  )\n  await db.execute(`\n    CREATE TABLE IF NOT EXISTS harvest_attempts (\n      id                       TEXT PRIMARY KEY,\n      job_id                   TEXT NOT NULL REFERENCES jobs(id),\n      user_id                  INTEGER NOT NULL REFERENCES users(id),\n      attempt_number           INTEGER NOT NULL,\n      max_attempts             INTEGER NOT NULL,\n      query                    TEXT NOT NULL,\n      location                 TEXT,\n      max_questions            INTEGER NOT NULL,\n      status                   TEXT NOT NULL,\n      outcome                  TEXT,\n      kernel_session_id        TEXT,\n      question_count           INTEGER,\n      duration_ms              INTEGER,\n      error                    TEXT,\n      will_retry               INTEGER,\n      kernel_delete_started    INTEGER NOT NULL DEFAULT 0,\n      kernel_delete_succeeded  INTEGER,\n      kernel_delete_error      TEXT,\n      browser_close_succeeded  INTEGER,\n      browser_close_error      TEXT,\n      debug_json               TEXT,\n      created_at               TEXT NOT NULL DEFAULT (datetime('now')),\n      completed_at             TEXT,\n      UNIQUE(job_id, attempt_number)\n    )\n  `)\n  await db.execute(`CREATE INDEX IF NOT EXISTS harvest_attempts_job_id ON harvest_attempts(job_id)`)\n  await db.execute(`CREATE INDEX IF NOT EXISTS harvest_attempts_user_created_at ON harvest_attempts(user_id, created_at DESC)`)\n  try { await db.execute(`ALTER TABLE harvest_attempts ADD COLUMN debug_json TEXT`) } catch {}\n  try { await db.execute(`ALTER TABLE users ADD COLUMN password_hash TEXT`) } catch {}\n  try { await db.execute(`ALTER TABLE users ADD COLUMN key_active INTEGER NOT NULL DEFAULT 1`) } catch {}\n\n  await db.execute(`\n    CREATE TABLE IF NOT EXISTS kpo_jobs (\n      id          TEXT    PRIMARY KEY,\n      user_id     INTEGER NOT NULL REFERENCES users(id),\n      status      TEXT    NOT NULL,\n      entity_name TEXT    NOT NULL,\n      entity_type TEXT    NOT NULL,\n      url         TEXT,\n      request     TEXT    NOT NULL,\n      result      TEXT,\n      error       TEXT,\n      created_at  TEXT    NOT NULL DEFAULT (datetime('now')),\n      updated_at  TEXT    NOT NULL DEFAULT (datetime('now'))\n    )\n  `)\n  await db.execute(`\n    CREATE TABLE IF NOT EXISTS kpo_phase_log (\n      id         INTEGER PRIMARY KEY AUTOINCREMENT,\n      job_id     TEXT    NOT NULL REFERENCES kpo_jobs(id),\n      phase      TEXT    NOT NULL,\n      output     TEXT    NOT NULL,\n      created_at TEXT    NOT NULL DEFAULT (datetime('now'))\n    )\n  `)\n  await db.execute(`CREATE INDEX IF NOT EXISTS kpo_jobs_user_id ON kpo_jobs(user_id)`)\n  await db.execute(`CREATE INDEX IF NOT EXISTS kpo_jobs_status ON kpo_jobs(status)`)\n  await db.execute(`CREATE INDEX IF NOT EXISTS kpo_phase_log_job_id ON kpo_phase_log(job_id)`)\n  try { await db.execute(`ALTER TABLE users ADD COLUMN stripe_customer_id TEXT`) } catch {}\n  try { await db.execute(`ALTER TABLE users ADD COLUMN balance_mc INTEGER NOT NULL DEFAULT 0`) } catch {}\n  try { await db.execute(`ALTER TABLE users ADD COLUMN extra_concurrency_slots INTEGER NOT NULL DEFAULT 0`) } catch {}\n  try { await db.execute(`ALTER TABLE users ADD COLUMN concurrency_stripe_sub_id TEXT`) } catch {}\n  try { await db.execute(`ALTER TABLE users ADD COLUMN subscription_concurrency INTEGER NOT NULL DEFAULT 0`) } catch {}\n  try { await db.execute(`ALTER TABLE users ADD COLUMN subscription_tier TEXT`) } catch {}\n  try { await db.execute(`ALTER TABLE users ADD COLUMN subscription_id TEXT`) } catch {}\n  try { await db.execute(`ALTER TABLE users ADD COLUMN memory_key TEXT`) } catch {}\n  try { await db.execute(`ALTER TABLE users ADD COLUMN memory_identity TEXT`) } catch {}\n  try { await db.execute(`ALTER TABLE users ADD COLUMN memory_plan TEXT NOT NULL DEFAULT 'free'`) } catch {}\n  try { await db.execute(`ALTER TABLE users ADD COLUMN memory_subscription_id TEXT`) } catch {}\n  try { await db.execute(`ALTER TABLE users ADD COLUMN memory_provisioned INTEGER NOT NULL DEFAULT 0`) } catch {}\n  await db.execute(`\n    CREATE TABLE IF NOT EXISTS concurrency_locks (\n      id          TEXT PRIMARY KEY,\n      user_id     INTEGER NOT NULL REFERENCES users(id),\n      operation   TEXT NOT NULL,\n      status      TEXT NOT NULL DEFAULT 'active',\n      acquired_at TEXT NOT NULL DEFAULT (datetime('now')),\n      expires_at  TEXT NOT NULL,\n      released_at TEXT,\n      metadata    TEXT\n    )\n  `)\n  await db.execute(`CREATE INDEX IF NOT EXISTS concurrency_locks_user_status ON concurrency_locks(user_id, status, expires_at)`)\n  await db.execute(`CREATE INDEX IF NOT EXISTS concurrency_locks_operation ON concurrency_locks(operation, status, expires_at)`)\n  await db.execute(`CREATE INDEX IF NOT EXISTS concurrency_locks_active_user ON concurrency_locks(user_id, expires_at) WHERE status = 'active'`)\n  await db.execute(`\n    CREATE TABLE IF NOT EXISTS ledger (\n      id          INTEGER PRIMARY KEY AUTOINCREMENT,\n      user_id     INTEGER NOT NULL REFERENCES users(id),\n      amount_mc   INTEGER NOT NULL,\n      operation   TEXT NOT NULL,\n      description TEXT,\n      stripe_pi   TEXT,\n      created_at  TEXT NOT NULL DEFAULT (datetime('now'))\n    )\n  `)\n  await db.execute(`CREATE INDEX IF NOT EXISTS ledger_user_id ON ledger(user_id)`)\n  await db.execute(`CREATE UNIQUE INDEX IF NOT EXISTS ledger_stripe_pi_unique ON ledger(stripe_pi) WHERE stripe_pi IS NOT NULL`)\n  await db.execute(`\n    CREATE TABLE IF NOT EXISTS credit_lots (\n      id           INTEGER PRIMARY KEY AUTOINCREMENT,\n      user_id      INTEGER NOT NULL REFERENCES users(id),\n      amount_mc    INTEGER NOT NULL,\n      remaining_mc INTEGER NOT NULL,\n      source       TEXT NOT NULL,\n      stripe_pi    TEXT,\n      granted_at   TEXT NOT NULL DEFAULT (datetime('now')),\n      expires_at   TEXT NOT NULL,\n      never_expires INTEGER NOT NULL DEFAULT 0\n    )\n  `)\n  try { await db.execute(`ALTER TABLE credit_lots ADD COLUMN never_expires INTEGER NOT NULL DEFAULT 0`) } catch {}\n  await db.execute(`CREATE INDEX IF NOT EXISTS credit_lots_user_active ON credit_lots(user_id, expires_at, remaining_mc)`)\n  await db.execute(`\n    CREATE TABLE IF NOT EXISTS credit_lot_expirations (\n      lot_id       INTEGER PRIMARY KEY REFERENCES credit_lots(id),\n      user_id      INTEGER NOT NULL REFERENCES users(id),\n      amount_mc    INTEGER NOT NULL,\n      claim_token  TEXT NOT NULL UNIQUE,\n      expired_at   TEXT NOT NULL DEFAULT (datetime('now'))\n    )\n  `)\n  await db.execute(`\n    CREATE TRIGGER IF NOT EXISTS credit_lots_bounds_insert\n    BEFORE INSERT ON credit_lots\n    WHEN NEW.amount_mc < 0\n      OR NEW.remaining_mc < 0\n      OR NEW.remaining_mc > NEW.amount_mc\n    BEGIN\n      SELECT RAISE(ABORT, 'invalid credit lot bounds');\n    END\n  `)\n  await db.execute(`\n    CREATE TRIGGER IF NOT EXISTS credit_lots_bounds_update\n    BEFORE UPDATE OF amount_mc, remaining_mc ON credit_lots\n    WHEN NEW.amount_mc < 0\n      OR NEW.remaining_mc < 0\n      OR NEW.remaining_mc > NEW.amount_mc\n    BEGIN\n      SELECT RAISE(ABORT, 'invalid credit lot bounds');\n    END\n  `)\n  await db.execute(`\n    CREATE TRIGGER IF NOT EXISTS credit_lots_one_migration_insert\n    BEFORE INSERT ON credit_lots\n    WHEN NEW.source = 'migration'\n      AND EXISTS (\n        SELECT 1 FROM credit_lots\n        WHERE user_id = NEW.user_id AND source = 'migration'\n      )\n    BEGIN\n      SELECT RAISE(ABORT, 'duplicate migration lot');\n    END\n  `)\n  await db.execute(`\n    CREATE TRIGGER IF NOT EXISTS credit_lots_one_migration_update\n    BEFORE UPDATE OF source, user_id ON credit_lots\n    WHEN NEW.source = 'migration'\n      AND EXISTS (\n        SELECT 1 FROM credit_lots\n        WHERE user_id = NEW.user_id\n          AND source = 'migration'\n          AND id != OLD.id\n      )\n    BEGIN\n      SELECT RAISE(ABORT, 'duplicate migration lot');\n    END\n  `)\n  await db.execute(`\n    CREATE TABLE IF NOT EXISTS credit_lot_repairs (\n      repair_id       TEXT PRIMARY KEY,\n      user_id         INTEGER NOT NULL REFERENCES users(id),\n      report_sha256   TEXT NOT NULL,\n      classification TEXT NOT NULL,\n      before_json     TEXT NOT NULL,\n      plan_json       TEXT NOT NULL,\n      after_json      TEXT,\n      status          TEXT NOT NULL DEFAULT 'claimed',\n      created_at      TEXT NOT NULL DEFAULT (datetime('now')),\n      applied_at      TEXT\n    )\n  `)\n  await db.execute(`CREATE INDEX IF NOT EXISTS credit_lot_repairs_user_applied ON credit_lot_repairs(user_id, applied_at)`)\n  await db.execute(`\n    CREATE TABLE IF NOT EXISTS free_credit_refreshes (\n      user_id    INTEGER NOT NULL REFERENCES users(id),\n      month      TEXT NOT NULL,\n      created_at TEXT NOT NULL DEFAULT (datetime('now')),\n      PRIMARY KEY (user_id, month)\n    )\n  `)\n  await db.execute(`\n    CREATE TABLE IF NOT EXISTS billing_authorizations (\n      id                TEXT PRIMARY KEY,\n      user_id           INTEGER NOT NULL REFERENCES users(id),\n      idempotency_key   TEXT NOT NULL UNIQUE,\n      billing_class     TEXT NOT NULL,\n      status            TEXT NOT NULL,\n      minimum_amount_mc INTEGER NOT NULL,\n      maximum_amount_mc INTEGER NOT NULL,\n      token_hash        TEXT NOT NULL,\n      metadata          TEXT,\n      expires_at        TEXT NOT NULL,\n      created_at        TEXT NOT NULL DEFAULT (datetime('now')),\n      settled_at        TEXT\n    )\n  `)\n  await db.execute(`CREATE INDEX IF NOT EXISTS billing_authorizations_user_status ON billing_authorizations(user_id, status, expires_at)`)\n  await db.execute(`\n    CREATE TABLE IF NOT EXISTS billing_events (\n      id                 TEXT PRIMARY KEY,\n      user_id            INTEGER NOT NULL REFERENCES users(id),\n      authorization_id   TEXT REFERENCES billing_authorizations(id),\n      idempotency_key    TEXT NOT NULL UNIQUE,\n      billing_class      TEXT NOT NULL,\n      source_surface     TEXT NOT NULL,\n      status             TEXT NOT NULL,\n      amount_mc          INTEGER NOT NULL DEFAULT 0,\n      raw_cost_usd_nanos INTEGER,\n      multiplier_bps     INTEGER,\n      input_tokens       INTEGER,\n      output_tokens      INTEGER,\n      cached_tokens      INTEGER,\n      model_steps        INTEGER,\n      metadata           TEXT,\n      created_at         TEXT NOT NULL DEFAULT (datetime('now')),\n      settled_at         TEXT\n    )\n  `)\n  await db.execute(`CREATE INDEX IF NOT EXISTS billing_events_user_created_at ON billing_events(user_id, created_at DESC)`)\n  await db.execute(`CREATE INDEX IF NOT EXISTS billing_events_authorization ON billing_events(authorization_id)`)\n  await db.execute(`\n    CREATE TABLE IF NOT EXISTS billing_debits (\n      idempotency_key TEXT PRIMARY KEY,\n      user_id         INTEGER NOT NULL REFERENCES users(id),\n      amount_mc       INTEGER NOT NULL,\n      operation       TEXT NOT NULL,\n      description     TEXT,\n      status          TEXT NOT NULL DEFAULT 'claimed',\n      created_at      TEXT NOT NULL DEFAULT (datetime('now')),\n      applied_at      TEXT\n    )\n  `)\n  await db.execute(`CREATE INDEX IF NOT EXISTS billing_debits_user_status ON billing_debits(user_id, status)`)\n  await db.execute(`\n    CREATE TABLE IF NOT EXISTS billing_debit_allocations (\n      debit_idempotency_key TEXT NOT NULL REFERENCES billing_debits(idempotency_key),\n      lot_id                INTEGER NOT NULL REFERENCES credit_lots(id),\n      user_id               INTEGER NOT NULL REFERENCES users(id),\n      amount_mc             INTEGER NOT NULL,\n      expires_at            TEXT NOT NULL,\n      PRIMARY KEY (debit_idempotency_key, lot_id)\n    )\n  `)\n  await db.execute(`\n    CREATE TABLE IF NOT EXISTS connected_account_billing (\n      user_id                     INTEGER PRIMARY KEY REFERENCES users(id),\n      stripe_subscription_id      TEXT,\n      stripe_subscription_item_id TEXT,\n      price_id                    TEXT,\n      quantity                    INTEGER NOT NULL DEFAULT 0,\n      status                      TEXT NOT NULL DEFAULT 'pending',\n      last_error_code             TEXT,\n      checked_at                  TEXT NOT NULL DEFAULT (datetime('now')),\n      synced_at                   TEXT,\n      updated_at                  TEXT NOT NULL DEFAULT (datetime('now'))\n    )\n  `)\n  await db.execute(`\n    CREATE TABLE IF NOT EXISTS connected_account_charges (\n      user_id       INTEGER NOT NULL,\n      connection_id TEXT NOT NULL,\n      period_start  TEXT NOT NULL,\n      amount_mc     INTEGER NOT NULL,\n      created_at    TEXT NOT NULL DEFAULT (datetime('now')),\n      PRIMARY KEY (user_id, connection_id, period_start)\n    )\n  `)\n  await db.execute(`\n    CREATE TABLE IF NOT EXISTS billing_credits (\n      idempotency_key TEXT PRIMARY KEY,\n      user_id         INTEGER NOT NULL REFERENCES users(id),\n      amount_mc       INTEGER NOT NULL,\n      operation       TEXT NOT NULL,\n      description     TEXT,\n      status          TEXT NOT NULL DEFAULT 'claimed',\n      created_at      TEXT NOT NULL DEFAULT (datetime('now')),\n      applied_at      TEXT\n    )\n  `)\n  await db.execute(`CREATE INDEX IF NOT EXISTS billing_credits_user_status ON billing_credits(user_id, status)`)\n  await db.execute(`\n    CREATE TABLE IF NOT EXISTS admin_credit_adjustments (\n      reference   TEXT PRIMARY KEY,\n      user_id     INTEGER NOT NULL REFERENCES users(id),\n      direction   TEXT NOT NULL,\n      amount_mc   INTEGER NOT NULL,\n      reason      TEXT NOT NULL,\n      actor       TEXT NOT NULL,\n      status      TEXT NOT NULL DEFAULT 'applied',\n      created_at  TEXT NOT NULL DEFAULT (datetime('now')),\n      updated_at  TEXT\n    )\n  `)\n  await db.execute(`CREATE INDEX IF NOT EXISTS admin_credit_adjustments_user_created ON admin_credit_adjustments(user_id, created_at)`)\n  await db.execute(`\n    CREATE TABLE IF NOT EXISTS admin_credit_adjustment_events (\n      id             INTEGER PRIMARY KEY AUTOINCREMENT,\n      reference      TEXT NOT NULL REFERENCES admin_credit_adjustments(reference),\n      seq            INTEGER NOT NULL,\n      kind           TEXT NOT NULL,\n      from_amount_mc INTEGER,\n      to_amount_mc   INTEGER,\n      from_reason    TEXT,\n      to_reason      TEXT,\n      moved_mc       INTEGER NOT NULL DEFAULT 0,\n      actor          TEXT NOT NULL,\n      created_at     TEXT NOT NULL DEFAULT (datetime('now')),\n      UNIQUE (reference, seq)\n    )\n  `)\n  await db.execute(`CREATE INDEX IF NOT EXISTS admin_credit_adjustment_events_reference ON admin_credit_adjustment_events(reference)`)\n  await db.execute(`\n    CREATE TABLE IF NOT EXISTS site_audit_jobs (\n      id            TEXT    PRIMARY KEY,\n      user_id       INTEGER,\n      status        TEXT    DEFAULT 'pending',\n      client_domain TEXT,\n      session_path  TEXT,\n      request       TEXT,\n      result        TEXT,\n      error         TEXT,\n      created_at    TEXT    DEFAULT (datetime('now')),\n      updated_at    TEXT    DEFAULT (datetime('now'))\n    )\n  `)\n  await db.execute(`\n    CREATE TABLE IF NOT EXISTS site_audit_phase_log (\n      id             INTEGER PRIMARY KEY AUTOINCREMENT,\n      job_id         TEXT    REFERENCES site_audit_jobs(id),\n      phase          TEXT,\n      completed_at   TEXT    DEFAULT (datetime('now')),\n      output_summary TEXT\n    )\n  `)\n  await db.execute(`CREATE INDEX IF NOT EXISTS site_audit_jobs_user_id ON site_audit_jobs(user_id)`)\n  await db.execute(`CREATE INDEX IF NOT EXISTS site_audit_jobs_status ON site_audit_jobs(status)`)\n  await db.execute(`CREATE INDEX IF NOT EXISTS site_audit_phase_log_job_id ON site_audit_phase_log(job_id)`)\n  await db.execute(`\n    CREATE TABLE IF NOT EXISTS site_extract_jobs (\n      id            TEXT    PRIMARY KEY,\n      user_id       INTEGER,\n      status        TEXT    DEFAULT 'pending',\n      start_url     TEXT,\n      options       TEXT,\n      total_urls    INTEGER DEFAULT 0,\n      done_urls     INTEGER DEFAULT 0,\n      attempted_urls INTEGER DEFAULT 0,\n      successful_urls INTEGER DEFAULT 0,\n      failed_urls   INTEGER DEFAULT 0,\n      idempotency_key TEXT,\n      request_fingerprint TEXT,\n      dispatch_attempts INTEGER DEFAULT 0,\n      next_dispatch_at TEXT,\n      dispatch_error TEXT,\n      settlement_attempts INTEGER DEFAULT 0,\n      next_settlement_at TEXT,\n      settlement_error TEXT,\n      artifacts     TEXT,\n      error         TEXT,\n      billed_mc     INTEGER,\n      created_at    TEXT    DEFAULT (datetime('now')),\n      updated_at    TEXT    DEFAULT (datetime('now'))\n    )\n  `)\n  await db.execute(`\n    CREATE TABLE IF NOT EXISTS site_extract_pages (\n      job_id     TEXT,\n      url        TEXT,\n      page       TEXT,\n      PRIMARY KEY (job_id, url)\n    )\n  `)\n  await db.execute(`CREATE INDEX IF NOT EXISTS site_extract_jobs_user_id ON site_extract_jobs(user_id)`)\n  await db.execute(`CREATE INDEX IF NOT EXISTS site_extract_jobs_status ON site_extract_jobs(status)`)\n  await db.execute(`CREATE INDEX IF NOT EXISTS site_extract_pages_job_id ON site_extract_pages(job_id)`)\n  try { await db.execute(`ALTER TABLE site_extract_jobs ADD COLUMN attempted_urls INTEGER DEFAULT 0`) } catch {}\n  try { await db.execute(`ALTER TABLE site_extract_jobs ADD COLUMN successful_urls INTEGER DEFAULT 0`) } catch {}\n  try { await db.execute(`ALTER TABLE site_extract_jobs ADD COLUMN failed_urls INTEGER DEFAULT 0`) } catch {}\n  try { await db.execute(`ALTER TABLE site_extract_jobs ADD COLUMN idempotency_key TEXT`) } catch {}\n  try { await db.execute(`ALTER TABLE site_extract_jobs ADD COLUMN request_fingerprint TEXT`) } catch {}\n  try { await db.execute(`ALTER TABLE site_extract_jobs ADD COLUMN dispatch_attempts INTEGER DEFAULT 0`) } catch {}\n  try { await db.execute(`ALTER TABLE site_extract_jobs ADD COLUMN next_dispatch_at TEXT`) } catch {}\n  try { await db.execute(`ALTER TABLE site_extract_jobs ADD COLUMN dispatch_error TEXT`) } catch {}\n  try { await db.execute(`ALTER TABLE site_extract_jobs ADD COLUMN settlement_attempts INTEGER DEFAULT 0`) } catch {}\n  try { await db.execute(`ALTER TABLE site_extract_jobs ADD COLUMN next_settlement_at TEXT`) } catch {}\n  try { await db.execute(`ALTER TABLE site_extract_jobs ADD COLUMN settlement_error TEXT`) } catch {}\n  await db.execute(`CREATE UNIQUE INDEX IF NOT EXISTS site_extract_jobs_idempotency ON site_extract_jobs(user_id, idempotency_key) WHERE idempotency_key IS NOT NULL`)\n  await db.execute(`\n    CREATE TABLE IF NOT EXISTS password_reset_tokens (\n      token      TEXT    PRIMARY KEY,\n      user_id    INTEGER NOT NULL REFERENCES users(id),\n      expires_at TEXT    NOT NULL,\n      used       INTEGER NOT NULL DEFAULT 0,\n      created_at TEXT    NOT NULL DEFAULT (datetime('now'))\n    )\n  `)\n  await db.execute(`CREATE INDEX IF NOT EXISTS prt_user_id ON password_reset_tokens(user_id)`)\n  await db.execute(`\n    CREATE TABLE IF NOT EXISTS rate_limits (\n      scope        TEXT    NOT NULL,\n      key_hash     TEXT    NOT NULL,\n      window_start INTEGER NOT NULL,\n      count        INTEGER NOT NULL DEFAULT 0,\n      updated_at   TEXT    NOT NULL DEFAULT (datetime('now')),\n      PRIMARY KEY (scope, key_hash, window_start)\n    )\n  `)\n  await db.execute(`\n    CREATE TABLE IF NOT EXISTS request_events (\n      id           TEXT PRIMARY KEY,\n      user_id      INTEGER NOT NULL REFERENCES users(id),\n      source       TEXT NOT NULL,\n      status       TEXT NOT NULL,\n      query        TEXT NOT NULL,\n      location     TEXT,\n      result_count INTEGER,\n      result       TEXT,\n      error        TEXT,\n      created_at   TEXT NOT NULL DEFAULT (datetime('now'))\n    )\n  `)\n  await db.execute(`CREATE INDEX IF NOT EXISTS request_events_user_created_at ON request_events(user_id, created_at DESC)`)\n  await db.execute(`CREATE INDEX IF NOT EXISTS request_events_created_at ON request_events(created_at)`)\n  await db.execute(`\n    CREATE TABLE IF NOT EXISTS workflow_schedules (\n      id             TEXT PRIMARY KEY,\n      user_id        INTEGER NOT NULL REFERENCES users(id),\n      workflow_id    TEXT NOT NULL,\n      name           TEXT NOT NULL,\n      input_json     TEXT NOT NULL,\n      cadence        TEXT NOT NULL,\n      cron_expr      TEXT,\n      timezone       TEXT NOT NULL DEFAULT 'UTC',\n      webhook_url    TEXT,\n      status         TEXT NOT NULL DEFAULT 'active',\n      next_run_at    TEXT,\n      last_run_at    TEXT,\n      created_at     TEXT NOT NULL DEFAULT (datetime('now')),\n      updated_at     TEXT NOT NULL DEFAULT (datetime('now'))\n    )\n  `)\n  await db.execute(`CREATE INDEX IF NOT EXISTS workflow_schedules_user_status ON workflow_schedules(user_id, status, created_at DESC)`)\n  await db.execute(`CREATE INDEX IF NOT EXISTS workflow_schedules_due ON workflow_schedules(status, next_run_at)`)\n  await db.execute(`\n    CREATE TABLE IF NOT EXISTS workflow_runs (\n      id              TEXT PRIMARY KEY,\n      user_id         INTEGER NOT NULL REFERENCES users(id),\n      schedule_id     TEXT,\n      workflow_id     TEXT NOT NULL,\n      input_json      TEXT NOT NULL,\n      status          TEXT NOT NULL DEFAULT 'queued',\n      manifest_json   TEXT,\n      error_message   TEXT,\n      queued_at       TEXT NOT NULL DEFAULT (datetime('now')),\n      started_at      TEXT,\n      completed_at    TEXT,\n      idempotency_key TEXT,\n      step_index      INTEGER NOT NULL DEFAULT 0,\n      total_steps     INTEGER,\n      step_state_json TEXT,\n      step_log_json   TEXT,\n      webhook_url     TEXT\n    )\n  `)\n  try { await db.execute(`ALTER TABLE workflow_runs ADD COLUMN step_index INTEGER NOT NULL DEFAULT 0`) } catch {}\n  try { await db.execute(`ALTER TABLE workflow_runs ADD COLUMN total_steps INTEGER`) } catch {}\n  try { await db.execute(`ALTER TABLE workflow_runs ADD COLUMN step_state_json TEXT`) } catch {}\n  try { await db.execute(`ALTER TABLE workflow_runs ADD COLUMN step_log_json TEXT`) } catch {}\n  try { await db.execute(`ALTER TABLE workflow_runs ADD COLUMN webhook_url TEXT`) } catch {}\n  await db.execute(`CREATE INDEX IF NOT EXISTS workflow_runs_user_created ON workflow_runs(user_id, queued_at DESC)`)\n  await db.execute(`CREATE INDEX IF NOT EXISTS workflow_runs_schedule ON workflow_runs(schedule_id, queued_at DESC)`)\n  await db.execute(`CREATE UNIQUE INDEX IF NOT EXISTS workflow_runs_idempotency ON workflow_runs(idempotency_key) WHERE idempotency_key IS NOT NULL`)\n  await db.execute(`\n    CREATE TABLE IF NOT EXISTS workflow_artifacts (\n      id           TEXT PRIMARY KEY,\n      run_id       TEXT NOT NULL REFERENCES workflow_runs(id),\n      user_id      INTEGER NOT NULL REFERENCES users(id),\n      kind         TEXT NOT NULL,\n      label        TEXT NOT NULL,\n      path         TEXT NOT NULL,\n      content_type TEXT NOT NULL,\n      bytes        INTEGER,\n      rows_count   INTEGER,\n      content      TEXT,\n      created_at   TEXT NOT NULL DEFAULT (datetime('now'))\n    )\n  `)\n  try { await db.execute(`ALTER TABLE workflow_artifacts ADD COLUMN content TEXT`) } catch {}\n  await db.execute(`CREATE INDEX IF NOT EXISTS workflow_artifacts_run ON workflow_artifacts(run_id)`)\n  await db.execute(`\n    CREATE TABLE IF NOT EXISTS workflow_webhook_deliveries (\n      id           TEXT PRIMARY KEY,\n      run_id       TEXT NOT NULL REFERENCES workflow_runs(id),\n      schedule_id  TEXT,\n      user_id      INTEGER NOT NULL REFERENCES users(id),\n      webhook_url  TEXT NOT NULL,\n      status       TEXT NOT NULL,\n      attempts     INTEGER NOT NULL DEFAULT 0,\n      last_error   TEXT,\n      created_at   TEXT NOT NULL DEFAULT (datetime('now')),\n      delivered_at TEXT\n    )\n  `)\n  await db.execute(`CREATE INDEX IF NOT EXISTS workflow_webhook_deliveries_run ON workflow_webhook_deliveries(run_id)`)\n  await db.execute(`\n    CREATE TABLE IF NOT EXISTS scheduled_run_view_links (\n      share_id      TEXT PRIMARY KEY,\n      owner_id      TEXT NOT NULL,\n      run_id        TEXT NOT NULL,\n      artifact_id   TEXT NOT NULL,\n      title         TEXT NOT NULL,\n      summary       TEXT NOT NULL,\n      renderer_key  TEXT NOT NULL CHECK (renderer_key IN ('editorial_reading_room_v1','personal_authority_v1','newsroom_publisher_v1')),\n      token_hash    TEXT NOT NULL UNIQUE,\n      expires_at    TEXT NOT NULL,\n      revoked_at    TEXT,\n      created_at    TEXT NOT NULL\n    )\n  `)\n  await db.execute(`\n    CREATE INDEX IF NOT EXISTS scheduled_run_view_links_owner_run\n    ON scheduled_run_view_links(owner_id, run_id, created_at DESC)\n  `)\n  await db.execute(`\n    CREATE INDEX IF NOT EXISTS scheduled_run_view_links_expiry\n    ON scheduled_run_view_links(expires_at, revoked_at)\n  `)\n  await db.execute(`\n    CREATE TABLE IF NOT EXISTS stripe_events (\n      event_id      TEXT PRIMARY KEY,\n      status        TEXT NOT NULL DEFAULT 'processed',\n      attempt_count INTEGER NOT NULL DEFAULT 1,\n      claim_token   TEXT,\n      last_error    TEXT,\n      processed_at  TEXT NOT NULL DEFAULT (datetime('now')),\n      updated_at    TEXT NOT NULL DEFAULT (datetime('now'))\n    )\n  `)\n  for (const col of [\n    `status TEXT NOT NULL DEFAULT 'processed'`,\n    'attempt_count INTEGER NOT NULL DEFAULT 1',\n    'claim_token TEXT',\n    'last_error TEXT',\n    'updated_at TEXT',\n  ]) {\n    try {\n      await db.execute(`ALTER TABLE stripe_events ADD COLUMN ${col}`)\n    } catch {\n      void 0\n    }\n  }\n  await db.execute(`\n    UPDATE stripe_events\n    SET updated_at = COALESCE(updated_at, processed_at),\n        status = COALESCE(status, 'processed'),\n        attempt_count = MAX(1, COALESCE(attempt_count, 1))\n    WHERE updated_at IS NULL\n       OR status IS NULL\n       OR attempt_count IS NULL\n       OR attempt_count < 1\n  `)\n  await db.execute(`\n    CREATE TABLE IF NOT EXISTS oauth_clients (\n      client_id     TEXT PRIMARY KEY,\n      redirect_uris TEXT NOT NULL,\n      name          TEXT,\n      created_at    TEXT NOT NULL DEFAULT (datetime('now'))\n    )\n  `)\n  await db.execute(`\n    CREATE TABLE IF NOT EXISTS oauth_codes (\n      code           TEXT PRIMARY KEY,\n      client_id      TEXT NOT NULL,\n      redirect_uri   TEXT NOT NULL,\n      code_challenge TEXT NOT NULL,\n      identity       TEXT NOT NULL,\n      scope          TEXT NOT NULL,\n      resource       TEXT,\n      expires_at     TEXT NOT NULL,\n      created_at     TEXT NOT NULL DEFAULT (datetime('now'))\n    )\n  `)\n  await db.execute(`\n    CREATE TABLE IF NOT EXISTS oauth_tokens (\n      refresh_token TEXT PRIMARY KEY,\n      client_id     TEXT NOT NULL,\n      identity      TEXT NOT NULL,\n      scope         TEXT NOT NULL,\n      resource      TEXT,\n      expires_at    TEXT NOT NULL,\n      revoked       INTEGER NOT NULL DEFAULT 0,\n      created_at    TEXT NOT NULL DEFAULT (datetime('now'))\n    )\n  `)\n  await db.execute(`CREATE INDEX IF NOT EXISTS oauth_tokens_identity ON oauth_tokens(identity)`)\n  for (const col of ['replaced_by TEXT', 'rotated_at TEXT']) {\n    try {\n      await db.execute(`ALTER TABLE oauth_tokens ADD COLUMN ${col}`)\n    } catch {\n      void 0\n    }\n  }\n  await db.execute(`\n    CREATE TABLE IF NOT EXISTS page_snapshots (\n      user_id       INTEGER NOT NULL REFERENCES users(id),\n      url           TEXT    NOT NULL,\n      content_hash  TEXT    NOT NULL,\n      content       TEXT    NOT NULL,\n      title         TEXT,\n      content_bytes INTEGER NOT NULL,\n      truncated     INTEGER NOT NULL DEFAULT 0,\n      checked_at    TEXT    NOT NULL DEFAULT (datetime('now')),\n      PRIMARY KEY (user_id, url)\n    )\n  `)\n  await db.execute(`CREATE INDEX IF NOT EXISTS page_snapshots_user_checked_at ON page_snapshots(user_id, checked_at DESC)`)\n  await db.execute(`\n    CREATE TABLE IF NOT EXISTS inbox_messages (\n      id         TEXT    PRIMARY KEY,\n      user_id    INTEGER NOT NULL REFERENCES users(id),\n      subject    TEXT    NOT NULL,\n      body_md    TEXT    NOT NULL,\n      source     TEXT    NOT NULL DEFAULT 'operator',\n      sent_at    TEXT    NOT NULL DEFAULT (datetime('now')),\n      opened_at  TEXT,\n      deleted_at TEXT\n    )\n  `)\n  await db.execute(`CREATE INDEX IF NOT EXISTS inbox_messages_user_sent_at ON inbox_messages(user_id, sent_at DESC)`)\n  await db.execute(`\n    CREATE TABLE IF NOT EXISTS workshop_registrations (\n      id                    TEXT PRIMARY KEY,\n      event_slug            TEXT NOT NULL,\n      email                 TEXT NOT NULL,\n      first_name            TEXT NOT NULL,\n      last_name             TEXT,\n      status                TEXT NOT NULL,\n      zoom_registrant_id    TEXT,\n      zoom_join_url         TEXT,\n      resend_contact_id     TEXT,\n      confirmation_email_id TEXT,\n      field_notes_email_id  TEXT,\n      reminder_48h_email_id TEXT,\n      reminder_24h_email_id TEXT,\n      reminder_1h_email_id  TEXT,\n      last_error            TEXT,\n      registered_at         TEXT NOT NULL DEFAULT (datetime('now')),\n      updated_at            TEXT NOT NULL DEFAULT (datetime('now')),\n      UNIQUE(event_slug, email)\n    )\n  `)\n  for (const col of ['field_notes_email_id TEXT', 'reminder_48h_email_id TEXT', 'reminder_24h_email_id TEXT', 'reminder_1h_email_id TEXT']) {\n    try {\n      await db.execute(`ALTER TABLE workshop_registrations ADD COLUMN ${col}`)\n    } catch {\n      void 0\n    }\n  }\n  await db.execute(`\n    CREATE INDEX IF NOT EXISTS workshop_registrations_event_status\n    ON workshop_registrations(event_slug, status, registered_at)\n  `)\n  await db.execute(`\n    CREATE TABLE IF NOT EXISTS browser_profile_labels (\n      user_id    INTEGER NOT NULL REFERENCES users(id),\n      profile    TEXT    NOT NULL,\n      label      TEXT    NOT NULL,\n      created_at TEXT    NOT NULL DEFAULT (datetime('now')),\n      updated_at TEXT,\n      PRIMARY KEY (user_id, profile)\n    )\n  `)\n  if (!(await hasCurrentCoreSchema(db))) {\n    throw new Error('core schema migration finished without all required tables, indexes, and columns')\n  }\n  await runPendingDataRepairs(db)\n  await db.execute({\n    sql: 'INSERT OR IGNORE INTO schema_migrations (version) VALUES (?)',\n    args: [CORE_SCHEMA_VERSION],\n  })\n}\n\nexport interface OAuthClient {\n  client_id: string\n  redirect_uris: string[]\n  name: string | null\n  created_at: string\n}\n\nexport async function registerClient(clientId: string, redirectUris: string[], name: string | null): Promise<void> {\n  await getDb().execute({\n    sql: 'INSERT INTO oauth_clients (client_id, redirect_uris, name) VALUES (?, ?, ?)',\n    args: [clientId, JSON.stringify(redirectUris), name],\n  })\n}\n\nexport async function getClient(clientId: string): Promise<OAuthClient | undefined> {\n  const res = await getDb().execute({ sql: 'SELECT * FROM oauth_clients WHERE client_id = ?', args: [clientId] })\n  const row = res.rows[0] as unknown as Record<string, unknown> | undefined\n  if (!row) return undefined\n  let uris: string[] = []\n  try {\n    const parsed = JSON.parse(String(row.redirect_uris))\n    if (Array.isArray(parsed)) uris = parsed.filter((u): u is string => typeof u === 'string')\n  } catch {\n    uris = []\n  }\n  return {\n    client_id: String(row.client_id),\n    redirect_uris: uris,\n    name: row.name != null ? String(row.name) : null,\n    created_at: String(row.created_at),\n  }\n}\n\nexport interface OAuthCode {\n  code: string\n  client_id: string\n  redirect_uri: string\n  code_challenge: string\n  identity: string\n  scope: string\n  resource: string | null\n  expires_at: string\n}\n\nexport async function putCode(code: OAuthCode): Promise<void> {\n  await getDb().execute({\n    sql: 'INSERT INTO oauth_codes (code, client_id, redirect_uri, code_challenge, identity, scope, resource, expires_at) VALUES (?, ?, ?, ?, ?, ?, ?, ?)',\n    args: [code.code, code.client_id, code.redirect_uri, code.code_challenge, code.identity, code.scope, code.resource, code.expires_at],\n  })\n}\n\nexport async function consumeCode(code: string): Promise<OAuthCode | undefined> {\n  const db = getDb()\n  const res = await db.execute({ sql: 'SELECT * FROM oauth_codes WHERE code = ?', args: [code] })\n  const row = res.rows[0] as unknown as Record<string, unknown> | undefined\n  await db.execute({ sql: 'DELETE FROM oauth_codes WHERE code = ?', args: [code] })\n  if (!row) return undefined\n  return {\n    code: String(row.code),\n    client_id: String(row.client_id),\n    redirect_uri: String(row.redirect_uri),\n    code_challenge: String(row.code_challenge),\n    identity: String(row.identity),\n    scope: String(row.scope),\n    resource: row.resource != null ? String(row.resource) : null,\n    expires_at: String(row.expires_at),\n  }\n}\n\nexport interface OAuthRefresh {\n  refresh_token: string\n  client_id: string\n  identity: string\n  scope: string\n  resource: string | null\n  expires_at: string\n  revoked: number\n  replaced_by?: string | null\n  rotated_at?: string | null\n}\n\nexport async function putRefresh(token: OAuthRefresh): Promise<void> {\n  await getDb().execute({\n    sql: 'INSERT INTO oauth_tokens (refresh_token, client_id, identity, scope, resource, expires_at, revoked) VALUES (?, ?, ?, ?, ?, ?, 0)',\n    args: [token.refresh_token, token.client_id, token.identity, token.scope, token.resource, token.expires_at],\n  })\n}\n\nexport async function getRefresh(refreshToken: string): Promise<OAuthRefresh | undefined> {\n  const res = await getDb().execute({ sql: 'SELECT * FROM oauth_tokens WHERE refresh_token = ?', args: [refreshToken] })\n  const row = res.rows[0] as unknown as Record<string, unknown> | undefined\n  if (!row) return undefined\n  return {\n    refresh_token: String(row.refresh_token),\n    client_id: String(row.client_id),\n    identity: String(row.identity),\n    scope: String(row.scope),\n    resource: row.resource != null ? String(row.resource) : null,\n    expires_at: String(row.expires_at),\n    revoked: Number(row.revoked ?? 0),\n    replaced_by: row.replaced_by != null ? String(row.replaced_by) : null,\n    rotated_at: row.rotated_at != null ? String(row.rotated_at) : null,\n  }\n}\n\nexport async function revokeRefresh(refreshToken: string): Promise<void> {\n  await getDb().execute({ sql: 'UPDATE oauth_tokens SET revoked = 1 WHERE refresh_token = ?', args: [refreshToken] })\n}\n\nexport async function rotateRefresh(oldToken: string, next: OAuthRefresh): Promise<void> {\n  await putRefresh(next)\n  await getDb().execute({\n    sql: 'UPDATE oauth_tokens SET revoked = 1, replaced_by = ?, rotated_at = ? WHERE refresh_token = ?',\n    args: [next.refresh_token, new Date().toISOString(), oldToken],\n  })\n}\n\nexport type StripeEventClaim =\n  | { state: 'claimed'; token: string }\n  | { state: 'processed' | 'processing' }\n\n/**\n * Claims one Stripe event for processing.\n *\n * Failed work is immediately retryable. A processing claim can be taken over\n * only after ten minutes, beyond the serverless request ceiling, and the token\n * prevents an older worker from completing or failing a newer claim.\n */\nexport async function claimStripeEvent(eventId: string): Promise<StripeEventClaim> {\n  const token = randomUUID()\n  const claimed = await getDb().execute({\n    sql: `INSERT INTO stripe_events\n            (event_id, status, attempt_count, claim_token, last_error, processed_at, updated_at)\n          VALUES (?, 'processing', 1, ?, NULL, datetime('now'), datetime('now'))\n          ON CONFLICT(event_id) DO UPDATE SET\n            status = 'processing',\n            attempt_count = stripe_events.attempt_count + 1,\n            claim_token = excluded.claim_token,\n            last_error = NULL,\n            updated_at = datetime('now')\n          WHERE stripe_events.status = 'failed'\n             OR (\n               stripe_events.status = 'processing'\n               AND stripe_events.updated_at <= datetime('now', '-10 minutes')\n             )`,\n    args: [eventId, token],\n  })\n  if (claimed.rowsAffected > 0) return { state: 'claimed', token }\n\n  const current = await getDb().execute({\n    sql: 'SELECT status FROM stripe_events WHERE event_id = ? LIMIT 1',\n    args: [eventId],\n  })\n  return { state: String(current.rows[0]?.status) === 'processed' ? 'processed' : 'processing' }\n}\n\nexport async function completeStripeEvent(eventId: string, token: string): Promise<boolean> {\n  const result = await getDb().execute({\n    sql: `UPDATE stripe_events\n          SET status = 'processed',\n              claim_token = NULL,\n              last_error = NULL,\n              processed_at = datetime('now'),\n              updated_at = datetime('now')\n          WHERE event_id = ? AND status = 'processing' AND claim_token = ?`,\n    args: [eventId, token],\n  })\n  return result.rowsAffected === 1\n}\n\nexport async function failStripeEvent(eventId: string, token: string, error: string): Promise<boolean> {\n  const result = await getDb().execute({\n    sql: `UPDATE stripe_events\n          SET status = 'failed',\n              claim_token = NULL,\n              last_error = ?,\n              updated_at = datetime('now')\n          WHERE event_id = ? AND status = 'processing' AND claim_token = ?`,\n    args: [error.slice(0, 500), eventId, token],\n  })\n  return result.rowsAffected === 1\n}\n\nexport async function stripeEventAlreadyProcessed(eventId: string): Promise<boolean> {\n  const res = await getDb().execute({\n    sql: `SELECT 1 FROM stripe_events WHERE event_id = ? AND status = 'processed' LIMIT 1`,\n    args: [eventId],\n  })\n  return res.rows.length > 0\n}\n\nexport function generateApiKey(): string {\n  return 'sk_' + randomBytes(24).toString('hex')\n}\n\nexport interface User {\n  id: number\n  email: string\n  name: string | null\n  api_key: string\n  key_active: number\n  password_hash: string | null\n  created_at: string\n  active: number\n  stripe_customer_id: string | null\n  balance_mc: number\n  extra_concurrency_slots: number\n  concurrency_stripe_sub_id: string | null\n  subscription_concurrency: number\n  subscription_tier: string | null\n  subscription_id: string | null\n  memory_plan: string\n  memory_subscription_id: string | null\n  memory_provisioned: number\n}\n\nexport function hashPassword(password: string): string {\n  const salt = randomBytes(16).toString('hex')\n  const hash = scryptSync(password, salt, 64)\n  return `${salt}:${hash.toString('hex')}`\n}\n\nexport function verifyPassword(password: string, stored: string): boolean {\n  const [salt, hash] = stored.split(':')\n  const hashBuf = Buffer.from(hash, 'hex')\n  const derived = scryptSync(password, salt, 64)\n  return timingSafeEqual(hashBuf, derived)\n}\n\nfunction rowToUser(row: Record<string, unknown>): User {\n  return {\n    id: Number(row.id),\n    email: String(row.email),\n    name: row.name != null ? String(row.name) : null,\n    api_key: String(row.api_key),\n    key_active: Number(row.key_active ?? 1),\n    password_hash: row.password_hash != null ? String(row.password_hash) : null,\n    created_at: String(row.created_at),\n    active: Number(row.active),\n    stripe_customer_id: row.stripe_customer_id != null ? String(row.stripe_customer_id) : null,\n    balance_mc: Number(row.balance_mc ?? 0),\n    extra_concurrency_slots: Number(row.extra_concurrency_slots ?? 0),\n    concurrency_stripe_sub_id: row.concurrency_stripe_sub_id != null ? String(row.concurrency_stripe_sub_id) : null,\n    subscription_concurrency: Number(row.subscription_concurrency ?? 0),\n    subscription_tier: row.subscription_tier != null ? String(row.subscription_tier) : null,\n    subscription_id: row.subscription_id != null ? String(row.subscription_id) : null,\n    memory_plan: row.memory_plan != null ? String(row.memory_plan) : 'free',\n    memory_subscription_id: row.memory_subscription_id != null ? String(row.memory_subscription_id) : null,\n    memory_provisioned: Number(row.memory_provisioned ?? 0),\n  }\n}\n\nexport async function getUserByEmail(email: string): Promise<User | undefined> {\n  const res = await getDb().execute({ sql: 'SELECT * FROM users WHERE email = ? AND active = 1', args: [email] })\n  return res.rows[0] ? rowToUser(res.rows[0] as unknown as Record<string, unknown>) : undefined\n}\n\nexport async function getUserByApiKey(api_key: string): Promise<User | undefined> {\n  const res = await getDb().execute({ sql: 'SELECT * FROM users WHERE api_key = ? AND active = 1 AND key_active = 1', args: [api_key] })\n  return res.rows[0] ? rowToUser(res.rows[0] as unknown as Record<string, unknown>) : undefined\n}\n\nexport async function getUserById(id: number | bigint): Promise<User | undefined> {\n  const res = await getDb().execute({ sql: 'SELECT * FROM users WHERE id = ? AND active = 1', args: [id] })\n  return res.rows[0] ? rowToUser(res.rows[0] as unknown as Record<string, unknown>) : undefined\n}\n\nexport async function getUserMemoryCreds(id: number | bigint): Promise<{ memory_key: string | null; memory_identity: string | null }> {\n  const res = await getDb().execute({ sql: 'SELECT memory_key, memory_identity FROM users WHERE id = ?', args: [id] })\n  const row = res.rows[0] as unknown as Record<string, unknown> | undefined\n  return {\n    memory_key: row?.memory_key != null ? String(row.memory_key) : null,\n    memory_identity: row?.memory_identity != null ? String(row.memory_identity) : null,\n  }\n}\n\nexport async function setUserMemoryCreds(id: number | bigint, memoryKeyEncrypted: string, memoryIdentity: string): Promise<void> {\n  await getDb().execute({ sql: 'UPDATE users SET memory_key = ?, memory_identity = ? WHERE id = ?', args: [memoryKeyEncrypted, memoryIdentity, id] })\n}\n\nexport async function setPassword(id: number | bigint, password: string): Promise<void> {\n  await getDb().execute({ sql: 'UPDATE users SET password_hash = ? WHERE id = ?', args: [hashPassword(password), id] })\n}\n\nexport async function getUserStats(userId: number | bigint): Promise<{ total: number; done: number; failed: number; totalQuestions: number; lastUsed: string | null }> {\n  const db = getDb()\n  const [totR, doneR, failR, tqR, luR] = await Promise.all([\n    db.execute({ sql: 'SELECT COUNT(*) as n FROM jobs WHERE user_id = ?', args: [userId] }),\n    db.execute({ sql: \"SELECT COUNT(*) as n FROM jobs WHERE user_id = ? AND status = 'done'\", args: [userId] }),\n    db.execute({ sql: \"SELECT COUNT(*) as n FROM jobs WHERE user_id = ? AND status = 'failed'\", args: [userId] }),\n    db.execute({ sql: \"SELECT COALESCE(SUM(json_extract(result, '$.totalQuestions')), 0) as n FROM jobs WHERE user_id = ? AND status = 'done'\", args: [userId] }),\n    db.execute({ sql: 'SELECT MAX(created_at) as t FROM jobs WHERE user_id = ?', args: [userId] }),\n  ])\n  return {\n    total: Number(totR.rows[0]?.n ?? 0),\n    done: Number(doneR.rows[0]?.n ?? 0),\n    failed: Number(failR.rows[0]?.n ?? 0),\n    totalQuestions: Number(tqR.rows[0]?.n ?? 0),\n    lastUsed: luR.rows[0]?.t != null ? String(luR.rows[0].t) : null,\n  }\n}\n\nexport async function createUser(email: string, name?: string, password?: string, stripeCustomerId?: string): Promise<{ id: number | bigint; email: string; name?: string; api_key: string; password?: string }> {\n  const db = getDb()\n  const api_key = generateApiKey()\n  const plainPassword = password ?? randomBytes(6).toString('hex')\n  const password_hash = hashPassword(plainPassword)\n  const result = await db.execute({\n    sql: 'INSERT INTO users (email, name, api_key, password_hash, stripe_customer_id) VALUES (?, ?, ?, ?, ?)',\n    args: [email, name ?? null, api_key, password_hash, stripeCustomerId ?? null],\n  })\n  return { id: result.lastInsertRowid!, email, name, api_key, password: plainPassword }\n}\n\nexport async function checkRateLimit(\n  scope: string,\n  key: string,\n  limit: number,\n  windowSeconds: number,\n): Promise<{ allowed: boolean; count: number; remaining: number; resetSeconds: number }> {\n  const nowSeconds = Math.floor(Date.now() / 1000)\n  const windowStart = Math.floor(nowSeconds / windowSeconds) * windowSeconds\n  const keyHash = createHash('sha256').update(key).digest('hex')\n  const db = getDb()\n\n  if (!_rateLimitSchemaReady) {\n    await db.execute(`\n      CREATE TABLE IF NOT EXISTS rate_limits (\n        scope        TEXT    NOT NULL,\n        key_hash     TEXT    NOT NULL,\n        window_start INTEGER NOT NULL,\n        count        INTEGER NOT NULL DEFAULT 0,\n        updated_at   TEXT    NOT NULL DEFAULT (datetime('now')),\n        PRIMARY KEY (scope, key_hash, window_start)\n      )\n    `)\n    _rateLimitSchemaReady = true\n  }\n\n  await db.execute({\n    sql: `\n      INSERT INTO rate_limits (scope, key_hash, window_start, count)\n      VALUES (?, ?, ?, 1)\n      ON CONFLICT(scope, key_hash, window_start)\n      DO UPDATE SET count = count + 1, updated_at = datetime('now')\n    `,\n    args: [scope, keyHash, windowStart],\n  })\n\n  const res = await db.execute({\n    sql: 'SELECT count FROM rate_limits WHERE scope = ? AND key_hash = ? AND window_start = ?',\n    args: [scope, keyHash, windowStart],\n  })\n  const count = Number(res.rows[0]?.count ?? 1)\n  return {\n    allowed: count <= limit,\n    count,\n    remaining: Math.max(0, limit - count),\n    resetSeconds: Math.max(1, windowStart + windowSeconds - nowSeconds),\n  }\n}\n\nexport async function createPasswordResetToken(userId: number | bigint): Promise<string> {\n  const token = randomBytes(32).toString('hex')\n  const tokenHash = createHash('sha256').update(token).digest('hex')\n  const expiresAt = new Date(Date.now() + 60 * 60 * 1000).toISOString()\n  await getDb().execute({\n    sql: 'INSERT INTO password_reset_tokens (token, user_id, expires_at) VALUES (?, ?, ?)',\n    args: [tokenHash, userId, expiresAt],\n  })\n  return token\n}\n\nexport async function consumePasswordResetToken(token: string): Promise<number | null> {\n  const db = getDb()\n  const tokenHash = createHash('sha256').update(token).digest('hex')\n  const res = await db.execute({\n    sql: \"SELECT user_id, expires_at, used FROM password_reset_tokens WHERE token = ?\",\n    args: [tokenHash],\n  })\n  const row = res.rows[0]\n  if (!row) return null\n  if (Number(row.used)) return null\n  if (new Date(String(row.expires_at)) < new Date()) return null\n  await db.execute({ sql: 'UPDATE password_reset_tokens SET used = 1 WHERE token = ?', args: [tokenHash] })\n  return Number(row.user_id)\n}\n\nexport async function rotateApiKey(userId: number | bigint): Promise<string> {\n  const newKey = generateApiKey()\n  await getDb().execute({ sql: 'UPDATE users SET api_key = ?, key_active = 1 WHERE id = ?', args: [newKey, userId] })\n  return newKey\n}\n\nexport async function revokeApiKey(userId: number | bigint): Promise<void> {\n  await getDb().execute({ sql: 'UPDATE users SET key_active = 0 WHERE id = ?', args: [userId] })\n}\n\nexport async function listUsers(): Promise<Omit<User, 'api_key'>[]> {\n  const res = await getDb().execute('SELECT id, email, name, created_at, active FROM users ORDER BY created_at DESC')\n  return res.rows.map(r => rowToUser({ ...r as unknown as Record<string, unknown>, api_key: '', key_active: 1 }))\n}\n\nexport async function deactivateUser(id: number): Promise<void> {\n  await getDb().execute({ sql: 'UPDATE users SET active = 0 WHERE id = ?', args: [id] })\n}\n\ninterface RawJob {\n  id: string\n  user_id: number\n  status: string\n  query: string\n  options: string\n  callback_url: string | null\n  result: string | null\n  error: string | null\n  created_at: string\n  started_at: string | null\n  completed_at: string | null\n}\n\nexport interface Job {\n  id: string\n  user_id: number\n  status: string\n  query: string\n  options: Record<string, unknown>\n  callback_url: string | null\n  result: unknown | null\n  error: string | null\n  created_at: string\n  started_at: string | null\n  completed_at: string | null\n}\n\nexport interface HarvestAttemptLog {\n  id: string\n  job_id: string\n  user_id: number\n  attempt_number: number\n  max_attempts: number\n  query: string\n  location: string | null\n  max_questions: number\n  status: string\n  outcome: string | null\n  kernel_session_id: string | null\n  question_count: number | null\n  duration_ms: number | null\n  error: string | null\n  will_retry: boolean | null\n  kernel_delete_started: boolean\n  kernel_delete_succeeded: boolean | null\n  kernel_delete_error: string | null\n  browser_close_succeeded: boolean | null\n  browser_close_error: string | null\n  debug: unknown | null\n  created_at: string\n  completed_at: string | null\n}\n\nexport interface StartHarvestAttemptInput {\n  jobId: string\n  userId: number | bigint\n  attemptNumber: number\n  maxAttempts: number\n  query: string\n  location: string | null\n  maxQuestions: number\n  startedAt: string\n}\n\nexport interface FinishHarvestAttemptInput {\n  jobId: string\n  attemptNumber: number\n  outcome: string\n  kernelSessionId: string | null\n  questionCount: number\n  durationMs: number\n  error: string | null\n  willRetry: boolean\n  kernelDeleteStarted: boolean\n  kernelDeleteSucceeded: boolean | null\n  kernelDeleteError: string | null\n  browserCloseSucceeded: boolean | null\n  browserCloseError: string | null\n  debug: unknown | null\n  completedAt: string\n}\n\nfunction rowToRawJob(row: Record<string, unknown>): RawJob {\n  return {\n    id: String(row.id),\n    user_id: Number(row.user_id),\n    status: String(row.status),\n    query: String(row.query),\n    options: String(row.options),\n    callback_url: row.callback_url != null ? String(row.callback_url) : null,\n    result: row.result != null ? String(row.result) : null,\n    error: row.error != null ? String(row.error) : null,\n    created_at: String(row.created_at),\n    started_at: row.started_at != null ? String(row.started_at) : null,\n    completed_at: row.completed_at != null ? String(row.completed_at) : null,\n  }\n}\n\nfunction nullableBoolean(value: unknown): boolean | null {\n  return value == null ? null : Number(value) === 1\n}\n\nfunction parseNullableJson(value: unknown): unknown | null {\n  if (value == null) return null\n  try {\n    return JSON.parse(String(value)) as unknown\n  } catch {\n    return null\n  }\n}\n\nfunction rowToHarvestAttemptLog(row: Record<string, unknown>): HarvestAttemptLog {\n  return {\n    id: String(row.id),\n    job_id: String(row.job_id),\n    user_id: Number(row.user_id),\n    attempt_number: Number(row.attempt_number),\n    max_attempts: Number(row.max_attempts),\n    query: String(row.query),\n    location: row.location != null ? String(row.location) : null,\n    max_questions: Number(row.max_questions),\n    status: String(row.status),\n    outcome: row.outcome != null ? String(row.outcome) : null,\n    kernel_session_id: row.kernel_session_id != null ? String(row.kernel_session_id) : null,\n    question_count: row.question_count != null ? Number(row.question_count) : null,\n    duration_ms: row.duration_ms != null ? Number(row.duration_ms) : null,\n    error: row.error != null ? String(row.error) : null,\n    will_retry: nullableBoolean(row.will_retry),\n    kernel_delete_started: Number(row.kernel_delete_started ?? 0) === 1,\n    kernel_delete_succeeded: nullableBoolean(row.kernel_delete_succeeded),\n    kernel_delete_error: row.kernel_delete_error != null ? String(row.kernel_delete_error) : null,\n    browser_close_succeeded: nullableBoolean(row.browser_close_succeeded),\n    browser_close_error: row.browser_close_error != null ? String(row.browser_close_error) : null,\n    debug: parseNullableJson(row.debug_json),\n    created_at: String(row.created_at),\n    completed_at: row.completed_at != null ? String(row.completed_at) : null,\n  }\n}\n\nfunction deserialize(raw: RawJob): Job {\n  return {\n    ...raw,\n    options: JSON.parse(raw.options) as Record<string, unknown>,\n    result: raw.result ? JSON.parse(raw.result) : null,\n  }\n}\n\nexport async function createJob(userId: number | bigint, query: string, options: object, callbackUrl?: string): Promise<string> {\n  const id = randomUUID()\n  await createJobWithId(id, userId, query, options, callbackUrl)\n  return id\n}\n\nexport async function createJobWithId(id: string, userId: number | bigint, query: string, options: object, callbackUrl?: string): Promise<void> {\n  if (!/^[a-zA-Z0-9-]{1,100}$/.test(id)) throw new Error('job id is invalid')\n  await getDb().execute({\n    sql: 'INSERT INTO jobs (id, user_id, query, options, callback_url) VALUES (?, ?, ?, ?, ?)',\n    args: [id, userId, query, JSON.stringify(options), callbackUrl ?? null],\n  })\n}\n\nexport async function createRunningJob(userId: number | bigint, query: string, options: object): Promise<string> {\n  const id = randomUUID()\n  await getDb().execute({\n    sql: `INSERT INTO jobs (id, user_id, query, options, status, started_at) VALUES (?, ?, ?, ?, 'running', datetime('now'))`,\n    args: [id, userId, query, JSON.stringify(options)],\n  })\n  return id\n}\n\nexport async function startHarvestAttempt(input: StartHarvestAttemptInput): Promise<void> {\n  await getDb().execute({\n    sql: `\n      INSERT INTO harvest_attempts (\n        id, job_id, user_id, attempt_number, max_attempts, query, location, max_questions, status, created_at\n      )\n      VALUES (?, ?, ?, ?, ?, ?, ?, ?, 'running', ?)\n      ON CONFLICT(job_id, attempt_number) DO UPDATE SET\n        status = 'running',\n        created_at = excluded.created_at,\n        completed_at = NULL,\n        outcome = NULL,\n        error = NULL,\n        debug_json = NULL\n    `,\n    args: [\n      randomUUID(),\n      input.jobId,\n      input.userId,\n      input.attemptNumber,\n      input.maxAttempts,\n      input.query,\n      input.location,\n      input.maxQuestions,\n      input.startedAt,\n    ],\n  })\n}\n\nexport async function finishHarvestAttempt(input: FinishHarvestAttemptInput): Promise<void> {\n  await getDb().execute({\n    sql: `\n      UPDATE harvest_attempts SET\n        status = 'finished',\n        outcome = ?,\n        kernel_session_id = ?,\n        question_count = ?,\n        duration_ms = ?,\n        error = ?,\n        will_retry = ?,\n        kernel_delete_started = ?,\n        kernel_delete_succeeded = ?,\n        kernel_delete_error = ?,\n        browser_close_succeeded = ?,\n        browser_close_error = ?,\n        debug_json = ?,\n        completed_at = ?\n      WHERE job_id = ? AND attempt_number = ?\n    `,\n    args: [\n      input.outcome,\n      input.kernelSessionId,\n      input.questionCount,\n      input.durationMs,\n      input.error,\n      input.willRetry ? 1 : 0,\n      input.kernelDeleteStarted ? 1 : 0,\n      input.kernelDeleteSucceeded == null ? null : input.kernelDeleteSucceeded ? 1 : 0,\n      input.kernelDeleteError,\n      input.browserCloseSucceeded == null ? null : input.browserCloseSucceeded ? 1 : 0,\n      input.browserCloseError,\n      input.debug == null ? null : JSON.stringify(input.debug),\n      input.completedAt,\n      input.jobId,\n      input.attemptNumber,\n    ],\n  })\n}\n\nexport async function listHarvestAttempts(jobId: string, userId?: number | bigint): Promise<HarvestAttemptLog[]> {\n  const res = userId == null\n    ? await getDb().execute({\n        sql: `SELECT * FROM harvest_attempts WHERE job_id = ? ORDER BY attempt_number ASC`,\n        args: [jobId],\n      })\n    : await getDb().execute({\n        sql: `\n          SELECT ha.*\n          FROM harvest_attempts ha\n          INNER JOIN jobs j ON j.id = ha.job_id\n          WHERE ha.job_id = ? AND j.user_id = ?\n          ORDER BY ha.attempt_number ASC\n        `,\n        args: [jobId, userId],\n      })\n  return res.rows.map(r => rowToHarvestAttemptLog(r as unknown as Record<string, unknown>))\n}\n\nexport async function getJob(id: string, userId?: number | bigint): Promise<Job | undefined> {\n  const db = getDb()\n  const res = userId\n    ? await db.execute({ sql: 'SELECT * FROM jobs WHERE id = ? AND user_id = ?', args: [id, userId] })\n    : await db.execute({ sql: 'SELECT * FROM jobs WHERE id = ?', args: [id] })\n  return res.rows[0] ? deserialize(rowToRawJob(res.rows[0] as unknown as Record<string, unknown>)) : undefined\n}\n\nexport async function listJobs(userId: number | bigint): Promise<Job[]> {\n  const res = await getDb().execute({ sql: 'SELECT * FROM jobs WHERE user_id = ? ORDER BY created_at DESC LIMIT 50', args: [userId] })\n  return res.rows.map(r => deserialize(rowToRawJob(r as unknown as Record<string, unknown>)))\n}\n\nexport interface PageSnapshotRow {\n  contentHash: string\n  content: string\n  title: string | null\n  contentBytes: number\n  truncated: boolean\n  checkedAt: string\n}\n\nexport async function getPageSnapshot(userId: number | bigint, url: string): Promise<PageSnapshotRow | null> {\n  const res = await getDb().execute({\n    sql: 'SELECT content_hash, content, title, content_bytes, truncated, checked_at FROM page_snapshots WHERE user_id = ? AND url = ?',\n    args: [userId, url],\n  })\n  const row = res.rows[0] as unknown as Record<string, unknown> | undefined\n  if (!row) return null\n  return {\n    contentHash: String(row.content_hash),\n    content: String(row.content),\n    title: row.title == null ? null : String(row.title),\n    contentBytes: Number(row.content_bytes),\n    truncated: Number(row.truncated) === 1,\n    checkedAt: String(row.checked_at),\n  }\n}\n\nexport interface UpsertPageSnapshotInput {\n  contentHash: string\n  content: string\n  title: string | null\n  contentBytes: number\n  truncated: boolean\n}\n\nexport async function upsertPageSnapshot(userId: number | bigint, url: string, snapshot: UpsertPageSnapshotInput): Promise<void> {\n  await getDb().execute({\n    sql: `\n      INSERT INTO page_snapshots (user_id, url, content_hash, content, title, content_bytes, truncated, checked_at)\n      VALUES (?, ?, ?, ?, ?, ?, ?, datetime('now'))\n      ON CONFLICT(user_id, url) DO UPDATE SET\n        content_hash  = excluded.content_hash,\n        content       = excluded.content,\n        title         = excluded.title,\n        content_bytes = excluded.content_bytes,\n        truncated     = excluded.truncated,\n        checked_at    = excluded.checked_at\n    `,\n    args: [userId, url, snapshot.contentHash, snapshot.content, snapshot.title, snapshot.contentBytes, snapshot.truncated ? 1 : 0],\n  })\n}\n\nexport interface RequestEvent {\n  id: string\n  user_id: number\n  source: string\n  status: string\n  query: string\n  location: string | null\n  result_count: number | null\n  result: unknown | null\n  error: string | null\n  created_at: string\n}\n\nconst REQUEST_EVENT_RESULT_MAX_BYTES = 32 * 1024\n\nfunction serializeRequestEventResult(result: unknown): string | null {\n  if (result == null) return null\n  const serialized = JSON.stringify(result)\n  const bytes = Buffer.byteLength(serialized, 'utf8')\n  if (bytes <= REQUEST_EVENT_RESULT_MAX_BYTES) return serialized\n\n  // request_events powers the lightweight history list; the full response is\n  // already returned to the caller (and, for jobs/exports, stored in its\n  // canonical artifact). Keeping another multi-megabyte copy here grew the\n  // production database by gigabytes without any product consumer reading it.\n  return JSON.stringify({\n    truncated: true,\n    originalBytes: bytes,\n    sha256: createHash('sha256').update(serialized).digest('hex'),\n  })\n}\n\nfunction rowToRequestEvent(row: Record<string, unknown>): RequestEvent {\n  const rawResult = row.result != null ? String(row.result) : null\n  return {\n    id: String(row.id),\n    user_id: Number(row.user_id),\n    source: String(row.source),\n    status: String(row.status),\n    query: String(row.query),\n    location: row.location != null ? String(row.location) : null,\n    result_count: row.result_count != null ? Number(row.result_count) : null,\n    result: rawResult ? JSON.parse(rawResult) : null,\n    error: row.error != null ? String(row.error) : null,\n    created_at: String(row.created_at),\n  }\n}\n\nexport async function logRequestEvent(input: {\n  userId: number | bigint\n  source: string\n  status: 'done' | 'failed'\n  query: string\n  location?: string | null\n  resultCount?: number | null\n  result?: unknown\n  error?: string | null\n}): Promise<string> {\n  const id = randomUUID()\n  await getDb().execute({\n    sql: `\n      INSERT INTO request_events (id, user_id, source, status, query, location, result_count, result, error)\n      VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)\n    `,\n    args: [\n      id,\n      input.userId,\n      input.source,\n      input.status,\n      input.query,\n      input.location ?? null,\n      input.resultCount ?? null,\n      serializeRequestEventResult(input.result),\n      input.error ?? null,\n    ],\n  })\n  return id\n}\n\nexport async function listRequestEvents(userId: number | bigint, limit = 100): Promise<RequestEvent[]> {\n  const res = await getDb().execute({\n    sql: `\n      SELECT id, user_id, source, status, query, location, result_count, NULL AS result, error, created_at\n      FROM request_events\n      WHERE user_id = ?\n      ORDER BY created_at DESC\n      LIMIT ?\n    `,\n    args: [userId, limit],\n  })\n  return res.rows.map(r => rowToRequestEvent(r as unknown as Record<string, unknown>))\n}\n\nexport type WorkflowScheduleStatus = 'active' | 'paused' | 'deleted'\nexport type WorkflowCadence = 'daily' | 'weekly' | 'monthly'\nexport type WorkflowRunStatus = 'queued' | 'running' | 'succeeded' | 'partial' | 'failed' | 'cancelled'\n\nexport interface WorkflowScheduleRecord {\n  id: string\n  user_id: number\n  workflow_id: string\n  name: string\n  input: Record<string, unknown>\n  cadence: WorkflowCadence\n  cron_expr: string | null\n  timezone: string\n  webhook_url: string | null\n  status: WorkflowScheduleStatus\n  next_run_at: string | null\n  last_run_at: string | null\n  created_at: string\n  updated_at: string\n}\n\nexport interface WorkflowRunRecord {\n  id: string\n  user_id: number\n  schedule_id: string | null\n  workflow_id: string\n  input: Record<string, unknown>\n  status: WorkflowRunStatus\n  manifest: unknown | null\n  error_message: string | null\n  queued_at: string\n  started_at: string | null\n  completed_at: string | null\n  idempotency_key: string | null\n  step_index: number\n  total_steps: number | null\n  step_log: WorkflowStepLogEntry[]\n  webhook_url: string | null\n}\n\nexport interface WorkflowStepLogEntry {\n  stepIndex: number\n  stepId: string\n  stepTitle: string\n  output: Record<string, unknown>\n  warnings: string[]\n  at: string\n}\n\nexport interface WorkflowArtifactRecord {\n  id: string\n  run_id: string\n  user_id: number\n  kind: string\n  label: string\n  path: string\n  content_type: string\n  bytes: number | null\n  rows_count: number | null\n  content: string | null\n  created_at: string\n}\n\nfunction parseJsonRecordValue(value: unknown): Record<string, unknown> {\n  if (value == null) return {}\n  try {\n    const parsed = JSON.parse(String(value)) as unknown\n    return parsed && typeof parsed === 'object' && !Array.isArray(parsed) ? parsed as Record<string, unknown> : {}\n  } catch {\n    return {}\n  }\n}\n\nfunction rowToWorkflowSchedule(row: Record<string, unknown>): WorkflowScheduleRecord {\n  return {\n    id: String(row.id),\n    user_id: Number(row.user_id),\n    workflow_id: String(row.workflow_id),\n    name: String(row.name),\n    input: parseJsonRecordValue(row.input_json),\n    cadence: String(row.cadence) as WorkflowCadence,\n    cron_expr: row.cron_expr != null ? String(row.cron_expr) : null,\n    timezone: String(row.timezone ?? 'UTC'),\n    webhook_url: row.webhook_url != null ? String(row.webhook_url) : null,\n    status: String(row.status) as WorkflowScheduleStatus,\n    next_run_at: row.next_run_at != null ? String(row.next_run_at) : null,\n    last_run_at: row.last_run_at != null ? String(row.last_run_at) : null,\n    created_at: String(row.created_at),\n    updated_at: String(row.updated_at),\n  }\n}\n\nfunction rowToWorkflowRun(row: Record<string, unknown>): WorkflowRunRecord {\n  return {\n    id: String(row.id),\n    user_id: Number(row.user_id),\n    schedule_id: row.schedule_id != null ? String(row.schedule_id) : null,\n    workflow_id: String(row.workflow_id),\n    input: parseJsonRecordValue(row.input_json),\n    status: String(row.status) as WorkflowRunStatus,\n    manifest: row.manifest_json != null ? parseNullableJson(row.manifest_json) : null,\n    error_message: row.error_message != null ? String(row.error_message) : null,\n    queued_at: String(row.queued_at),\n    started_at: row.started_at != null ? String(row.started_at) : null,\n    completed_at: row.completed_at != null ? String(row.completed_at) : null,\n    idempotency_key: row.idempotency_key != null ? String(row.idempotency_key) : null,\n    step_index: row.step_index != null ? Number(row.step_index) : 0,\n    total_steps: row.total_steps != null ? Number(row.total_steps) : null,\n    step_log: parseWorkflowStepLog(row.step_log_json),\n    webhook_url: row.webhook_url != null ? String(row.webhook_url) : null,\n  }\n}\n\nfunction parseWorkflowStepLog(value: unknown): WorkflowStepLogEntry[] {\n  if (value == null) return []\n  try {\n    const parsed = JSON.parse(String(value)) as unknown\n    return Array.isArray(parsed) ? parsed as WorkflowStepLogEntry[] : []\n  } catch {\n    return []\n  }\n}\n\nfunction rowToWorkflowArtifact(row: Record<string, unknown>): WorkflowArtifactRecord {\n  return {\n    id: String(row.id),\n    run_id: String(row.run_id),\n    user_id: Number(row.user_id),\n    kind: String(row.kind),\n    label: String(row.label),\n    path: String(row.path),\n    content_type: String(row.content_type),\n    bytes: row.bytes != null ? Number(row.bytes) : null,\n    rows_count: row.rows_count != null ? Number(row.rows_count) : null,\n    content: row.content != null ? String(row.content) : null,\n    created_at: String(row.created_at),\n  }\n}\n\nexport async function createWorkflowSchedule(input: {\n  userId: number | bigint\n  workflowId: string\n  name: string\n  workflowInput: Record<string, unknown>\n  cadence: WorkflowCadence\n  timezone?: string\n  webhookUrl?: string | null\n  nextRunAt?: string | null\n}): Promise<WorkflowScheduleRecord> {\n  const id = randomUUID()\n  await getDb().execute({\n    sql: `\n      INSERT INTO workflow_schedules (\n        id, user_id, workflow_id, name, input_json, cadence, timezone, webhook_url, next_run_at\n      )\n      VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)\n    `,\n    args: [\n      id,\n      input.userId,\n      input.workflowId,\n      input.name,\n      JSON.stringify(input.workflowInput),\n      input.cadence,\n      input.timezone ?? 'UTC',\n      input.webhookUrl ?? null,\n      input.nextRunAt ?? null,\n    ],\n  })\n  const schedule = await getWorkflowSchedule(id, input.userId)\n  if (!schedule) throw new Error('Failed to create workflow schedule')\n  return schedule\n}\n\nexport async function getWorkflowSchedule(id: string, userId?: number | bigint): Promise<WorkflowScheduleRecord | null> {\n  const res = userId == null\n    ? await getDb().execute({ sql: 'SELECT * FROM workflow_schedules WHERE id = ? AND status != ? LIMIT 1', args: [id, 'deleted'] })\n    : await getDb().execute({ sql: 'SELECT * FROM workflow_schedules WHERE id = ? AND user_id = ? AND status != ? LIMIT 1', args: [id, userId, 'deleted'] })\n  return res.rows[0] ? rowToWorkflowSchedule(res.rows[0] as unknown as Record<string, unknown>) : null\n}\n\nexport async function listWorkflowSchedules(userId: number | bigint): Promise<WorkflowScheduleRecord[]> {\n  const res = await getDb().execute({\n    sql: `SELECT * FROM workflow_schedules WHERE user_id = ? AND status != 'deleted' ORDER BY created_at DESC LIMIT 100`,\n    args: [userId],\n  })\n  return res.rows.map(r => rowToWorkflowSchedule(r as unknown as Record<string, unknown>))\n}\n\nexport async function patchWorkflowSchedule(input: {\n  id: string\n  userId: number | bigint\n  name?: string\n  status?: WorkflowScheduleStatus\n  workflowInput?: Record<string, unknown>\n  cadence?: WorkflowCadence\n  timezone?: string\n  webhookUrl?: string | null\n  nextRunAt?: string | null\n}): Promise<WorkflowScheduleRecord | null> {\n  const sets: string[] = []\n  const args: Array<string | number | bigint | null> = []\n  if (input.name !== undefined) { sets.push('name = ?'); args.push(input.name) }\n  if (input.status !== undefined) { sets.push('status = ?'); args.push(input.status) }\n  if (input.workflowInput !== undefined) { sets.push('input_json = ?'); args.push(JSON.stringify(input.workflowInput)) }\n  if (input.cadence !== undefined) { sets.push('cadence = ?'); args.push(input.cadence) }\n  if (input.timezone !== undefined) { sets.push('timezone = ?'); args.push(input.timezone) }\n  if (input.webhookUrl !== undefined) { sets.push('webhook_url = ?'); args.push(input.webhookUrl) }\n  if (input.nextRunAt !== undefined) { sets.push('next_run_at = ?'); args.push(input.nextRunAt) }\n  if (sets.length === 0) return getWorkflowSchedule(input.id, input.userId)\n  sets.push(`updated_at = datetime('now')`)\n  await getDb().execute({\n    sql: `UPDATE workflow_schedules SET ${sets.join(', ')} WHERE id = ? AND user_id = ? AND status != 'deleted'`,\n    args: [...args, input.id, input.userId],\n  })\n  return getWorkflowSchedule(input.id, input.userId)\n}\n\nexport async function deleteWorkflowSchedule(id: string, userId: number | bigint): Promise<boolean> {\n  const res = await getDb().execute({\n    sql: `UPDATE workflow_schedules SET status = 'deleted', updated_at = datetime('now') WHERE id = ? AND user_id = ? AND status != 'deleted'`,\n    args: [id, userId],\n  })\n  return res.rowsAffected > 0\n}\n\nexport async function listDueWorkflowSchedules(nowIso: string, limit = 10): Promise<WorkflowScheduleRecord[]> {\n  const res = await getDb().execute({\n    sql: `\n      SELECT * FROM workflow_schedules\n      WHERE status = 'active' AND next_run_at IS NOT NULL AND next_run_at <= ?\n      ORDER BY next_run_at ASC\n      LIMIT ?\n    `,\n    args: [nowIso, limit],\n  })\n  return res.rows.map(r => rowToWorkflowSchedule(r as unknown as Record<string, unknown>))\n}\n\nexport async function markWorkflowScheduleRan(id: string, lastRunAt: string, nextRunAt: string | null): Promise<void> {\n  await getDb().execute({\n    sql: `UPDATE workflow_schedules SET last_run_at = ?, next_run_at = ?, updated_at = datetime('now') WHERE id = ?`,\n    args: [lastRunAt, nextRunAt, id],\n  })\n}\n\nexport async function createWorkflowRun(input: {\n  id?: string\n  userId: number | bigint\n  workflowId: string\n  workflowInput: Record<string, unknown>\n  scheduleId?: string | null\n  idempotencyKey?: string | null\n  webhookUrl?: string | null\n  totalSteps?: number | null\n}): Promise<WorkflowRunRecord> {\n  const id = input.id ?? randomUUID()\n  const args = [\n    id,\n    input.userId,\n    input.scheduleId ?? null,\n    input.workflowId,\n    JSON.stringify(input.workflowInput),\n    input.idempotencyKey ?? null,\n    input.webhookUrl ?? null,\n    input.totalSteps ?? null,\n  ]\n  try {\n    await getDb().execute({\n      sql: `\n        INSERT INTO workflow_runs (id, user_id, schedule_id, workflow_id, input_json, idempotency_key, webhook_url, total_steps)\n        VALUES (?, ?, ?, ?, ?, ?, ?, ?)\n      `,\n      args,\n    })\n  } catch (err) {\n    if (input.idempotencyKey) {\n      const existing = await getWorkflowRunByIdempotencyKey(input.idempotencyKey)\n      if (existing) return existing\n    }\n    throw err\n  }\n  const run = await getWorkflowRun(id, input.userId)\n  if (!run) throw new Error('Failed to create workflow run')\n  return run\n}\n\nexport async function getWorkflowRun(id: string, userId?: number | bigint): Promise<WorkflowRunRecord | null> {\n  const res = userId == null\n    ? await getDb().execute({ sql: 'SELECT * FROM workflow_runs WHERE id = ? LIMIT 1', args: [id] })\n    : await getDb().execute({ sql: 'SELECT * FROM workflow_runs WHERE id = ? AND user_id = ? LIMIT 1', args: [id, userId] })\n  return res.rows[0] ? rowToWorkflowRun(res.rows[0] as unknown as Record<string, unknown>) : null\n}\n\nexport async function getWorkflowRunByIdempotencyKey(idempotencyKey: string): Promise<WorkflowRunRecord | null> {\n  const res = await getDb().execute({ sql: 'SELECT * FROM workflow_runs WHERE idempotency_key = ? LIMIT 1', args: [idempotencyKey] })\n  return res.rows[0] ? rowToWorkflowRun(res.rows[0] as unknown as Record<string, unknown>) : null\n}\n\nexport async function listWorkflowRuns(userId: number | bigint, limit = 50): Promise<WorkflowRunRecord[]> {\n  const res = await getDb().execute({\n    sql: 'SELECT * FROM workflow_runs WHERE user_id = ? ORDER BY queued_at DESC LIMIT ?',\n    args: [userId, limit],\n  })\n  return res.rows.map(r => rowToWorkflowRun(r as unknown as Record<string, unknown>))\n}\n\nexport async function markWorkflowRunRunning(id: string): Promise<void> {\n  await getDb().execute({\n    sql: `UPDATE workflow_runs SET status = 'running', started_at = COALESCE(started_at, datetime('now')) WHERE id = ?`,\n    args: [id],\n  })\n}\n\nexport async function completeWorkflowRunRecord(id: string, status: Exclude<WorkflowRunStatus, 'queued' | 'running' | 'cancelled'>, manifest: unknown): Promise<void> {\n  await getDb().execute({\n    sql: `UPDATE workflow_runs SET status = ?, manifest_json = ?, completed_at = datetime('now') WHERE id = ?`,\n    args: [status, JSON.stringify(manifest), id],\n  })\n}\n\nexport async function failWorkflowRunRecord(id: string, errorMessage: string, manifest?: unknown): Promise<void> {\n  await getDb().execute({\n    sql: `UPDATE workflow_runs SET status = 'failed', error_message = ?, manifest_json = ?, completed_at = datetime('now') WHERE id = ?`,\n    args: [errorMessage, manifest == null ? null : JSON.stringify(manifest), id],\n  })\n}\n\nfunction workflowArtifactContentType(kind: string): string {\n  if (kind === 'html_report') return 'text/html; charset=utf-8'\n  if (kind === 'csv') return 'text/csv; charset=utf-8'\n  if (kind === 'json') return 'application/json; charset=utf-8'\n  return 'text/plain; charset=utf-8'\n}\n\nexport interface WorkflowArtifactInput {\n  kind: string\n  label: string\n  path: string\n  bytes?: number\n  rows?: number\n  content?: string | null\n}\n\nasync function insertWorkflowArtifact(runId: string, userId: number | bigint, artifact: WorkflowArtifactInput): Promise<void> {\n  await getDb().execute({\n    sql: `\n      INSERT INTO workflow_artifacts (id, run_id, user_id, kind, label, path, content_type, bytes, rows_count, content)\n      VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)\n    `,\n    args: [\n      randomUUID(),\n      runId,\n      userId,\n      artifact.kind,\n      artifact.label,\n      artifact.path,\n      workflowArtifactContentType(artifact.kind),\n      artifact.bytes ?? null,\n      artifact.rows ?? null,\n      artifact.content ?? null,\n    ],\n  })\n}\n\nexport async function replaceWorkflowArtifacts(runId: string, userId: number | bigint, artifacts: WorkflowArtifactInput[]): Promise<WorkflowArtifactRecord[]> {\n  await getDb().execute({ sql: 'DELETE FROM workflow_artifacts WHERE run_id = ? AND user_id = ?', args: [runId, userId] })\n  for (const artifact of artifacts) await insertWorkflowArtifact(runId, userId, artifact)\n  return listWorkflowArtifacts(runId, userId)\n}\n\nexport async function appendWorkflowArtifacts(runId: string, userId: number | bigint, artifacts: WorkflowArtifactInput[]): Promise<WorkflowArtifactRecord[]> {\n  for (const artifact of artifacts) await insertWorkflowArtifact(runId, userId, artifact)\n  return listWorkflowArtifacts(runId, userId)\n}\n\nexport async function getWorkflowRunStepState(id: string, userId?: number | bigint): Promise<{ stepIndex: number; totalSteps: number | null; state: unknown; stepLog: WorkflowStepLogEntry[] } | null> {\n  const res = userId == null\n    ? await getDb().execute({ sql: 'SELECT step_index, total_steps, step_state_json, step_log_json FROM workflow_runs WHERE id = ? LIMIT 1', args: [id] })\n    : await getDb().execute({ sql: 'SELECT step_index, total_steps, step_state_json, step_log_json FROM workflow_runs WHERE id = ? AND user_id = ? LIMIT 1', args: [id, userId] })\n  const row = res.rows[0] as unknown as Record<string, unknown> | undefined\n  if (!row) return null\n  return {\n    stepIndex: row.step_index != null ? Number(row.step_index) : 0,\n    totalSteps: row.total_steps != null ? Number(row.total_steps) : null,\n    state: row.step_state_json != null ? parseNullableJson(row.step_state_json) : null,\n    stepLog: parseWorkflowStepLog(row.step_log_json),\n  }\n}\n\nexport async function advanceWorkflowRunStep(id: string, input: {\n  stepIndex: number\n  totalSteps: number\n  state: unknown\n  status: WorkflowRunStatus\n  logEntry: WorkflowStepLogEntry\n  appendLog: WorkflowStepLogEntry[]\n}): Promise<void> {\n  const stepLog = [...input.appendLog, input.logEntry]\n  await getDb().execute({\n    sql: `UPDATE workflow_runs SET status = ?, step_index = ?, total_steps = ?, step_state_json = ?, step_log_json = ?, started_at = COALESCE(started_at, datetime('now')) WHERE id = ?`,\n    args: [input.status, input.stepIndex, input.totalSteps, JSON.stringify(input.state ?? null), JSON.stringify(stepLog), id],\n  })\n}\n\nexport async function listWorkflowArtifacts(runId: string, userId: number | bigint): Promise<WorkflowArtifactRecord[]> {\n  const res = await getDb().execute({\n    sql: 'SELECT * FROM workflow_artifacts WHERE run_id = ? AND user_id = ? ORDER BY created_at ASC',\n    args: [runId, userId],\n  })\n  return res.rows.map(r => rowToWorkflowArtifact(r as unknown as Record<string, unknown>))\n}\n\nexport async function getWorkflowArtifact(id: string, userId: number | bigint): Promise<WorkflowArtifactRecord | null> {\n  const res = await getDb().execute({\n    sql: 'SELECT * FROM workflow_artifacts WHERE id = ? AND user_id = ? LIMIT 1',\n    args: [id, userId],\n  })\n  return res.rows[0] ? rowToWorkflowArtifact(res.rows[0] as unknown as Record<string, unknown>) : null\n}\n\nexport async function recordWorkflowWebhookDelivery(input: {\n  runId: string\n  scheduleId?: string | null\n  userId: number | bigint\n  webhookUrl: string\n  status: 'delivered' | 'failed'\n  attempts: number\n  lastError?: string | null\n}): Promise<void> {\n  await getDb().execute({\n    sql: `\n      INSERT INTO workflow_webhook_deliveries (\n        id, run_id, schedule_id, user_id, webhook_url, status, attempts, last_error, delivered_at\n      )\n      VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)\n    `,\n    args: [\n      randomUUID(),\n      input.runId,\n      input.scheduleId ?? null,\n      input.userId,\n      input.webhookUrl,\n      input.status,\n      input.attempts,\n      input.lastError ?? null,\n      input.status === 'delivered' ? new Date().toISOString() : null,\n    ],\n  })\n}\n\nexport async function countActiveUsers(): Promise<number> {\n  const res = await getDb().execute(`SELECT COUNT(*) as n FROM users WHERE active = 1`)\n  return Number(res.rows[0]?.n ?? 0)\n}\n\nexport async function countActiveJobsForUser(userId: number | bigint): Promise<number> {\n  const res = await getDb().execute({\n    sql: `SELECT COUNT(*) as n FROM jobs WHERE user_id = ? AND (status = 'pending' OR (status = 'running' AND started_at > datetime('now', '-10 minutes')))`,\n    args: [userId],\n  })\n  return Number(res.rows[0]?.n ?? 0)\n}\n\nexport async function claimPendingJob(): Promise<RawJob | undefined> {\n  const db = getDb()\n  const res = await db.execute(`\n    SELECT * FROM jobs\n    WHERE status = 'pending'\n      AND COALESCE(json_extract(options, '$.executionOwner'), 'cron') != 'inngest'\n    ORDER BY created_at\n    LIMIT 1\n  `)\n  const job = res.rows[0] ? rowToRawJob(res.rows[0] as unknown as Record<string, unknown>) : undefined\n  if (!job) return undefined\n  const upd = await db.execute({\n    sql: `UPDATE jobs SET status = 'running', started_at = datetime('now') WHERE id = ? AND status = 'pending'`,\n    args: [job.id],\n  })\n  return upd.rowsAffected === 0 ? undefined : { ...job, status: 'running' }\n}\n\n/** Claim one durable PAA job without allowing a second worker to own it. */\nexport async function claimInngestPaaJob(id: string): Promise<Job | undefined> {\n  const db = getDb()\n  const current = await getJob(id)\n  if (!current || current.options.executionOwner !== 'inngest' || current.options.serpOnly === true) return undefined\n  if (current.status === 'running') return undefined\n  if (current.status === 'pending') {\n    const updated = await db.execute({\n      sql: `UPDATE jobs\n            SET status = 'running', started_at = COALESCE(started_at, datetime('now')), error = NULL\n            WHERE id = ? AND status = 'pending'`,\n      args: [id],\n    })\n    if (updated.rowsAffected === 0) return getJob(id)\n    return { ...current, status: 'running', started_at: current.started_at ?? new Date().toISOString() }\n  }\n  return current\n}\n\n/** Persist recoverable browser progress while keeping the job non-terminal. */\nexport async function checkpointJob(id: string, result: object): Promise<boolean> {\n  const updated = await getDb().execute({\n    sql: `UPDATE jobs SET result = ? WHERE id = ? AND status = 'running'`,\n    args: [JSON.stringify(result), id],\n  })\n  return updated.rowsAffected === 1\n}\n\nexport async function listPendingInngestPaaJobs(limit = 25): Promise<Job[]> {\n  const safeLimit = Math.max(1, Math.min(100, Math.floor(limit)))\n  const rows = await getDb().execute({\n    sql: `SELECT * FROM jobs\n          WHERE status = 'pending'\n            AND json_extract(options, '$.executionOwner') = 'inngest'\n            AND COALESCE(json_extract(options, '$.serpOnly'), 0) = 0\n          ORDER BY created_at\n          LIMIT ?`,\n    args: [safeLimit],\n  })\n  return rows.rows.map(row => deserialize(rowToRawJob(row as unknown as Record<string, unknown>)))\n}\n\nexport async function listRecentTerminalInngestPaaJobs(limit = 25): Promise<Job[]> {\n  const safeLimit = Math.max(1, Math.min(100, Math.floor(limit)))\n  const rows = await getDb().execute({\n    sql: `SELECT * FROM jobs\n          WHERE status IN ('done', 'failed', 'cancelled')\n            AND json_extract(options, '$.executionOwner') = 'inngest'\n            AND json_extract(options, '$.billingDebitKey') IS NOT NULL\n            AND json_extract(options, '$.billingSettledAt') IS NULL\n          ORDER BY completed_at DESC\n          LIMIT ?`,\n    args: [safeLimit],\n  })\n  return rows.rows.map(row => deserialize(rowToRawJob(row as unknown as Record<string, unknown>)))\n}\n\nexport async function listStaleRunningInngestPaaJobs(limit = 25): Promise<Job[]> {\n  const safeLimit = Math.max(1, Math.min(100, Math.floor(limit)))\n  const rows = await getDb().execute({\n    sql: `SELECT * FROM jobs\n          WHERE status = 'running'\n            AND started_at <= datetime('now', '-10 minutes')\n            AND json_extract(options, '$.executionOwner') = 'inngest'\n          ORDER BY started_at\n          LIMIT ?`,\n    args: [safeLimit],\n  })\n  return rows.rows.map(row => deserialize(rowToRawJob(row as unknown as Record<string, unknown>)))\n}\n\nexport async function markJobBillingSettled(id: string, billedMc: number, settledAt = new Date().toISOString()): Promise<boolean> {\n  if (!Number.isSafeInteger(billedMc) || billedMc < 0) throw new Error('job billed amount must be a non-negative integer')\n  const updated = await getDb().execute({\n    sql: `UPDATE jobs\n          SET options = json_set(options, '$.billingSettledAt', ?, '$.billedMc', ?)\n          WHERE id = ?`,\n    args: [settledAt, billedMc, id],\n  })\n  return updated.rowsAffected === 1\n}\n\nexport async function completeJob(id: string, result: object): Promise<void> {\n  await getDb().execute({ sql: `UPDATE jobs SET status = 'done', result = ?, completed_at = datetime('now') WHERE id = ?`, args: [JSON.stringify(result), id] })\n}\n\nexport async function failJob(id: string, error: string): Promise<void> {\n  await getDb().execute({ sql: `UPDATE jobs SET status = 'failed', error = ?, completed_at = datetime('now') WHERE id = ?`, args: [error, id] })\n}\n\nexport async function cancelJob(id: string, reason: string): Promise<void> {\n  await getDb().execute({ sql: `UPDATE jobs SET status = 'cancelled', error = ?, completed_at = datetime('now') WHERE id = ?`, args: [reason, id] })\n}\n\nexport interface KpoJobRow {\n  id: string\n  user_id: number\n  status: string\n  entity_name: string\n  entity_type: string\n  url: string | null\n  request: string\n  result: string | null\n  error: string | null\n  created_at: string\n  updated_at: string\n}\n\nexport interface KpoPhaseLogRow {\n  id: number\n  job_id: string\n  phase: string\n  output: string\n  created_at: string\n}\n\nfunction rowToKpoJob(row: Record<string, unknown>): KpoJobRow {\n  return {\n    id: String(row.id),\n    user_id: Number(row.user_id),\n    status: String(row.status),\n    entity_name: String(row.entity_name),\n    entity_type: String(row.entity_type),\n    url: row.url != null ? String(row.url) : null,\n    request: String(row.request),\n    result: row.result != null ? String(row.result) : null,\n    error: row.error != null ? String(row.error) : null,\n    created_at: String(row.created_at),\n    updated_at: String(row.updated_at),\n  }\n}\n\nfunction rowToKpoPhaseLog(row: Record<string, unknown>): KpoPhaseLogRow {\n  return {\n    id: Number(row.id),\n    job_id: String(row.job_id),\n    phase: String(row.phase),\n    output: String(row.output),\n    created_at: String(row.created_at),\n  }\n}\n\nexport async function createKpoJob(jobId: string, userId: number, entityName: string, entityType: string, request: object, url?: string): Promise<void> {\n  await getDb().execute({\n    sql: `INSERT INTO kpo_jobs (id, user_id, status, entity_name, entity_type, url, request) VALUES (?, ?, 'pending', ?, ?, ?, ?)`,\n    args: [jobId, userId, entityName, entityType, url ?? null, JSON.stringify(request)],\n  })\n}\n\nexport async function claimPendingKpoJob(): Promise<KpoJobRow | null> {\n  const db = getDb()\n  const upd = await db.execute(\n    `UPDATE kpo_jobs SET status='running', updated_at=datetime('now') WHERE id = (SELECT id FROM kpo_jobs WHERE status='pending' ORDER BY created_at ASC LIMIT 1) RETURNING *`\n  )\n  if (upd.rowsAffected === 0) return null\n  return upd.rows[0] ? rowToKpoJob(upd.rows[0] as unknown as Record<string, unknown>) : null\n}\n\nexport async function getKpoJob(jobId: string): Promise<KpoJobRow | null> {\n  const res = await getDb().execute({ sql: `SELECT * FROM kpo_jobs WHERE id = ?`, args: [jobId] })\n  return res.rows[0] ? rowToKpoJob(res.rows[0] as unknown as Record<string, unknown>) : null\n}\n\nexport async function updateKpoJobState(jobId: string, status: string): Promise<void> {\n  await getDb().execute({ sql: `UPDATE kpo_jobs SET status = ?, updated_at = datetime('now') WHERE id = ?`, args: [status, jobId] })\n}\n\nexport async function completeKpoJob(jobId: string, result: object): Promise<void> {\n  await getDb().execute({ sql: `UPDATE kpo_jobs SET status = 'done', result = ?, updated_at = datetime('now') WHERE id = ?`, args: [JSON.stringify(result), jobId] })\n}\n\nexport async function failKpoJob(jobId: string, error: string): Promise<void> {\n  await getDb().execute({ sql: `UPDATE kpo_jobs SET status = 'failed', error = ?, updated_at = datetime('now') WHERE id = ?`, args: [error, jobId] })\n}\n\nexport async function logKpoPhaseComplete(jobId: string, phase: string, output: unknown): Promise<void> {\n  await getDb().execute({\n    sql: `INSERT INTO kpo_phase_log (job_id, phase, output) VALUES (?, ?, ?)`,\n    args: [jobId, phase, JSON.stringify(output)],\n  })\n}\n\nexport async function getKpoPhaseLog(jobId: string): Promise<KpoPhaseLogRow[]> {\n  const res = await getDb().execute({ sql: `SELECT * FROM kpo_phase_log WHERE job_id = ? ORDER BY created_at ASC`, args: [jobId] })\n  return res.rows.map(r => rowToKpoPhaseLog(r as unknown as Record<string, unknown>))\n}\n\nexport async function listKpoJobs(userId?: number): Promise<KpoJobRow[]> {\n  const res = userId != null\n    ? await getDb().execute({ sql: `SELECT * FROM kpo_jobs WHERE user_id = ? ORDER BY created_at DESC LIMIT 50`, args: [userId] })\n    : await getDb().execute(`SELECT * FROM kpo_jobs ORDER BY created_at DESC LIMIT 50`)\n  return res.rows.map(r => rowToKpoJob(r as unknown as Record<string, unknown>))\n}\n\nexport async function getUserByStripeCustomerId(customerId: string): Promise<User | undefined> {\n  const res = await getDb().execute({\n    sql: 'SELECT * FROM users WHERE stripe_customer_id = ? AND active = 1',\n    args: [customerId],\n  })\n  return res.rows[0] ? rowToUser(res.rows[0] as unknown as Record<string, unknown>) : undefined\n}\n\nexport async function setStripeCustomerId(userId: number, customerId: string): Promise<void> {\n  await getDb().execute({\n    sql: 'UPDATE users SET stripe_customer_id = ? WHERE id = ?',\n    args: [customerId, userId],\n  })\n}\n\nexport async function setExtraConcurrencySlots(userId: number, slots: number): Promise<void> {\n  await getDb().execute({\n    sql: 'UPDATE users SET extra_concurrency_slots = ? WHERE id = ?',\n    args: [Math.max(0, slots), userId],\n  })\n}\n\nexport async function setConcurrencySubId(userId: number, subId: string | null): Promise<void> {\n  await getDb().execute({\n    sql: 'UPDATE users SET concurrency_stripe_sub_id = ? WHERE id = ?',\n    args: [subId, userId],\n  })\n}\n\nexport async function setSubscriptionTier(userId: number, tier: string | null, concurrency: number, subId: string | null): Promise<void> {\n  await getDb().execute({\n    sql: 'UPDATE users SET subscription_tier = ?, subscription_concurrency = ?, subscription_id = ? WHERE id = ?',\n    args: [tier, Math.max(0, Math.round(concurrency)), subId, userId],\n  })\n}\n\nexport type ConnectedAccountBillingStatus = 'pending' | 'synced' | 'blocked' | 'error'\n\nexport interface ConnectedAccountBillingState {\n  user_id: number\n  stripe_subscription_id: string | null\n  stripe_subscription_item_id: string | null\n  price_id: string | null\n  quantity: number\n  status: ConnectedAccountBillingStatus\n  last_error_code: string | null\n  checked_at: string\n  synced_at: string | null\n  updated_at: string\n}\n\nfunction rowToConnectedAccountBillingState(row: Record<string, unknown>): ConnectedAccountBillingState {\n  return {\n    user_id: Number(row.user_id),\n    stripe_subscription_id: row.stripe_subscription_id != null ? String(row.stripe_subscription_id) : null,\n    stripe_subscription_item_id: row.stripe_subscription_item_id != null ? String(row.stripe_subscription_item_id) : null,\n    price_id: row.price_id != null ? String(row.price_id) : null,\n    quantity: Math.max(0, Number(row.quantity ?? 0)),\n    status: (['pending', 'synced', 'blocked', 'error'].includes(String(row.status)) ? String(row.status) : 'error') as ConnectedAccountBillingStatus,\n    last_error_code: row.last_error_code != null ? String(row.last_error_code) : null,\n    checked_at: String(row.checked_at),\n    synced_at: row.synced_at != null ? String(row.synced_at) : null,\n    updated_at: String(row.updated_at),\n  }\n}\n\nexport async function getConnectedAccountBillingState(userId: number | bigint): Promise<ConnectedAccountBillingState | null> {\n  const res = await getDb().execute({\n    sql: 'SELECT * FROM connected_account_billing WHERE user_id = ? LIMIT 1',\n    args: [userId],\n  })\n  const row = res.rows[0] as unknown as Record<string, unknown> | undefined\n  return row ? rowToConnectedAccountBillingState(row) : null\n}\n\nexport async function setConnectedAccountBillingState(input: {\n  userId: number | bigint\n  stripeSubscriptionId: string | null\n  stripeSubscriptionItemId: string | null\n  priceId: string | null\n  quantity: number\n  status: ConnectedAccountBillingStatus\n  lastErrorCode?: string | null\n  synced?: boolean\n}): Promise<ConnectedAccountBillingState> {\n  const quantity = Math.max(0, Math.round(input.quantity))\n  await getDb().execute({\n    sql: `INSERT INTO connected_account_billing\n      (user_id, stripe_subscription_id, stripe_subscription_item_id, price_id, quantity, status, last_error_code, checked_at, synced_at, updated_at)\n      VALUES (?, ?, ?, ?, ?, ?, ?, datetime('now'), CASE WHEN ? THEN datetime('now') ELSE NULL END, datetime('now'))\n      ON CONFLICT(user_id) DO UPDATE SET\n        stripe_subscription_id = excluded.stripe_subscription_id,\n        stripe_subscription_item_id = excluded.stripe_subscription_item_id,\n        price_id = excluded.price_id,\n        quantity = excluded.quantity,\n        status = excluded.status,\n        last_error_code = excluded.last_error_code,\n        checked_at = datetime('now'),\n        synced_at = CASE WHEN ? THEN datetime('now') ELSE connected_account_billing.synced_at END,\n        updated_at = datetime('now')`,\n    args: [\n      input.userId,\n      input.stripeSubscriptionId,\n      input.stripeSubscriptionItemId,\n      input.priceId,\n      quantity,\n      input.status,\n      input.lastErrorCode ?? null,\n      input.synced === true ? 1 : 0,\n      input.synced === true ? 1 : 0,\n    ],\n  })\n  const state = await getConnectedAccountBillingState(input.userId)\n  if (!state) throw new Error('connected account billing state was not persisted')\n  return state\n}\n\nexport async function listUsersDueForConnectedAccountBillingReconciliation(\n  staleMinutes = 60,\n  limit = 20,\n): Promise<User[]> {\n  const modifier = `-${Math.max(1, Math.round(staleMinutes))} minutes`\n  const res = await getDb().execute({\n    sql: `SELECT u.*\n      FROM users u\n      LEFT JOIN connected_account_billing b ON b.user_id = u.id\n      WHERE u.active = 1\n        AND u.subscription_id IS NOT NULL\n        AND (b.checked_at IS NULL OR b.checked_at <= datetime('now', ?))\n      ORDER BY COALESCE(b.checked_at, u.created_at) ASC\n      LIMIT ?`,\n    args: [modifier, Math.max(1, Math.min(100, Math.round(limit)))],\n  })\n  return res.rows.map(row => rowToUser(row as unknown as Record<string, unknown>))\n}\n\nexport async function setMemoryPlan(userId: number, plan: 'free' | 'pro' | 'team', subscriptionId: string | null): Promise<void> {\n  await getDb().execute({\n    sql: 'UPDATE users SET memory_plan = ?, memory_subscription_id = ? WHERE id = ?',\n    args: [plan, subscriptionId, userId],\n  })\n}\n\nexport async function getMemoryPlan(userId: number | bigint): Promise<{ plan: string; memory_subscription_id: string | null }> {\n  const res = await getDb().execute({ sql: 'SELECT memory_plan, memory_subscription_id FROM users WHERE id = ?', args: [userId] })\n  const row = res.rows[0] as unknown as Record<string, unknown> | undefined\n  return {\n    plan: row?.memory_plan != null ? String(row.memory_plan) : 'free',\n    memory_subscription_id: row?.memory_subscription_id != null ? String(row.memory_subscription_id) : null,\n  }\n}\n\nexport async function getMemoryProvisioned(userId: number | bigint): Promise<boolean> {\n  const res = await getDb().execute({ sql: 'SELECT memory_provisioned FROM users WHERE id = ?', args: [userId] })\n  const row = res.rows[0] as unknown as Record<string, unknown> | undefined\n  return Number(row?.memory_provisioned ?? 0) === 1\n}\n\nexport async function setMemoryProvisioned(userId: number | bigint, provisioned: boolean): Promise<void> {\n  await getDb().execute({ sql: 'UPDATE users SET memory_provisioned = ? WHERE id = ?', args: [provisioned ? 1 : 0, userId] })\n}\n\nexport async function resolveUserByMemorySubscription(subscriptionId: string): Promise<User | undefined> {\n  const res = await getDb().execute({ sql: 'SELECT * FROM users WHERE memory_subscription_id = ? AND active = 1', args: [subscriptionId] })\n  return res.rows[0] ? rowToUser(res.rows[0] as unknown as Record<string, unknown>) : undefined\n}\n\nexport const CREDIT_LOT_TTL = '+3 months'\n\nasync function ensureMigrated(userId: number): Promise<number> {\n  const db = getDb()\n  const inserted = await db.execute({\n    sql: `INSERT INTO credit_lots (user_id, amount_mc, remaining_mc, source, expires_at)\n          SELECT ?, legacy.balance_mc, legacy.balance_mc, 'migration', datetime('now', ?)\n          FROM (\n            SELECT COALESCE(SUM(amount_mc), 0) AS balance_mc\n            FROM ledger\n            WHERE user_id = ?\n          ) AS legacy\n          WHERE legacy.balance_mc > 0\n            AND EXISTS (\n              SELECT 1 FROM ledger\n              WHERE user_id = ? AND created_at < ?\n            )\n            AND NOT EXISTS (\n              SELECT 1 FROM credit_lots WHERE user_id = ?\n            )`,\n    args: [userId, CREDIT_LOT_TTL, userId, userId, CREDIT_LOTS_EPOCH, userId],\n  })\n  return inserted.rowsAffected\n}\n\nexport async function expireOldLots(): Promise<{ expired_lots: number; expired_mc: number }> {\n  const db = getDb()\n  const lots = await db.execute({\n    sql: `SELECT id\n          FROM credit_lots\n          WHERE remaining_mc > 0\n            AND never_expires = 0\n            AND expires_at <= datetime('now')\n          LIMIT 500`,\n  })\n  if (lots.rows.length === 0) return { expired_lots: 0, expired_mc: 0 }\n  const claims: Array<{ token: string; insertResultIndex: number }> = []\n  const stmts: Array<{ sql: string; args: Array<string | number> }> = []\n  for (const lot of lots.rows) {\n    const lotId = Number(lot.id)\n    const token = randomUUID()\n    claims.push({ token, insertResultIndex: stmts.length })\n    stmts.push({\n      sql: `INSERT OR IGNORE INTO credit_lot_expirations (lot_id, user_id, amount_mc, claim_token)\n            SELECT id, user_id, remaining_mc, ?\n            FROM credit_lots\n            WHERE id = ?\n              AND remaining_mc > 0\n              AND never_expires = 0\n              AND expires_at <= datetime('now')`,\n      args: [token, lotId],\n    })\n    stmts.push({\n      sql: `UPDATE credit_lots\n            SET remaining_mc = 0\n            WHERE id = ?\n              AND EXISTS (\n                SELECT 1 FROM credit_lot_expirations\n                WHERE lot_id = ? AND claim_token = ?\n              )`,\n      args: [lotId, lotId, token],\n    })\n    stmts.push({\n      sql: `INSERT INTO ledger (user_id, amount_mc, operation, description)\n            SELECT user_id, -amount_mc, 'credit_expired', 'credits expired'\n            FROM credit_lot_expirations\n            WHERE lot_id = ? AND claim_token = ?`,\n      args: [lotId, token],\n    })\n  }\n  const results = await db.batch(stmts, 'write')\n  const claimedTokens = claims\n    .filter(claim => results[claim.insertResultIndex]?.rowsAffected === 1)\n    .map(claim => claim.token)\n  if (claimedTokens.length === 0) return { expired_lots: 0, expired_mc: 0 }\n\n  const placeholders = claimedTokens.map(() => '?').join(', ')\n  const expired = await db.execute({\n    sql: `SELECT user_id, amount_mc\n          FROM credit_lot_expirations\n          WHERE claim_token IN (${placeholders})`,\n    args: claimedTokens,\n  })\n  const users = new Set(expired.rows.map(row => Number(row.user_id)))\n  for (const uid of users) await reconcileBalanceMc(uid)\n  return {\n    expired_lots: expired.rows.length,\n    expired_mc: expired.rows.reduce((sum, row) => sum + Number(row.amount_mc), 0),\n  }\n}\n\nexport async function migrateExistingBalancesToLots(): Promise<{ migrated: number; total_mc: number }> {\n  const db = getDb()\n  const res = await db.execute({\n    sql: `SELECT DISTINCT u.id AS user_id\n          FROM users u\n          JOIN ledger l ON l.user_id = u.id\n          WHERE l.created_at < ?\n            AND NOT EXISTS (SELECT 1 FROM credit_lots WHERE user_id = u.id)`,\n    args: [CREDIT_LOTS_EPOCH],\n  })\n  let migrated = 0\n  let totalMc = 0\n  for (const row of res.rows) {\n    const uid = Number(row.user_id)\n    const inserted = await ensureMigrated(uid)\n    if (inserted === 0) continue\n    const lot = await db.execute({\n      sql: `SELECT amount_mc FROM credit_lots\n            WHERE user_id = ? AND source = 'migration'\n            ORDER BY id DESC LIMIT 1`,\n      args: [uid],\n    })\n    const amountMc = Number(lot.rows[0]?.amount_mc ?? 0)\n    await reconcileBalanceMc(uid)\n    migrated += 1\n    totalMc += amountMc\n  }\n  return { migrated, total_mc: totalMc }\n}\n\nexport async function creditMc(\n  userId: number,\n  mc: number,\n  operation: string,\n  description?: string,\n  stripePaymentIntent?: string,\n  options?: { neverExpires?: boolean },\n): Promise<number> {\n  const db = getDb()\n  await ensureMigrated(userId)\n  let neverExpires = options?.neverExpires\n  if (neverExpires === undefined && mc > 0) {\n    const tier = await db.execute({ sql: 'SELECT subscription_tier FROM users WHERE id = ? LIMIT 1', args: [userId] })\n    neverExpires = String(tier.rows[0]?.subscription_tier ?? '').startsWith('sonic')\n  }\n  const expiry = neverExpires ? '9999-12-31 23:59:59' : null\n\n  if (stripePaymentIntent) {\n    const stmts: Array<{ sql: string; args: Array<string | number | null> }> = [{\n      sql: 'INSERT OR IGNORE INTO ledger (user_id, amount_mc, operation, description, stripe_pi) VALUES (?, ?, ?, ?, ?)',\n      args: [userId, mc, operation, description ?? null, stripePaymentIntent],\n    }]\n    if (mc > 0) {\n      stmts.push({\n        sql: `INSERT INTO credit_lots (user_id, amount_mc, remaining_mc, source, stripe_pi, expires_at, never_expires)\n              SELECT ?, ?, ?, ?, ?, COALESCE(?, datetime('now', ?)), ?\n              WHERE changes() = 1`,\n        args: [userId, mc, mc, operation, stripePaymentIntent, expiry, CREDIT_LOT_TTL, neverExpires ? 1 : 0],\n      })\n    }\n    stmts.push({\n      sql: 'UPDATE users SET balance_mc = balance_mc + ? WHERE id = ? AND changes() = 1',\n      args: [mc, userId],\n    })\n    await db.batch(stmts, 'write')\n    const res = await db.execute({ sql: 'SELECT balance_mc FROM users WHERE id = ?', args: [userId] })\n    return Number(res.rows[0]?.balance_mc ?? 0)\n  }\n\n  const stmts: Array<{ sql: string; args: Array<string | number | null> }> = []\n  if (mc > 0) {\n    stmts.push({ sql: `INSERT INTO credit_lots (user_id, amount_mc, remaining_mc, source, stripe_pi, expires_at, never_expires) VALUES (?, ?, ?, ?, ?, COALESCE(?, datetime('now', ?)), ?)`, args: [userId, mc, mc, operation, null, expiry, CREDIT_LOT_TTL, neverExpires ? 1 : 0] })\n  }\n  stmts.push({ sql: 'UPDATE users SET balance_mc = balance_mc + ? WHERE id = ?', args: [mc, userId] })\n  stmts.push({ sql: 'INSERT INTO ledger (user_id, amount_mc, operation, description, stripe_pi) VALUES (?, ?, ?, ?, ?)', args: [userId, mc, operation, description ?? null, null] })\n  await db.batch(stmts, 'write')\n  const res = await db.execute({ sql: 'SELECT balance_mc FROM users WHERE id = ?', args: [userId] })\n  return Number(res.rows[0]?.balance_mc ?? 0)\n}\n\nexport async function creditMcIdempotent(\n  userId: number,\n  mc: number,\n  operation: string,\n  description: string,\n  idempotencyKey: string,\n  options?: { neverExpires?: boolean },\n): Promise<{ ok: boolean; balance_mc: number; duplicate: boolean }> {\n  if (!Number.isSafeInteger(mc) || mc <= 0) throw new Error('idempotent credit amount must be a positive integer')\n  if (!idempotencyKey || idempotencyKey.length > 500) throw new Error('idempotent credit key is required and must be bounded')\n\n  const db = getDb()\n  await ensureMigrated(userId)\n\n  let neverExpires = options?.neverExpires\n  if (neverExpires === undefined) {\n    const tier = await db.execute({ sql: 'SELECT subscription_tier FROM users WHERE id = ? LIMIT 1', args: [userId] })\n    neverExpires = String(tier.rows[0]?.subscription_tier ?? '').startsWith('sonic')\n  }\n  const expiry = neverExpires ? '9999-12-31 23:59:59' : null\n\n  const readBalance = async (): Promise<number> => {\n    const res = await db.execute({ sql: 'SELECT balance_mc FROM users WHERE id = ?', args: [userId] })\n    return Number(res.rows[0]?.balance_mc ?? 0)\n  }\n\n  let results\n  try {\n    results = await db.batch([\n      {\n        sql: \"INSERT OR IGNORE INTO billing_credits (idempotency_key, user_id, amount_mc, operation, description, status) VALUES (?, ?, ?, ?, ?, 'claimed')\",\n        args: [idempotencyKey, userId, mc, operation, description],\n      },\n      {\n        sql: \"UPDATE billing_credits SET status = 'applying' WHERE idempotency_key = ? AND user_id = ? AND amount_mc = ? AND operation = ? AND description = ? AND status = 'claimed'\",\n        args: [idempotencyKey, userId, mc, operation, description],\n      },\n      {\n        sql: `INSERT INTO credit_lots (user_id, amount_mc, remaining_mc, source, stripe_pi, expires_at, never_expires)\n              SELECT user_id, amount_mc, amount_mc, operation, NULL, COALESCE(?, datetime('now', ?)), ?\n              FROM billing_credits\n              WHERE idempotency_key = ? AND status = 'applying'`,\n        args: [expiry, CREDIT_LOT_TTL, neverExpires ? 1 : 0, idempotencyKey],\n      },\n      {\n        sql: `UPDATE users\n              SET balance_mc = balance_mc + COALESCE((\n                SELECT amount_mc FROM billing_credits WHERE idempotency_key = ? AND status = 'applying'\n              ), 0)\n              WHERE id = ?`,\n        args: [idempotencyKey, userId],\n      },\n      {\n        sql: \"INSERT INTO ledger (user_id, amount_mc, operation, description) SELECT user_id, amount_mc, operation, description FROM billing_credits WHERE idempotency_key = ? AND status = 'applying'\",\n        args: [idempotencyKey],\n      },\n      {\n        sql: \"UPDATE billing_credits SET status = 'applied', applied_at = datetime('now') WHERE idempotency_key = ? AND status = 'applying'\",\n        args: [idempotencyKey],\n      },\n    ], 'write')\n  } catch (error) {\n    try {\n      const recovered = await db.execute({\n        sql: \"SELECT user_id, amount_mc, operation, description FROM billing_credits WHERE idempotency_key = ? AND status = 'applied' LIMIT 1\",\n        args: [idempotencyKey],\n      })\n      const row = recovered.rows[0]\n      if (\n        row\n        && Number(row.user_id) === userId\n        && Number(row.amount_mc) === mc\n        && String(row.operation) === operation\n        && String(row.description) === description\n      ) {\n        return { ok: true, balance_mc: await readBalance(), duplicate: true }\n      }\n    } catch {\n      void 0\n    }\n    throw error\n  }\n\n  const claim = await db.execute({\n    sql: 'SELECT user_id, amount_mc, operation, description, status FROM billing_credits WHERE idempotency_key = ? LIMIT 1',\n    args: [idempotencyKey],\n  })\n  const row = claim.rows[0]\n  if (\n    !row\n    || Number(row.user_id) !== userId\n    || Number(row.amount_mc) !== mc\n    || String(row.operation) !== operation\n    || String(row.description) !== description\n  ) {\n    throw new Error('idempotent credit key conflicts with another credit')\n  }\n\n  return {\n    ok: String(row.status) === 'applied',\n    balance_mc: await readBalance(),\n    duplicate: results[1]?.rowsAffected === 0 && String(row.status) === 'applied',\n  }\n}\n\nexport async function ledgerExistsForOperation(userId: number, operation: string): Promise<boolean> {\n  const res = await getDb().execute({\n    sql: 'SELECT 1 FROM ledger WHERE user_id = ? AND operation = ? LIMIT 1',\n    args: [userId, operation],\n  })\n  return res.rows.length > 0\n}\n\nexport async function claimMonthlyFreeRefresh(userId: number, month: string): Promise<boolean> {\n  const res = await getDb().execute({\n    sql: 'INSERT OR IGNORE INTO free_credit_refreshes (user_id, month) VALUES (?, ?)',\n    args: [userId, month],\n  })\n  return res.rowsAffected > 0\n}\n\nexport async function debitMc(\n  userId: number,\n  mc: number,\n  operation: string,\n  description?: string,\n): Promise<{ ok: boolean; balance_mc: number }> {\n  const debit = await debitMcIdempotent(\n    userId,\n    mc,\n    operation,\n    description ?? '',\n    `debit:${randomUUID()}`,\n  )\n  return { ok: debit.ok, balance_mc: debit.balance_mc }\n}\n\n/**\n * Atomically applies a wallet debit once for a stable billing key.\n *\n * The debit claim, wallet update, credit-lot drain, and ledger row are committed\n * in one libSQL batch. A claim that could not be funded remains retryable, while\n * a completed claim makes retries a read-only success. This is intended for\n * billing lifecycles that must be recoverable after a caller or process crash.\n */\nexport async function debitMcIdempotent(\n  userId: number,\n  mc: number,\n  operation: string,\n  description: string,\n  idempotencyKey: string,\n): Promise<{ ok: boolean; balance_mc: number; duplicate: boolean }> {\n  if (!Number.isSafeInteger(mc) || mc <= 0) throw new Error('idempotent debit amount must be a positive integer')\n  if (!idempotencyKey || idempotencyKey.length > 500) throw new Error('idempotent debit key is required and must be bounded')\n\n  const db = getDb()\n  await ensureMigrated(userId)\n  let results\n  try {\n    results = await db.batch([\n      {\n        // An unfunded claim may be retried with a corrected estimate under the\n        // same logical operation fingerprint. Applied claims remain immutable.\n        sql: \"UPDATE billing_debits SET amount_mc = ? WHERE idempotency_key = ? AND user_id = ? AND operation = ? AND description = ? AND status = 'claimed'\",\n        args: [mc, idempotencyKey, userId, operation, description],\n      },\n      {\n        sql: \"INSERT OR IGNORE INTO billing_debits (idempotency_key, user_id, amount_mc, operation, description, status) VALUES (?, ?, ?, ?, ?, 'claimed')\",\n        args: [idempotencyKey, userId, mc, operation, description],\n      },\n      {\n        sql: `UPDATE users\n              SET balance_mc = balance_mc - ?\n              WHERE id = ?\n                AND balance_mc >= ?\n                AND (SELECT COALESCE(SUM(remaining_mc), 0)\n                     FROM credit_lots\n                     WHERE user_id = ?\n                       AND remaining_mc > 0\n                       AND (never_expires = 1 OR expires_at > datetime('now'))) >= ?\n                AND EXISTS (SELECT 1 FROM billing_debits WHERE idempotency_key = ? AND user_id = ? AND amount_mc = ? AND operation = ? AND description = ? AND status = 'claimed')`,\n        args: [mc, userId, mc, userId, mc, idempotencyKey, userId, mc, operation, description],\n      },\n      {\n        sql: \"UPDATE billing_debits SET status = 'applying' WHERE idempotency_key = ? AND user_id = ? AND amount_mc = ? AND operation = ? AND description = ? AND status = 'claimed' AND changes() = 1\",\n        args: [idempotencyKey, userId, mc, operation, description],\n      },\n      {\n        sql: `INSERT OR IGNORE INTO billing_debit_allocations\n                (debit_idempotency_key, lot_id, user_id, amount_mc, expires_at)\n              WITH ordered AS MATERIALIZED (\n                SELECT id,\n                       remaining_mc,\n                       expires_at,\n                       COALESCE(SUM(remaining_mc) OVER (\n                         ORDER BY never_expires ASC, expires_at ASC, granted_at ASC, id ASC\n                         ROWS BETWEEN UNBOUNDED PRECEDING AND 1 PRECEDING\n                       ), 0) AS prior_mc\n                FROM credit_lots\n                WHERE user_id = ? AND remaining_mc > 0 AND (never_expires = 1 OR expires_at > datetime('now'))\n              ), allocation AS (\n                SELECT id, expires_at, MIN(remaining_mc, MAX(0, ? - prior_mc)) AS take_mc\n                FROM ordered\n              )\n              SELECT ?, id, ?, take_mc, expires_at\n              FROM allocation\n              WHERE take_mc > 0\n                AND EXISTS (SELECT 1 FROM billing_debits WHERE idempotency_key = ? AND status = 'applying')`,\n        args: [userId, mc, idempotencyKey, userId, idempotencyKey],\n      },\n      {\n        sql: `UPDATE credit_lots\n              SET remaining_mc = remaining_mc - COALESCE((\n                SELECT amount_mc FROM billing_debit_allocations\n                WHERE debit_idempotency_key = ? AND lot_id = credit_lots.id\n              ), 0)\n              WHERE id IN (\n                SELECT lot_id FROM billing_debit_allocations WHERE debit_idempotency_key = ?\n              )\n                AND EXISTS (SELECT 1 FROM billing_debits WHERE idempotency_key = ? AND status = 'applying')`,\n        args: [idempotencyKey, idempotencyKey, idempotencyKey],\n      },\n      {\n        sql: \"INSERT INTO ledger (user_id, amount_mc, operation, description) SELECT user_id, -amount_mc, operation, description FROM billing_debits WHERE idempotency_key = ? AND status = 'applying'\",\n        args: [idempotencyKey],\n      },\n      {\n        sql: \"UPDATE billing_debits SET status = 'applied', applied_at = datetime('now') WHERE idempotency_key = ? AND status = 'applying'\",\n        args: [idempotencyKey],\n      },\n    ], 'write')\n  } catch (error) {\n    // libSQL can report a transport failure after the transaction committed.\n    // Read the durable receipt before surfacing the error so callers do not\n    // retry a completed debit under a different key.\n    try {\n      const recovered = await db.execute({\n        sql: \"SELECT user_id, amount_mc, operation, description, status FROM billing_debits WHERE idempotency_key = ? AND status = 'applied' LIMIT 1\",\n        args: [idempotencyKey],\n      })\n      const row = recovered.rows[0]\n      if (\n        row\n        && Number(row.user_id) === userId\n        && Number(row.amount_mc) === mc\n        && String(row.operation) === operation\n        && String(row.description) === description\n      ) {\n        const balance = await db.execute({ sql: 'SELECT balance_mc FROM users WHERE id = ?', args: [userId] })\n        return { ok: true, balance_mc: Number(balance.rows[0]?.balance_mc ?? 0), duplicate: true }\n      }\n    } catch {\n      // Preserve the original batch error when the recovery read also fails.\n    }\n    throw error\n  }\n\n  const claim = await db.execute({\n    sql: 'SELECT user_id, amount_mc, operation, description, status FROM billing_debits WHERE idempotency_key = ? LIMIT 1',\n    args: [idempotencyKey],\n  })\n  const row = claim.rows[0]\n  if (\n    !row\n    || Number(row.user_id) !== userId\n    || Number(row.amount_mc) !== mc\n    || String(row.operation) !== operation\n    || String(row.description) !== description\n  ) {\n    throw new Error('idempotent debit key conflicts with another debit')\n  }\n  const balance = await db.execute({ sql: 'SELECT balance_mc FROM users WHERE id = ?', args: [userId] })\n  return {\n    ok: String(row.status) === 'applied',\n    balance_mc: Number(balance.rows[0]?.balance_mc ?? 0),\n    duplicate: results[2]?.rowsAffected === 0 && String(row.status) === 'applied',\n  }\n}\n\nexport interface IdempotentDebitSettlement {\n  balance_mc: number\n  final_amount_mc: number\n  /** Credits actually restored to the spendable wallet. */\n  refund_mc: number\n  /** Unused hold that had already expired and therefore was not restored. */\n  expired_refund_mc?: number\n  duplicate: boolean\n}\n\n/**\n * Atomically settles an idempotent debit hold to its final charge.\n *\n * The first settlement for a hold wins and is persisted as a billing-event\n * receipt. Retrying the same settlement, including after an ambiguous\n * post-commit transport error, reads that receipt and cannot apply a second\n * refund. A later retry that observes a different runtime cost receives the\n * first durable final amount so its public billing result remains consistent\n * with the wallet.\n */\nexport async function settleDebitMcIdempotent(\n  userId: number,\n  debitIdempotencyKey: string,\n  finalAmountMc: number,\n  refundOperation: string,\n  description: string,\n  sourceSurface: string,\n): Promise<IdempotentDebitSettlement> {\n  if (!Number.isSafeInteger(finalAmountMc) || finalAmountMc < 0) {\n    throw new Error('idempotent debit settlement amount must be a non-negative integer')\n  }\n  if (!debitIdempotencyKey || debitIdempotencyKey.length > 500) {\n    throw new Error('idempotent debit settlement key is required and must be bounded')\n  }\n  if (!refundOperation || !sourceSurface) {\n    throw new Error('idempotent debit settlement operation and source are required')\n  }\n\n  const db = getDb()\n  await ensureMigrated(userId)\n  const settlementKey = `debit-settlement:${createHash('sha256').update(debitIdempotencyKey).digest('hex')}`\n  const billingClass = 'wallet_hold_settlement'\n  const eventId = randomUUID()\n  const metadata = JSON.stringify({ debitIdempotencyKey, refundOperation, description })\n\n  const readReceipt = async (): Promise<IdempotentDebitSettlement | null> => {\n    const receipt = await db.execute({\n      sql: `SELECT e.user_id,\n                   e.status,\n                   e.amount_mc AS final_amount_mc,\n                   e.billing_class,\n                   e.source_surface,\n                   e.settled_at,\n                   d.amount_mc AS held_amount_mc\n            FROM billing_events e\n            JOIN billing_debits d ON d.idempotency_key = ?\n            WHERE e.idempotency_key = ?\n              AND d.user_id = e.user_id\n              AND d.status = 'applied'\n            LIMIT 1`,\n      args: [debitIdempotencyKey, settlementKey],\n    })\n    const row = receipt.rows[0]\n    if (!row) return null\n    if (\n      Number(row.user_id) !== userId\n      || String(row.billing_class) !== billingClass\n      || String(row.source_surface) !== sourceSurface\n    ) {\n      throw new Error('idempotent debit settlement key conflicts with another settlement')\n    }\n    if (String(row.status) !== 'settled') return null\n    const heldAmountMc = Number(row.held_amount_mc)\n    const durableFinalAmountMc = Number(row.final_amount_mc)\n    if (\n      !Number.isSafeInteger(heldAmountMc)\n      || !Number.isSafeInteger(durableFinalAmountMc)\n      || durableFinalAmountMc < 0\n      || durableFinalAmountMc > heldAmountMc\n    ) {\n      throw new Error('idempotent debit settlement receipt is invalid')\n    }\n    const nominalRefundMc = heldAmountMc - durableFinalAmountMc\n    const allocationRefund = await db.execute({\n      sql: `WITH ordered AS (\n              SELECT a.amount_mc,\n                     a.expires_at,\n                     COALESCE(SUM(a.amount_mc) OVER (\n                       ORDER BY a.expires_at ASC, a.lot_id ASC\n                       ROWS BETWEEN UNBOUNDED PRECEDING AND 1 PRECEDING\n                     ), 0) AS prior_mc\n              FROM billing_debit_allocations a\n              WHERE a.debit_idempotency_key = ?\n            ), refunds AS (\n              SELECT expires_at,\n                     amount_mc - MIN(amount_mc, MAX(0, ? - prior_mc)) AS refund_mc\n              FROM ordered\n            )\n            SELECT (SELECT COUNT(*) FROM ordered) AS allocation_count,\n                   COALESCE(SUM(CASE WHEN expires_at > ? THEN refund_mc ELSE 0 END), 0) AS credited_refund_mc\n            FROM refunds`,\n      args: [debitIdempotencyKey, durableFinalAmountMc, String(row.settled_at)],\n    })\n    const allocationCount = Number(allocationRefund.rows[0]?.allocation_count ?? 0)\n    const creditedRefundMc = allocationCount > 0\n      ? Number(allocationRefund.rows[0]?.credited_refund_mc ?? 0)\n      : 0\n    const balance = await db.execute({ sql: 'SELECT balance_mc FROM users WHERE id = ?', args: [userId] })\n    return {\n      balance_mc: Number(balance.rows[0]?.balance_mc ?? 0),\n      final_amount_mc: durableFinalAmountMc,\n      refund_mc: creditedRefundMc,\n      expired_refund_mc: nominalRefundMc - creditedRefundMc,\n      duplicate: true,\n    }\n  }\n\n  let results\n  try {\n    results = await db.batch([\n      {\n        sql: `INSERT OR IGNORE INTO billing_events\n                (id, user_id, idempotency_key, billing_class, source_surface, status, amount_mc, metadata)\n              VALUES (?, ?, ?, ?, ?, 'settling', ?, ?)`,\n        args: [eventId, userId, settlementKey, billingClass, sourceSurface, finalAmountMc, metadata],\n      },\n      {\n        sql: `WITH ordered AS (\n                SELECT a.lot_id,\n                       a.amount_mc,\n                       a.expires_at,\n                       COALESCE(SUM(a.amount_mc) OVER (\n                         ORDER BY a.expires_at ASC, a.lot_id ASC\n                         ROWS BETWEEN UNBOUNDED PRECEDING AND 1 PRECEDING\n                       ), 0) AS prior_mc\n                FROM billing_debit_allocations a\n                WHERE a.debit_idempotency_key = ?\n              ), refunds AS (\n                SELECT lot_id,\n                       expires_at,\n                       amount_mc - MIN(amount_mc, MAX(0, ? - prior_mc)) AS refund_mc\n                FROM ordered\n              )\n              UPDATE users\n              SET balance_mc = balance_mc + CASE\n                WHEN EXISTS (SELECT 1 FROM ordered) THEN COALESCE(\n                  (SELECT SUM(refund_mc) FROM refunds WHERE expires_at > datetime('now')),\n                  0\n                )\n                -- Holds created before allocation provenance cannot be safely\n                -- tied back to a source lot. Treat their unused portion as\n                -- expired instead of minting spendable credits with a guessed\n                -- expiry.\n                ELSE 0\n              END\n              WHERE id = ?\n                AND EXISTS (\n                  SELECT 1\n                  FROM billing_events e\n                  JOIN billing_debits d ON d.idempotency_key = ?\n                  WHERE e.idempotency_key = ?\n                    AND e.user_id = ?\n                    AND e.billing_class = ?\n                    AND e.source_surface = ?\n                    AND e.status = 'settling'\n                    AND d.user_id = e.user_id\n                    AND d.status = 'applied'\n                    AND e.amount_mc BETWEEN 0 AND d.amount_mc\n                )`,\n        args: [\n          debitIdempotencyKey,\n          finalAmountMc,\n          userId,\n          debitIdempotencyKey,\n          settlementKey,\n          userId,\n          billingClass,\n          sourceSurface,\n        ],\n      },\n      {\n        sql: `UPDATE billing_events\n              SET status = 'applying'\n              WHERE idempotency_key = ?\n                AND user_id = ?\n                AND billing_class = ?\n                AND source_surface = ?\n                AND status = 'settling'\n                AND changes() = 1`,\n        args: [settlementKey, userId, billingClass, sourceSurface],\n      },\n      {\n        sql: `WITH ordered AS (\n                SELECT a.lot_id,\n                       a.amount_mc,\n                       a.expires_at,\n                       COALESCE(SUM(a.amount_mc) OVER (\n                         ORDER BY a.expires_at ASC, a.lot_id ASC\n                         ROWS BETWEEN UNBOUNDED PRECEDING AND 1 PRECEDING\n                       ), 0) AS prior_mc\n                FROM billing_debit_allocations a\n                WHERE a.debit_idempotency_key = ?\n              ), refunds AS (\n                SELECT lot_id,\n                       expires_at,\n                       amount_mc - MIN(amount_mc, MAX(0, ? - prior_mc)) AS refund_mc\n                FROM ordered\n              )\n              UPDATE credit_lots\n              SET remaining_mc = remaining_mc + COALESCE((SELECT refund_mc FROM refunds WHERE refunds.lot_id = credit_lots.id), 0)\n              WHERE id IN (\n                SELECT lot_id FROM refunds\n                WHERE refund_mc > 0 AND expires_at > datetime('now')\n              )\n                AND EXISTS (SELECT 1 FROM billing_events WHERE idempotency_key = ? AND status = 'applying')`,\n        args: [debitIdempotencyKey, finalAmountMc, settlementKey],\n      },\n      {\n        sql: `WITH ordered AS (\n                SELECT a.amount_mc,\n                       a.expires_at,\n                       COALESCE(SUM(a.amount_mc) OVER (\n                         ORDER BY a.expires_at ASC, a.lot_id ASC\n                         ROWS BETWEEN UNBOUNDED PRECEDING AND 1 PRECEDING\n                       ), 0) AS prior_mc\n                FROM billing_debit_allocations a\n                WHERE a.debit_idempotency_key = ?\n              ), refunds AS (\n                SELECT expires_at,\n                       amount_mc - MIN(amount_mc, MAX(0, ? - prior_mc)) AS refund_mc\n                FROM ordered\n              ), credited AS (\n                SELECT e.user_id,\n                       CASE\n                         WHEN EXISTS (SELECT 1 FROM ordered) THEN COALESCE(\n                           (SELECT SUM(refund_mc) FROM refunds WHERE expires_at > datetime('now')),\n                           0\n                         )\n                         ELSE 0\n                       END AS refund_mc\n                FROM billing_events e\n                JOIN billing_debits d ON d.idempotency_key = ?\n                WHERE e.idempotency_key = ?\n                  AND e.status = 'applying'\n              )\n              INSERT INTO ledger (user_id, amount_mc, operation, description)\n              SELECT user_id,\n                     refund_mc,\n                     ?,\n                     ?\n              FROM credited\n              WHERE refund_mc > 0`,\n        args: [\n          debitIdempotencyKey,\n          finalAmountMc,\n          debitIdempotencyKey,\n          settlementKey,\n          refundOperation,\n          description,\n        ],\n      },\n      {\n        sql: `UPDATE billing_events\n              SET status = 'settled', settled_at = datetime('now')\n              WHERE idempotency_key = ?\n                AND status = 'applying'`,\n        args: [settlementKey],\n      },\n    ], 'write')\n  } catch (error) {\n    try {\n      const recovered = await readReceipt()\n      if (recovered) return recovered\n    } catch {\n      // Preserve the original batch error when the recovery read also fails.\n    }\n    throw error\n  }\n\n  const receipt = await readReceipt()\n  if (!receipt) throw new Error('idempotent debit settlement did not reach a durable settled state')\n  return { ...receipt, duplicate: results[0]?.rowsAffected === 0 }\n}\n\nexport async function getLedger(userId: number, limit = 50): Promise<LedgerRow[]> {\n  const res = await getDb().execute({\n    sql: 'SELECT * FROM ledger WHERE user_id = ? ORDER BY created_at DESC LIMIT ?',\n    args: [userId, limit],\n  })\n  return res.rows.map(r => ({\n    id:          Number(r.id),\n    user_id:     Number(r.user_id),\n    amount_mc:   Number(r.amount_mc),\n    operation:   String(r.operation),\n    description: r.description != null ? String(r.description) : null,\n    stripe_pi:   r.stripe_pi   != null ? String(r.stripe_pi)   : null,\n    created_at:  String(r.created_at),\n  }))\n}\n\nexport interface LedgerOperationClassification {\n  refund: string[]\n  grant: string[]\n  purchase: string[]\n}\n\nexport interface BillingLedgerSummary {\n  spent_mc: number\n  refunded_mc: number\n  granted_mc: number\n  purchased_mc: number\n  net_mc: number\n  by_operation: Array<{ operation: string; mc: number; count: number }>\n}\n\nexport async function getBillingLedgerSummary(\n  userId: number,\n  since: string,\n  classification: LedgerOperationClassification,\n): Promise<BillingLedgerSummary> {\n  const db = getDb()\n  const refundPlaceholders = classification.refund.map(() => '?').join(', ')\n  const grantPlaceholders = classification.grant.map(() => '?').join(', ')\n  const purchasePlaceholders = classification.purchase.map(() => '?').join(', ')\n  const totalsRes = await db.execute({\n    sql: `SELECT\n      COALESCE(SUM(CASE WHEN amount_mc < 0 AND operation NOT IN (${refundPlaceholders}) THEN -amount_mc ELSE 0 END), 0) AS spent_mc,\n      COALESCE(SUM(CASE WHEN amount_mc > 0 AND operation IN (${refundPlaceholders}) THEN amount_mc ELSE 0 END), 0) AS refunded_mc,\n      COALESCE(SUM(CASE WHEN amount_mc > 0 AND operation IN (${grantPlaceholders}) THEN amount_mc ELSE 0 END), 0) AS granted_mc,\n      COALESCE(SUM(CASE WHEN amount_mc > 0 AND operation IN (${purchasePlaceholders}) THEN amount_mc ELSE 0 END), 0) AS purchased_mc,\n      COALESCE(SUM(amount_mc), 0) AS net_mc\n      FROM ledger WHERE user_id = ? AND created_at >= ?`,\n    args: [\n      ...classification.refund, ...classification.refund,\n      ...classification.grant, ...classification.purchase,\n      userId, since,\n    ],\n  })\n  const byOpRes = await db.execute({\n    sql: `SELECT operation, -SUM(amount_mc) AS mc, COUNT(*) AS count\n      FROM ledger\n      WHERE user_id = ? AND created_at >= ? AND amount_mc < 0 AND operation NOT IN (${refundPlaceholders})\n      GROUP BY operation\n      ORDER BY mc DESC, operation ASC`,\n    args: [userId, since, ...classification.refund],\n  })\n  const totals = totalsRes.rows[0]\n  return {\n    spent_mc:     Number(totals?.spent_mc ?? 0),\n    refunded_mc:  Number(totals?.refunded_mc ?? 0),\n    granted_mc:   Number(totals?.granted_mc ?? 0),\n    purchased_mc: Number(totals?.purchased_mc ?? 0),\n    net_mc:       Number(totals?.net_mc ?? 0),\n    by_operation: byOpRes.rows.map(r => ({\n      operation: String(r.operation),\n      mc:        Number(r.mc),\n      count:     Number(r.count),\n    })),\n  }\n}\n\nexport async function ledgerExistsForStripePI(stripePI: string): Promise<boolean> {\n  const res = await getDb().execute({\n    sql: 'SELECT 1 FROM ledger WHERE stripe_pi = ? LIMIT 1',\n    args: [stripePI],\n  })\n  return res.rows.length > 0\n}\n\nexport async function reconcileBalanceMc(userId: number | bigint, knownBalanceMc?: number): Promise<number> {\n  const db = getDb()\n  // The caller's value is only a fast-path hint. Every write compares against\n  // the cached balance observed in the same snapshot as the lot projection.\n  void knownBalanceMc\n  for (let attempt = 0; attempt < 8; attempt += 1) {\n    const snapshot = await db.execute({\n      sql: `SELECT\n              u.balance_mc AS cached_balance_mc,\n              COUNT(l.id) AS lot_count,\n              COALESCE(SUM(CASE\n                WHEN l.remaining_mc > 0\n                  AND (l.never_expires = 1 OR l.expires_at > datetime('now'))\n                THEN l.remaining_mc ELSE 0 END), 0) AS projected_balance_mc\n            FROM users u\n            LEFT JOIN credit_lots l ON l.user_id = u.id\n            WHERE u.id = ?\n            GROUP BY u.id, u.balance_mc`,\n      args: [userId],\n    })\n    const row = snapshot.rows[0]\n    if (!row) return 0\n\n    if (Number(row.lot_count ?? 0) === 0) {\n      await ensureMigrated(Number(userId))\n      const nowHasLots = await db.execute({\n        sql: 'SELECT 1 FROM credit_lots WHERE user_id = ? LIMIT 1',\n        args: [userId],\n      })\n      if (nowHasLots.rows.length > 0) continue\n    }\n\n    const cachedBalanceMc = Number(row.cached_balance_mc ?? 0)\n    const projectedBalanceMc = Number(row.projected_balance_mc ?? 0)\n    if (cachedBalanceMc === projectedBalanceMc) return projectedBalanceMc\n\n    const updated = await db.execute({\n      sql: 'UPDATE users SET balance_mc = ? WHERE id = ? AND balance_mc = ?',\n      args: [projectedBalanceMc, userId, cachedBalanceMc],\n    })\n    if (updated.rowsAffected === 1) return projectedBalanceMc\n  }\n\n  // Under sustained contention, return the last committed wallet balance\n  // instead of risking a stale projection write.\n  const current = await db.execute({\n    sql: 'SELECT balance_mc FROM users WHERE id = ? LIMIT 1',\n    args: [userId],\n  })\n  return Number(current.rows[0]?.balance_mc ?? 0)\n}\n\nexport interface LedgerRow {\n  id: number\n  user_id: number\n  amount_mc: number\n  operation: string\n  description: string | null\n  stripe_pi: string | null\n  created_at: string\n}\n\nexport const SiteAuditJobRowSchema = z.object({\n  id: z.string(),\n  user_id: z.number(),\n  status: z.string().default('pending'),\n  client_domain: z.string(),\n  session_path: z.string(),\n  request: z.string(),\n  result: z.string().nullable(),\n  error: z.string().nullable(),\n  created_at: z.string(),\n  updated_at: z.string(),\n});\nexport type SiteAuditJobRow = z.infer<typeof SiteAuditJobRowSchema>;\n\nexport const SiteAuditPhaseLogRowSchema = z.object({\n  id: z.number(),\n  job_id: z.string(),\n  phase: z.string(),\n  completed_at: z.string(),\n  output_summary: z.string(),\n});\nexport type SiteAuditPhaseLogRow = z.infer<typeof SiteAuditPhaseLogRowSchema>;\n\nexport interface InboxMessageSummary {\n  id: string\n  subject: string\n  sent_at: string\n  opened_at: string | null\n}\n\nexport interface InboxMessage extends InboxMessageSummary {\n  body_md: string\n  source: string\n}\n\nexport async function listInboxMessages(userId: number | bigint): Promise<InboxMessageSummary[]> {\n  const res = await getDb().execute({\n    sql: 'SELECT id, subject, sent_at, opened_at FROM inbox_messages WHERE user_id = ? AND deleted_at IS NULL ORDER BY sent_at DESC, id DESC',\n    args: [userId],\n  })\n  return res.rows.map(r => ({\n    id: String(r.id),\n    subject: String(r.subject),\n    sent_at: String(r.sent_at),\n    opened_at: r.opened_at != null ? String(r.opened_at) : null,\n  }))\n}\n\nexport async function getInboxMessage(userId: number | bigint, id: string): Promise<InboxMessage | undefined> {\n  const res = await getDb().execute({\n    sql: 'SELECT id, subject, body_md, source, sent_at, opened_at FROM inbox_messages WHERE id = ? AND user_id = ? AND deleted_at IS NULL',\n    args: [id, userId],\n  })\n  const row = res.rows[0]\n  if (!row) return undefined\n  return {\n    id: String(row.id),\n    subject: String(row.subject),\n    body_md: String(row.body_md),\n    source: String(row.source),\n    sent_at: String(row.sent_at),\n    opened_at: row.opened_at != null ? String(row.opened_at) : null,\n  }\n}\n\nexport async function markInboxMessageOpened(userId: number | bigint, id: string): Promise<string | null> {\n  const db = getDb()\n  await db.execute({\n    sql: \"UPDATE inbox_messages SET opened_at = datetime('now') WHERE id = ? AND user_id = ? AND deleted_at IS NULL AND opened_at IS NULL\",\n    args: [id, userId],\n  })\n  const res = await db.execute({\n    sql: 'SELECT opened_at FROM inbox_messages WHERE id = ? AND user_id = ? AND deleted_at IS NULL',\n    args: [id, userId],\n  })\n  const row = res.rows[0]\n  if (!row) return null\n  return row.opened_at != null ? String(row.opened_at) : null\n}\n\nexport async function softDeleteInboxMessage(userId: number | bigint, id: string): Promise<boolean> {\n  const res = await getDb().execute({\n    sql: \"UPDATE inbox_messages SET deleted_at = datetime('now') WHERE id = ? AND user_id = ? AND deleted_at IS NULL\",\n    args: [id, userId],\n  })\n  return res.rowsAffected > 0\n}\n\nexport async function insertInboxMessage(userId: number | bigint, subject: string, bodyMd: string, source = 'operator'): Promise<string> {\n  const id = randomUUID()\n  await getDb().execute({\n    sql: 'INSERT INTO inbox_messages (id, user_id, subject, body_md, source) VALUES (?, ?, ?, ?, ?)',\n    args: [id, userId, subject, bodyMd, source],\n  })\n  return id\n}\n\nexport async function listActiveUserIds(): Promise<number[]> {\n  const res = await getDb().execute('SELECT id FROM users WHERE active = 1')\n  return res.rows.map(r => Number(r.id))\n}\n","import { AsyncLocalStorage } from 'node:async_hooks'\n\nexport interface CostContext {\n  op: string\n  probeRunId?: string | null\n  userId?: number | null\n  forceHeadless?: boolean\n  forceHeadful?: boolean\n  harvestBrowserMode?: 'headless-test'\n  subOp?: string | null\n  headlessSentOut?: { value: boolean | null }\n}\n\nexport const HEADLESS_HARVEST_TEST_ENV = 'MCP_SCRAPER_ALLOW_HEADLESS_HARVEST_TEST'\n\nconst storage = new AsyncLocalStorage<CostContext>()\n\nexport function runWithCostContext<T>(ctx: CostContext, fn: () => T): T {\n  return storage.run(ctx, fn)\n}\n\nexport function currentCostContext(): CostContext | undefined {\n  return storage.getStore()\n}\n","export const KERNEL_HEADLESS_USD_PER_SEC = 0.0000166667\nexport const KERNEL_HEADFUL_USD_PER_SEC = 0.0001333336\n\nexport const HEADLESS_OPS = new Set<string>(['serp', 'fb_search', 'fb_ad', 'instagram'])\n\nexport const FAL_WIZPER_USD_PER_COMPUTE_SEC = 0.00111\nexport const FAL_WIZPER_WALLCLOCK_TO_COMPUTE_SEC_FACTOR = 4 // calibrated from 2 real Fal invoices (4.36x, 6.18x wall-clock vs actual)\nexport const DEEPINFRA_QWEN_USD_PER_1K_OUT_TOKENS = 0.0004\n\nfunction envRate(name: string, fallback: number): number {\n  const raw = process.env[name]?.trim()\n  if (!raw) return fallback\n  const parsed = Number(raw)\n  return Number.isFinite(parsed) && parsed >= 0 ? parsed : fallback\n}\n\nexport const NANGO_USD_PER_CONNECTION_MONTH = envRate('NANGO_USD_PER_CONNECTION_MONTH', 1)\nexport const NANGO_USD_PER_FUNCTION_RUN = envRate('NANGO_USD_PER_FUNCTION_RUN', 0.0001)\nexport const NANGO_USD_PER_PROXY_REQUEST = envRate('NANGO_USD_PER_PROXY_REQUEST', 0.0001)\nexport const NANGO_USD_PER_COMPUTE_SEC = envRate('NANGO_USD_PER_COMPUTE_SEC', 0.0002)\n\nexport const CONNECTED_TARGET_MARGIN_MULTIPLE = 2.25\n\nexport function kernelCostUsd(ms: number, headful: boolean): number {\n  const sec = Math.max(0, ms) / 1000\n  return sec * (headful ? KERNEL_HEADFUL_USD_PER_SEC : KERNEL_HEADLESS_USD_PER_SEC)\n}\n\nexport function vendorCostUsd(vendor: string, units: number): number {\n  if (vendor === 'fal_wizper') {\n    const estimatedComputeSec = Math.max(0, units) / FAL_WIZPER_WALLCLOCK_TO_COMPUTE_SEC_FACTOR\n    return estimatedComputeSec * FAL_WIZPER_USD_PER_COMPUTE_SEC\n  }\n  if (vendor === 'deepinfra_qwen') return (Math.max(0, units) / 1000) * DEEPINFRA_QWEN_USD_PER_1K_OUT_TOKENS\n  if (vendor === 'openrouter') return Math.max(0, units) // units IS the real USD cost OpenRouter reported, not a rate-multiplied estimate\n  if (vendor === 'mcp_memory_video') return Math.max(0, units) // units IS the real costUsd mcp-memory's video pipeline reported (OpenRouter + transcription)\n  if (vendor === 'mcp_memory_ai') return Math.max(0, units) // units IS the real costUsd mcp-memory reported via /ai-debit\n  if (vendor === 'nango_connection') return Math.max(0, units) * NANGO_USD_PER_CONNECTION_MONTH\n  if (vendor === 'nango_function_run') return Math.max(0, units) * NANGO_USD_PER_FUNCTION_RUN\n  if (vendor === 'nango_proxy_request') return Math.max(0, units) * NANGO_USD_PER_PROXY_REQUEST\n  if (vendor === 'nango_compute') return Math.max(0, units) * NANGO_USD_PER_COMPUTE_SEC\n  return 0\n}\n","import type { Browser, BrowserContext, Page } from 'playwright'\n\nconst RUNTIME_BROWSER_UA = /\\b(?:HeadlessChrome|Chrome|Chromium|Firefox)\\/\\d+|\\bVersion\\/\\d+.*\\bSafari\\//\n\nexport async function detectPageUserAgent(page: Pick<Page, 'evaluate'>): Promise<string | null> {\n  try {\n    const value = await page.evaluate(() => navigator.userAgent)\n    return typeof value === 'string' && RUNTIME_BROWSER_UA.test(value) ? value : null\n  } catch {\n    return null\n  }\n}\n\nexport async function detectRuntimeUserAgent(\n  browser: Pick<Browser, 'contexts' | 'newContext'>,\n): Promise<string | null> {\n  let probeContext: BrowserContext | null = null\n  try {\n    let page = browser.contexts().flatMap(context => context.pages())[0]\n    if (!page) {\n      probeContext = await browser.newContext()\n      page = probeContext.pages()[0] ?? await probeContext.newPage()\n    }\n    return await detectPageUserAgent(page)\n  } catch {\n    return null\n  } finally {\n    if (probeContext) await probeContext.close().catch(() => {})\n  }\n}\n\nexport function withRuntimeUserAgent<T extends Record<string, unknown>>(\n  options: T,\n  runtimeUserAgent: string | null,\n): T & { userAgent?: string } {\n  return runtimeUserAgent ? { ...options, userAgent: runtimeUserAgent } : options\n}\n","import type { CanonicalLocationName } from './types.js'\n\nexport const LOCATIONS: Record<string, CanonicalLocationName> = {\n  'austin':           'Austin,Texas,United States',\n  'new york':         'New York,New York,United States',\n  'new york city':    'New York,New York,United States',\n  'nyc':              'New York,New York,United States',\n  'los angeles':      'Los Angeles,California,United States',\n  'la':               'Los Angeles,California,United States',\n  'chicago':          'Chicago,Illinois,United States',\n  'houston':          'Houston,Texas,United States',\n  'phoenix':          'Phoenix,Arizona,United States',\n  'philadelphia':     'Philadelphia,Pennsylvania,United States',\n  'philly':           'Philadelphia,Pennsylvania,United States',\n  'san antonio':      'San Antonio,Texas,United States',\n  'dallas':           'Dallas,Texas,United States',\n  'miami':            'Miami,Florida,United States',\n  'seattle':          'Seattle,Washington,United States',\n  'denver':           'Denver,Colorado,United States',\n  'loveland':         'Loveland,Colorado,United States',\n  'loveland co':      'Loveland,Colorado,United States',\n  'fort collins':     'Fort Collins,Colorado,United States',\n  'boulder':          'Boulder,Colorado,United States',\n  'colorado springs': 'Colorado Springs,Colorado,United States',\n  'boston':           'Boston,Massachusetts,United States',\n  'atlanta':          'Atlanta,Georgia,United States',\n  'san francisco':    'San Francisco,California,United States',\n  'sf':               'San Francisco,California,United States',\n  'portland':         'Portland,Oregon,United States',\n  'las vegas':        'Las Vegas,Nevada,United States',\n  'minneapolis':      'Minneapolis,Minnesota,United States',\n  'detroit':          'Detroit,Michigan,United States',\n  'nashville':        'Nashville,Tennessee,United States',\n  'charlotte':        'Charlotte,North Carolina,United States',\n  'orlando':          'Orlando,Florida,United States',\n  'san diego':        'San Diego,California,United States',\n  'baltimore':        'Baltimore,Maryland,United States',\n  'sacramento':       'Sacramento,California,United States',\n  'columbus':         'Columbus,Ohio,United States',\n  'indianapolis':     'Indianapolis,Indiana,United States',\n  'san jose':         'San Jose,California,United States',\n  'fort worth':       'Fort Worth,Texas,United States',\n  'jacksonville':     'Jacksonville,Florida,United States',\n  'memphis':          'Memphis,Tennessee,United States',\n  'louisville':       'Louisville,Kentucky,United States',\n  'raleigh':          'Raleigh,North Carolina,United States',\n  'richmond':         'Richmond,Virginia,United States',\n  'salt lake city':   'Salt Lake City,Utah,United States',\n  'toronto':          'Toronto,Ontario,Canada',\n  'vancouver':        'Vancouver,British Columbia,Canada',\n  'montreal':         'Montreal,Quebec,Canada',\n  'calgary':          'Calgary,Alberta,Canada',\n  'ottawa':           'Ottawa,Ontario,Canada',\n  'london':           'London,England,United Kingdom',\n  'manchester':       'Manchester,England,United Kingdom',\n  'birmingham':       'Birmingham,England,United Kingdom',\n  'edinburgh':        'Edinburgh,Scotland,United Kingdom',\n  'glasgow':          'Glasgow,Scotland,United Kingdom',\n  'leeds':            'Leeds,England,United Kingdom',\n  'sydney':           'Sydney,New South Wales,Australia',\n  'melbourne':        'Melbourne,Victoria,Australia',\n  'brisbane':         'Brisbane,Queensland,Australia',\n  'perth':            'Perth,Western Australia,Australia',\n  'adelaide':         'Adelaide,South Australia,Australia',\n  'dublin':           'Dublin,Leinster,Ireland',\n}\n","import type { CanonicalLocationName, UuleString } from './types.js'\nimport { LOCATIONS } from './locations.js'\n\nfunction encodeVarint(value: number): number[] {\n  const bytes: number[] = []\n  let remaining = value\n  do {\n    let byte = remaining & 0x7f\n    remaining >>>= 7\n    if (remaining > 0) byte |= 0x80\n    bytes.push(byte)\n  } while (remaining > 0)\n  return bytes\n}\n\nexport function encodeUule(name: CanonicalLocationName): UuleString {\n  const locationBytes = Buffer.from(name, 'utf8')\n  const payload = Buffer.concat([\n    Buffer.from([0x08, 0x02, 0x10, 0x20, 0x22]),\n    Buffer.from(encodeVarint(locationBytes.length)),\n    locationBytes,\n  ])\n  return `w+${payload.toString('base64')}`\n}\n\nexport function normalizeLocation(input: string): CanonicalLocationName {\n  const raw = input.toLowerCase().trim()\n  if (LOCATIONS[raw]) return LOCATIONS[raw] as CanonicalLocationName\n  const beforeComma = raw.split(',')[0].trim()\n  if (beforeComma !== raw && LOCATIONS[beforeComma]) return LOCATIONS[beforeComma] as CanonicalLocationName\n  const withoutState = raw.replace(/\\s+[a-z]{2}$/, '').trim()\n  if (withoutState !== raw && LOCATIONS[withoutState]) return LOCATIONS[withoutState] as CanonicalLocationName\n  return input as CanonicalLocationName\n}\n","import type { CanonicalLocationName } from './types.js'\n\nfunction normalizedTerms(value: string): string[] {\n  return value\n    .normalize('NFKD')\n    .replace(/[\\u0300-\\u036f]/g, '')\n    .toLowerCase()\n    .split(/[^a-z0-9]+/)\n    .filter(Boolean)\n}\n\nfunction includesTermSequence(query: string, phrase: string): boolean {\n  const queryTerms = normalizedTerms(query)\n  const phraseTerms = normalizedTerms(phrase)\n  if (phraseTerms.length === 0 || phraseTerms.length > queryTerms.length) return false\n\n  return queryTerms.some((_, start) => phraseTerms.every((term, index) => queryTerms[start + index] === term))\n}\n\nfunction cityFromCanonicalLocation(location: CanonicalLocationName, fallback: string): string {\n  return location.split(',', 1)[0]?.trim() || fallback.split(',', 1)[0]?.trim() || fallback.trim()\n}\n\n/**\n * Makes a location request visible to Google without making callers duplicate\n * a city that is already in their query. UULE carries the same location signal\n * separately; this function only determines the search text.\n */\nexport function buildLocalizedSerpQuery(\n  query: string,\n  location: string | undefined,\n  canonicalLocation: CanonicalLocationName | null,\n): string {\n  const normalizedQuery = query.trim()\n  const requestedLocation = location?.trim()\n  if (!requestedLocation || !canonicalLocation) return normalizedQuery\n\n  const city = cityFromCanonicalLocation(canonicalLocation, requestedLocation)\n  return includesTermSequence(normalizedQuery, city)\n    ? normalizedQuery\n    : `${normalizedQuery} ${requestedLocation}`\n}\n","import type { CanonicalLocationName, LocalPackBusiness, OrganicResult, SerpLocationCandidate, SerpLocationEvidence } from './types.js'\n\nconst STATE_TO_CODE: Record<string, string> = {\n  alabama: 'AL', alaska: 'AK', arizona: 'AZ', arkansas: 'AR', california: 'CA',\n  colorado: 'CO', connecticut: 'CT', delaware: 'DE', florida: 'FL', georgia: 'GA',\n  hawaii: 'HI', idaho: 'ID', illinois: 'IL', indiana: 'IN', iowa: 'IA',\n  kansas: 'KS', kentucky: 'KY', louisiana: 'LA', maine: 'ME', maryland: 'MD',\n  massachusetts: 'MA', michigan: 'MI', minnesota: 'MN', mississippi: 'MS', missouri: 'MO',\n  montana: 'MT', nebraska: 'NE', nevada: 'NV', 'new hampshire': 'NH', 'new jersey': 'NJ',\n  'new mexico': 'NM', 'new york': 'NY', 'north carolina': 'NC', 'north dakota': 'ND', ohio: 'OH',\n  oklahoma: 'OK', oregon: 'OR', pennsylvania: 'PA', 'rhode island': 'RI', 'south carolina': 'SC',\n  'south dakota': 'SD', tennessee: 'TN', texas: 'TX', utah: 'UT', vermont: 'VT',\n  virginia: 'VA', washington: 'WA', 'west virginia': 'WV', wisconsin: 'WI', wyoming: 'WY',\n  'district of columbia': 'DC',\n}\n\nconst STATE_PATTERN = [\n  ...Object.keys(STATE_TO_CODE).map(s => s.replace(/\\s+/g, '\\\\s+')),\n  ...Object.values(STATE_TO_CODE),\n].join('|')\n\nconst CITY_STATE_RE = new RegExp(`\\\\b([A-Z][A-Za-z]+(?:[\\\\s.-][A-Z][A-Za-z]+){0,4}),?\\\\s+(${STATE_PATTERN})\\\\b`, 'gi')\n\nfunction normalizeRegionCode(input: string | null | undefined): string | null {\n  if (!input) return null\n  const trimmed = input.trim()\n  if (/^[A-Z]{2}$/i.test(trimmed)) return trimmed.toUpperCase()\n  return STATE_TO_CODE[trimmed.toLowerCase()] ?? null\n}\n\nfunction normalizeCity(input: string): string {\n  const cleaned = input\n    .replace(/\\s+/g, ' ')\n    .trim()\n    .replace(/^.*\\b(?:in|near|around|serving)\\s+/i, '')\n  return cleaned.toLowerCase().replace(/\\b[a-z]/g, char => char.toUpperCase())\n}\n\nfunction parseExpected(canonicalLocation: CanonicalLocationName | null): SerpLocationEvidence['expected'] {\n  if (!canonicalLocation) return null\n  const [city = '', region = ''] = canonicalLocation.split(',').map(part => part.trim())\n  return {\n    city: normalizeCity(city),\n    regionCode: normalizeRegionCode(region),\n    canonicalLocation,\n  }\n}\n\nfunction addCandidate(\n  candidates: Map<string, SerpLocationCandidate>,\n  city: string,\n  region: string,\n  example: string,\n): void {\n  const normalizedCity = normalizeCity(city)\n  const regionCode = normalizeRegionCode(region)\n  if (!normalizedCity || !regionCode) return\n  const key = `${normalizedCity.toLowerCase()}|${regionCode}`\n  const existing = candidates.get(key)\n  if (existing) {\n    existing.count++\n    if (existing.examples.length < 3 && !existing.examples.includes(example)) existing.examples.push(example)\n    return\n  }\n  candidates.set(key, { city: normalizedCity, regionCode, count: 1, examples: [example] })\n}\n\nfunction decodeSerpText(text: string): string {\n  try {\n    return decodeURIComponent(text)\n  } catch {}\n\n  try {\n    return decodeURIComponent(text.replace(/%(?![0-9a-fA-F]{2})/g, '%25'))\n  } catch {\n    return text\n  }\n}\n\nfunction scanText(candidates: Map<string, SerpLocationCandidate>, text: string): void {\n  const normalized = decodeSerpText(text).replace(/[+/|_-]+/g, ' ')\n  for (const match of normalized.matchAll(CITY_STATE_RE)) {\n    addCandidate(candidates, match[1] ?? '', match[2] ?? '', normalized.slice(0, 180))\n  }\n}\n\nexport function inferSerpLocationEvidence(\n  canonicalLocation: CanonicalLocationName | null,\n  organicResults: OrganicResult[],\n  localPack: LocalPackBusiness[],\n): SerpLocationEvidence {\n  const expected = parseExpected(canonicalLocation)\n  const candidates = new Map<string, SerpLocationCandidate>()\n\n  for (const result of organicResults) {\n    scanText(candidates, [result.title, result.snippet ?? '', result.cite ?? '', result.url].join(' '))\n  }\n  for (const business of localPack) {\n    scanText(candidates, [business.name, ...business.metadata, business.websiteUrl ?? '', business.directionsUrl ?? ''].join(' '))\n  }\n\n  const rankedCandidates = Array.from(candidates.values())\n    .sort((a, b) => b.count - a.count || a.city.localeCompare(b.city))\n    .slice(0, 8)\n\n  if (!expected) {\n    return { status: 'not_requested', expected: null, candidates: rankedCandidates }\n  }\n  if (rankedCandidates.length === 0) {\n    return { status: 'unknown', expected, candidates: [] }\n  }\n\n  const matched = rankedCandidates.some(candidate =>\n    candidate.city.toLowerCase() === expected.city.toLowerCase()\n    && (expected.regionCode == null || candidate.regionCode === expected.regionCode)\n  )\n\n  return {\n    status: matched ? 'matched' : 'mismatch',\n    expected,\n    candidates: rankedCandidates,\n  }\n}\n","const MAX_ANSWER_LENGTH = 1200\n\nconst BOILERPLATE_PATTERNS: RegExp[] = [\n  /An AI Overview is not available for this search/gi,\n  /Can't generate an AI overview right now\\.?\\s*Try again later\\.?/gi,\n  /\\bAI Overview\\b/gi,\n  /\\bView all\\b/gi,\n]\n\nconst CUT_MARKERS: RegExp[] = [\n  /\\bRelated Links\\b/i,\n  /\\bAsk anything in\\s*AI Mode\\b/i,\n  /\\bAI can make mistakes\\b/i,\n  /\\bThis is for informational purposes only\\b/i,\n  /\\bShow more\\b/i,\n  /\\b\\d+\\s+sites\\b/i,\n  /\\b\\d{1,2}\\s*[msh]\\s*[A-Z][A-Za-z]/,\n  /\\b(?:YouTube|Reddit|Facebook|Instagram|TikTok)·/,\n]\n\nfunction normalizeWhitespace(text: string): string {\n  return text\n    .replace(/\\u00a0/g, ' ')\n    .replace(/([.!?])([A-Z])/g, '$1 $2')\n    .replace(/([:;])([A-Z])/g, '$1 $2')\n    .replace(/([a-z])([A-Z][a-z])/g, '$1 $2')\n    .replace(/(\\d)([A-Z][a-z])/g, '$1 $2')\n    .replace(/([a-z])(\\d)/g, '$1 $2')\n    .replace(/\\s+/g, ' ')\n    .trim()\n}\n\nfunction cutAtFirstMarker(text: string): string {\n  let cutAt = -1\n  for (const marker of CUT_MARKERS) {\n    const match = marker.exec(text)\n    marker.lastIndex = 0\n    if (match && (cutAt === -1 || match.index < cutAt)) cutAt = match.index\n  }\n  return cutAt === -1 ? text : text.slice(0, cutAt)\n}\n\nfunction cutAtSourceTitle(text: string, sourceTitle?: string): string {\n  const title = sourceTitle?.trim()\n  if (!title || title.length < 8) return text\n  const idx = text.toLowerCase().indexOf(title.toLowerCase())\n  return idx > 40 ? text.slice(0, idx) : text\n}\n\nfunction findAttributionCut(beforeUrl: string): number {\n  const dateMatch = beforeUrl.match(/[•·]\\s*(?:Jan|Feb|Mar|Apr|May|Jun|Jul|Aug|Sep|Oct|Nov|Dec)[a-z]*\\.?\\s+\\d{1,2},\\s+\\d{4}/i)\n  if (dateMatch?.index && dateMatch.index > 40) return dateMatch.index\n\n  const start = Math.max(0, beforeUrl.length - 260)\n  const tail = beforeUrl.slice(start)\n  const sentenceBreaks = [...tail.matchAll(/[.!?]\\s*(?=[A-Z][A-Za-z0-9\"'$])/g)]\n  for (const match of sentenceBreaks) {\n    const remainder = tail.slice(match.index + 1).trim()\n    const lead = remainder.slice(0, 160)\n    const looksLikeTitle = /^(?:Best|Top|What|How|Why|When|Where|Which|Can|Should|Is|Are|Do|Does)\\b/i.test(remainder)\n    if (\n      remainder.length > 20 &&\n      looksLikeTitle &&\n      /(?:\\s[-|]\\s|Heating|Cooling|Company|Services|Blog|Guide|Review)/i.test(lead)\n    ) {\n      return start + match.index + 1\n    }\n  }\n  const last = sentenceBreaks.at(-1)\n  if (last?.index !== undefined) return start + last.index + 1\n\n  return beforeUrl.length\n}\n\nfunction cutAtUrlAttribution(text: string): string {\n  const urlMatch = text.match(/https?:\\/\\/\\S+/i)\n  if (!urlMatch?.index) return text\n\n  const beforeUrl = text.slice(0, urlMatch.index)\n  return beforeUrl.slice(0, findAttributionCut(beforeUrl))\n}\n\nfunction trimToSentenceLimit(text: string): string {\n  if (text.length <= MAX_ANSWER_LENGTH) return text\n  const slice = text.slice(0, MAX_ANSWER_LENGTH)\n  const lastSentence = Math.max(slice.lastIndexOf('.'), slice.lastIndexOf('!'), slice.lastIndexOf('?'))\n  return (lastSentence > 240 ? slice.slice(0, lastSentence + 1) : slice).trim()\n}\n\nexport function cleanPAAAnswerText(\n  answer: string | null | undefined,\n  question?: string,\n  sourceTitle?: string,\n): string | undefined {\n  if (!answer) return undefined\n\n  let text = normalizeWhitespace(answer)\n  const normalizedQuestion = question ? normalizeWhitespace(question) : ''\n  if (normalizedQuestion && text.toLowerCase().startsWith(normalizedQuestion.toLowerCase())) {\n    text = text.slice(normalizedQuestion.length).trim()\n  }\n\n  if (/^An error has occurred\\.?\\s*Please try again later\\.?/i.test(text)) {\n    return undefined\n  }\n\n  for (const pattern of BOILERPLATE_PATTERNS) {\n    text = text.replace(pattern, ' ')\n  }\n  text = text\n    .replace(/\\b[A-Z][A-Za-z&'\\u2019 -]{2,60}\\+\\d+\\b/g, ' ')\n    .replace(/\\b(?:[a-z0-9-]+\\.)+[a-z]{2,}\\+\\d+\\b/gi, ' ')\n\n  text = normalizeWhitespace(text)\n  text = cutAtFirstMarker(text)\n  text = cutAtSourceTitle(text, sourceTitle)\n  text = cutAtUrlAttribution(text)\n  text = normalizeWhitespace(text)\n  text = text.replace(/\\s+(?:Jan|Feb|Mar|Apr|May|Jun|Jul|Aug|Sep|Oct|Nov|Dec)[a-z]*\\.?\\s+\\d{1,2},\\s+\\d{4}$/i, '').trim()\n  text = trimToSentenceLimit(text)\n\n  if (!text) return undefined\n  if (/^An error has occurred\\.?\\s*Please try again later\\.?$/i.test(text)) return undefined\n\n  return text\n}\n","export interface AISurfaceSelectorConfig {\n  aio: {\n    root: string\n    legacyRoot?: string\n    wrapper?: string\n    controller: string\n    contentSubtree: string\n    heading: string\n    header: string\n    showMoreButton: string\n    sourcesPanel: string\n    disclaimer: string\n  }\n  aim: {\n    root: string\n    wrapper: string\n  }\n  expandWaitMs?: number\n}\n\nexport async function extractAISurfacesFromDocument(config?: AISurfaceSelectorConfig): Promise<{\n  surface: 'web' | 'aim' | 'unknown'\n  aiOverview: {\n    detected: boolean\n    text: string | null\n    citations: Array<{ text: string; href: string }>\n    expanded: boolean\n    fullyExpanded: boolean\n    sections: string[]\n  }\n  aiMode: {\n    detected: boolean\n    text: string | null\n    citations: Array<{ text: string; href: string }>\n  }\n}> {\n  const selectors = config ?? {\n    aio: {\n      root: '[data-lhcontainer][data-streaming-container][eid]',\n      legacyRoot: '[data-hveid=\"CBMQAA\"]',\n      wrapper: '.Fgyi2e',\n      controller: '[jscontroller=\"AkrxPe\"]',\n      contentSubtree: '[data-subtree=\"mfc\"]',\n      heading: '.Fzsovc.cwYVJe.RJPOee',\n      header: '.heWuVc',\n      showMoreButton: '[aria-label=\"Show more AI Overview\"]',\n      sourcesPanel: '.OZ9ddf.WAUd4',\n      disclaimer: '.DuQANe.MSJHRb',\n    },\n    aim: {\n      root: '[data-hveid=\"CAUQAA\"]',\n      wrapper: '.Fgyi2e',\n    },\n    expandWaitMs: 1500,\n  }\n\n  const sn: string = (window as unknown as { google?: { sn?: string } }).google?.sn ?? 'unknown'\n  const surface: 'web' | 'aim' | 'unknown' =\n    sn === 'aim' ? 'aim' : sn === 'web' ? 'web' : 'unknown'\n\n  function textOf(el: Element | null): string {\n    if (!el) return ''\n    return ((el as HTMLElement).innerText ?? el.textContent ?? '').trim()\n  }\n\n  function hasSubstantiveContent(text: string | null): text is string {\n    if (!text) return false\n    const normalized = text.replace(/\\s+/g, ' ').trim()\n    return normalized.length >= 16 && normalized.split(' ').filter(Boolean).length >= 2\n  }\n\n  function hasAIOverviewLabel(el: Element): boolean {\n    const heading = el.querySelector(selectors.aio.heading)\n    if (textOf(heading) === 'AI Overview') return true\n    const header = el.querySelector(selectors.aio.header)\n    if (textOf(header).split(/\\n|\\s{2,}/).some(part => part.trim() === 'AI Overview')) return true\n    return textOf(el).includes('AI Overview')\n  }\n\n  function findAIORoot(): Element | null {\n    const primaryRoots = Array.from(document.querySelectorAll(selectors.aio.root))\n    const labeledPrimary = primaryRoots.find(hasAIOverviewLabel)\n    if (labeledPrimary) return labeledPrimary\n    if (primaryRoots.length > 0) return primaryRoots[0]\n\n    if (selectors.aio.legacyRoot) {\n      const legacy = document.querySelector(selectors.aio.legacyRoot)\n      if (legacy) return legacy\n    }\n\n    const headings = document.querySelectorAll(`${selectors.aio.heading}, h1, h2, h3, [role=\"heading\"]`)\n    for (const h of headings) {\n      if (textOf(h) !== 'AI Overview') continue\n      let el: Element | null = h.parentElement\n      for (let i = 0; i < 8 && el; i++) {\n        if (\n          el.matches(selectors.aio.root) ||\n          el.querySelector(selectors.aio.controller) ||\n          el.querySelector(selectors.aio.contentSubtree)\n        ) {\n          return el\n        }\n        el = el.parentElement\n      }\n      return h.parentElement\n    }\n    return null\n  }\n\n  function cleanText(target: Element | null): string | null {\n    if (!target) return null\n    const clone = target.cloneNode(true) as Element\n    clone.querySelectorAll([\n      'script',\n      'style',\n      'noscript',\n      'img',\n      'picture',\n      'video',\n      selectors.aio.header,\n      selectors.aio.showMoreButton,\n      selectors.aio.sourcesPanel,\n      selectors.aio.disclaimer,\n      '[data-subtree=\"dfa\"]',\n      '[data-src-id]',\n      '[role=\"dialog\"]',\n      '.HWMcu',\n      '.bTFeG',\n      '.CyMdWb',\n      '.MFrAxb',\n      '.F0OfWd.hfWAgb',\n      '.x2qcTc.fZavHb',\n      '.SvjEff',\n      '.sR2MY',\n      '.lKuDef',\n      '.GSPQcc',\n      'a[href]',\n      'button',\n      '[role=\"button\"]',\n    ].join(',')).forEach(el => el.remove())\n\n    const holder = document.createElement('div')\n    holder.style.position = 'fixed'\n    holder.style.left = '-10000px'\n    holder.style.top = '0'\n    holder.style.width = `${Math.max(320, Math.round((target as HTMLElement).getBoundingClientRect?.().width || 960))}px`\n    holder.style.opacity = '0'\n    holder.style.pointerEvents = 'none'\n    holder.append(clone)\n    document.body.append(holder)\n\n    const rendered = ((clone as HTMLElement).innerText || clone.textContent || '')\n    holder.remove()\n\n    const lines = rendered\n      .replace(/\\r/g, '')\n      .replace(/[ \\t]+\\n/g, '\\n')\n      .replace(/\\n[ \\t]+/g, '\\n')\n      .replace(/\\n{3,}/g, '\\n\\n')\n      .replace(/[ \\t]{2,}/g, ' ')\n      .trim()\n      .split('\\n')\n      .map(line => line.replace(/\\u00a0/g, ' ').trim())\n      .filter(Boolean)\n\n    const filteredLines: string[] = []\n    for (let i = 0; i < lines.length; i++) {\n      const line = lines[i]\n      const next = lines[i + 1] ?? ''\n      if (line === 'AI Overview') continue\n      if (line === 'Show more') continue\n      if (/^AI can make mistakes/i.test(line)) continue\n      if (/^Thank you\\b/i.test(line)) continue\n      if (/^Your feedback helps Google improve/i.test(line)) continue\n      if (/^\\+?\\d+$/.test(line)) continue\n      if (/^\\+\\d+$/.test(next) && line.length <= 80) {\n        i++\n        continue\n      }\n      filteredLines.push(line)\n    }\n\n    const raw = filteredLines\n      .join('\\n')\n      .replace(/\\n{3,}/g, '\\n\\n')\n      .trim()\n\n    if (!raw || /not available|try again|can't generate/i.test(raw)) return null\n    return raw\n  }\n\n  function normalizeHref(rawHref: string): string | null {\n    if (!rawHref || rawHref.startsWith('javascript:')) return null\n    let href = rawHref\n    try {\n      const absolute = new URL(rawHref, window.location.href)\n      const q = absolute.searchParams.get('q') ?? absolute.searchParams.get('url')\n      if (/(\\.|^)google\\./i.test(absolute.hostname) && q?.startsWith('http')) {\n        href = q\n      } else {\n        href = absolute.href\n      }\n    } catch {\n      return null\n    }\n\n    if (!/^https?:\\/\\//i.test(href)) return null\n    try {\n      const url = new URL(href)\n      const isGoogleInternal = /(\\.|^)google\\./i.test(url.hostname)\n      if (isGoogleInternal) return null\n      return url.href\n    } catch {\n      return null\n    }\n  }\n\n  function extractCitations(root: Element | null): Array<{ text: string; href: string }> {\n    if (!root) return []\n    const seen = new Set<string>()\n    const citations: Array<{ text: string; href: string }> = []\n\n    for (const a of Array.from(root.querySelectorAll('a[href]'))) {\n      const href = normalizeHref(a.getAttribute('href') ?? '')\n      if (!href || seen.has(href)) continue\n      seen.add(href)\n\n      let fallbackHost = ''\n      try {\n        fallbackHost = new URL(href).hostname.replace(/^www\\./, '')\n      } catch {}\n\n      citations.push({\n        text: textOf(a) || fallbackHost || href,\n        href,\n      })\n    }\n\n    return citations\n  }\n\n  async function maybeExpand(root: Element): Promise<boolean> {\n    const button = root.querySelector(selectors.aio.showMoreButton)\n    if (!button || button.getAttribute('aria-expanded') !== 'false') return false\n    ;(button as HTMLElement).click()\n    const waitMs = selectors.expandWaitMs ?? 1500\n    if (waitMs > 0) await new Promise(resolve => setTimeout(resolve, waitMs))\n    return true\n  }\n\n  const aioRoot = findAIORoot()\n  let aioText: string | null = null\n  let aioCitations: Array<{ text: string; href: string }> = []\n  let aioExpanded = false\n  let aioFullyExpanded = false\n  let aioSections: string[] = []\n\n  if (aioRoot) {\n    aioExpanded = await maybeExpand(aioRoot)\n    const controller = aioRoot.querySelector(selectors.aio.controller)\n    const contentSubtree = aioRoot.querySelector(selectors.aio.contentSubtree)\n    const showMore = aioRoot.querySelector(selectors.aio.showMoreButton)\n    aioFullyExpanded =\n      controller?.getAttribute('data-trnct') === 'false' ||\n      showMore?.getAttribute('aria-expanded') === 'true' ||\n      !showMore\n\n    aioText = cleanText(contentSubtree ?? controller ?? aioRoot)\n    aioSections = (aioText ?? '')\n      .split('\\n')\n      .map(line => line.trim())\n      .filter(line => /^\\d+\\.\\s+.+/.test(line))\n    aioCitations = extractCitations(aioRoot)\n  }\n\n  const aimRoot = document.querySelector(selectors.aim.root)\n  const aimRootDetected = surface === 'aim' && !!aimRoot\n  const aimContainer = aimRoot?.closest(selectors.aim.wrapper) ?? aimRoot\n  const rawAimText = aimRootDetected ? cleanText(aimContainer) : null\n  const aimDetected = aimRootDetected && hasSubstantiveContent(rawAimText)\n  const aimText = aimDetected ? rawAimText : null\n  const aimCitations = aimDetected ? extractCitations(aimContainer) : []\n\n  const aiOverviewDetected = !!aioRoot && hasSubstantiveContent(aioText)\n\n  return {\n    surface,\n    aiOverview: {\n      detected: aiOverviewDetected,\n      text: aiOverviewDetected ? aioText : null,\n      citations: aiOverviewDetected ? aioCitations : [],\n      expanded: aiOverviewDetected ? aioExpanded : false,\n      fullyExpanded: aiOverviewDetected ? aioFullyExpanded : false,\n      sections: aiOverviewDetected ? aioSections : [],\n    },\n    aiMode: {\n      detected: aimDetected && aimText !== null,\n      text: aimText,\n      citations: aimCitations,\n    },\n  }\n}\n","import type { Page } from 'playwright'\nimport type { IBrowserDriver } from '../driver/IBrowserDriver.js'\nimport type { IProgressReporter } from '../output/IProgressReporter.js'\nimport { PAASelectors, VideoSelectors, ForumSelectors, ShortVideoSelectors, AIOverviewSelectors, AIModeSelectors, WhatPeopleSayingSelectors, OrganicSelectors, LocalPackSelectors } from '../selectors.js'\nimport { encodeUule, normalizeLocation } from '../uule.js'\nimport { buildLocalizedSerpQuery } from '../serp-location-query.js'\nimport { inferSerpLocationEvidence } from '../serp-location-debug.js'\nimport { RawPAAItemSchema, recencyToTbs } from '../schemas.js'\nimport { cleanPAAAnswerText } from '../lib/paa-answer-cleanup.js'\nimport { CaptchaError, RequestAbortedError } from '../errors.js'\nimport { extractAISurfacesFromDocument } from './ai-surfaces.js'\nimport type {\n  HarvestOptions, HarvestResult, HarvestStats,\n  RawPAAItem, PAANode, FlatRow, NormalizedQuestionKey, DriverConfig,\n  VideoResult, ForumResult, AIOverviewResult, AIModeResult, GoogleSurface,\n  WhatPeopleSayingCard, EntityIds, EntityRecord, HarvestDiagnosticWarning, HarvestCompleteness,\n  OrganicResult, LocalPackBusiness, BrowserDebugSnapshot, HarvestDebugSnapshot, SerpLocationEvidence,\n} from '../types.js'\nimport type { IPAAExtractor } from './IPAAExtractor.js'\n\nexport class PAAExtractor implements IPAAExtractor {\n  constructor(\n    private readonly driver: IBrowserDriver,\n    private readonly reporter: IProgressReporter,\n  ) {}\n\n  private completeness: HarvestCompleteness = {\n    paaWithoutAnswer: 0,\n    paaWithoutSource: 0,\n    paaAnswersRecovered: 0,\n    aioShareCaptured: null,\n  }\n\n  private aioShareUrlEarly: string | null = null\n  private collectedRows: FlatRow[] = []\n  private partialContext: { seed: string; location: string | null; startMs: number } | null = null\n\n  private normalizeQuestion(q: string): NormalizedQuestionKey {\n    return q.toLowerCase().replace(/[^\\w\\s]/g, '').replace(/\\s+/g, ' ').trim() as NormalizedQuestionKey\n  }\n\n  private throwIfAborted(signal?: AbortSignal): void {\n    if (!signal?.aborted) return\n    if (signal.reason instanceof DOMException && signal.reason.name === 'TimeoutError') throw signal.reason\n    throw new RequestAbortedError()\n  }\n\n  private async throwIfCaptcha(page: Page, context: string): Promise<void> {\n    const captchaCount = await page.locator(PAASelectors.captchaMarker).count().catch(() => 0)\n    if (captchaCount > 0) {\n      throw new CaptchaError(`${context} returned a CAPTCHA — retrying with a fresh session.`)\n    }\n  }\n\n  private async extractVisibleItems(page: Page): Promise<RawPAAItem[]> {\n    const sels = PAASelectors\n    const raw = await page.evaluate((selectors) => {\n      function cleanText(el: Element | null): string {\n        if (!el) return ''\n        const parts: string[] = []\n        for (const n of el.childNodes) {\n          if (n.nodeType === Node.TEXT_NODE) {\n            const text = n.textContent?.trim()\n            if (text) parts.push(text)\n          } else if (\n            (n as Element).tagName === 'STYLE' ||\n            (n as Element).tagName === 'SCRIPT'\n          ) {\n            continue\n          } else {\n            const text = cleanText(n as Element)\n            if (text) parts.push(text)\n          }\n        }\n        return parts.join(' ').replace(/\\s+/g, ' ').trim()\n      }\n      function cleanSourceLabel(value: string | null | undefined): string {\n        return (value ?? '')\n          .replace(/\\s*\\(\\+\\d+\\)\\s*-\\s*View related links.*$/i, '')\n          .replace(/\\s*-\\s*Opens in new tab.*$/i, '')\n          .trim()\n      }\n      return Array.from(document.querySelectorAll(selectors.item)).map((pair) => {\n        const clickTarget = pair.querySelector(selectors.clickTarget)\n        const expanded = pair.classList.contains(selectors.expandedClass)\n          || clickTarget?.getAttribute('aria-expanded') === 'true'\n        const answerContainer = expanded\n          ? pair.querySelector(selectors.answerContainer)\n          : null\n        const aiAnswerBody = answerContainer?.querySelector(selectors.aiAnswerBody)\n        const answerElement = aiAnswerBody ?? answerContainer\n        let sourceTitle = expanded\n          ? (pair.querySelector(selectors.sourceTitle) as HTMLElement | null)?.innerText?.trim() || undefined\n          : undefined\n        let sourceSite = expanded\n          ? (pair.querySelector(selectors.sourceSite) as HTMLElement | null)?.innerText?.trim() || undefined\n          : undefined\n        let sourceCite = expanded\n          ? (pair.querySelector(selectors.sourceCite) as HTMLElement | null)?.innerText?.trim() || undefined\n          : undefined\n        if (expanded && !sourceCite) {\n          const seenHrefs = new Set<string>()\n          const anchors: Array<{ text: string; label: string; href: string; host: string }> = []\n          const anchorRoot = answerElement ?? pair\n          const prioritized = [\n            ...Array.from(anchorRoot.querySelectorAll(selectors.aiCitation)),\n            ...Array.from(anchorRoot.querySelectorAll(selectors.aiSourceCard)),\n            ...Array.from(anchorRoot.querySelectorAll('a[href]')),\n          ] as HTMLAnchorElement[]\n          for (const a of prioritized) {\n            const href = a.href\n            if (!/^https?:\\/\\//.test(href) || seenHrefs.has(href)) continue\n            let host = ''\n            try { host = new URL(href).hostname } catch { continue }\n            if (/(^|\\.)google(usercontent)?\\.[a-z.]+$/.test(host)) continue\n            seenHrefs.add(href)\n            anchors.push({\n              text: (a.textContent || '').trim(),\n              label: cleanSourceLabel(a.getAttribute('aria-label')),\n              href,\n              host,\n            })\n          }\n          const primary = anchors.find(x => x.label.length > 2 || x.text.length > 2) ?? anchors[0]\n          if (primary) {\n            sourceTitle = sourceTitle || primary.label || primary.text || undefined\n            sourceSite  = sourceSite || primary.host.replace(/^www\\./, '')\n            sourceCite  = primary.href\n          }\n        }\n        return {\n          question:\n            pair.getAttribute(selectors.itemDataQ) ||\n            pair.getAttribute(selectors.itemDataInitQ) ||\n            (pair.querySelector(selectors.itemQuestionEl) as HTMLElement | null)?.innerText?.trim() ||\n            '',\n          answer: cleanText(answerElement) || undefined,\n          sourceTitle,\n          sourceSite,\n          sourceCite,\n        }\n      })\n    }, sels)\n\n    return raw.flatMap((item) => {\n      const cleaned = {\n        ...item,\n        answer: cleanPAAAnswerText(item.answer, item.question, item.sourceTitle),\n      }\n      const result = RawPAAItemSchema.safeParse(cleaned)\n      if (!result.success) {\n        console.warn('[PAAExtractor] item parse failed:', item.question, result.error.issues[0]?.message)\n        return []\n      }\n      return [result.data]\n    })\n  }\n\n  private async clickItem(page: Page, questionText: string): Promise<void> {\n    try {\n      const pairLocator = page.locator(\n        `${PAASelectors.item}[data-q=\"${questionText}\"], ${PAASelectors.item}[data-initq=\"${questionText}\"]`\n      ).first()\n      await pairLocator.click()\n    } catch { }\n  }\n\n  private toFlatRow(item: RawPAAItem, depth: number, parentQuestion: string | null, seed: string): FlatRow {\n    return {\n      seed_query:      seed,\n      question:        item.question,\n      answer:          item.answer ?? '',\n      source_title:    item.sourceTitle ?? '',\n      source_site:     item.sourceSite ?? '',\n      source_cite:     item.sourceCite ?? '',\n      depth,\n      parent_question: parentQuestion ?? '',\n      extracted_at:    new Date().toISOString(),\n    }\n  }\n\n  getPartialResult(): HarvestResult | null {\n    const ctx = this.partialContext\n    if (!ctx || this.collectedRows.length === 0) return null\n    const flat = [...this.collectedRows]\n    const stats: HarvestStats = {\n      seed: ctx.seed,\n      totalQuestions: flat.length,\n      maxDepthReached: flat.reduce((m, r) => Math.max(m, r.depth), 0),\n      durationMs: Date.now() - ctx.startMs,\n      errorCount: 0,\n    }\n    return {\n      seed: ctx.seed,\n      location: ctx.location,\n      extractedAt: new Date().toISOString(),\n      diagnostics: {\n        completionStatus: 'paa_found',\n        problem: null,\n        completeness: { ...this.completeness },\n        warnings: [{\n          code: 'paa_partial',\n          surface: 'paa',\n          message: `Harvest stopped before finishing; returning the ${flat.length} question(s) collected up to that point.`,\n          retryable: true,\n        }],\n      },\n      totalQuestions: flat.length,\n      whatPeopleSaying: [],\n      tree: this.buildTree(flat, ctx.seed),\n      flat,\n      videos: [],\n      forums: [],\n      organicResults: [],\n      localPack: [],\n      entityIds: {},\n      stats,\n    } as unknown as HarvestResult\n  }\n\n  private async runBFS(page: Page, options: HarvestOptions, signal?: AbortSignal): Promise<FlatRow[]> {\n    const seenKeys  = new Set<NormalizedQuestionKey>()\n    const seenQs    = new Set<string>()\n    const orderedQs: string[] = []\n    this.collectedRows = []\n    const results: FlatRow[] = this.collectedRows\n    const abandonedNeverReclick = new Set<string>()\n    const clickedOnceEver = new Set<string>()\n\n    const itemArgs = {\n      sel: PAASelectors.item,\n      dataQ: PAASelectors.itemDataQ,\n      dataInitQ: PAASelectors.itemDataInitQ,\n      questionEl: PAASelectors.itemQuestionEl,\n      answerSel: PAASelectors.answerContainer,\n      expandedClass: PAASelectors.expandedClass,\n      clickTarget: PAASelectors.clickTarget,\n    }\n\n    const readItemStates = (): Promise<Array<{ q: string; done: boolean }>> =>\n      page.evaluate(\n        ({ sel, dataQ, dataInitQ, questionEl, expandedClass, answerSel }: typeof itemArgs) => {\n          const answerText = (root: Element | null): string => {\n            if (!root) return ''\n            let out = ''\n            const walk = (n: Node): void => {\n              for (const c of Array.from(n.childNodes)) {\n                if (c.nodeType === Node.TEXT_NODE) out += c.textContent || ''\n                else if (c.nodeType === Node.ELEMENT_NODE && (c as Element).tagName !== 'STYLE' && (c as Element).tagName !== 'SCRIPT') walk(c)\n              }\n            }\n            walk(root)\n            return out.replace(/\\s+/g, ' ').trim()\n          }\n          return Array.from(document.querySelectorAll(sel))\n            .map(el => ({\n              q:\n                el.getAttribute(dataQ) ||\n                el.getAttribute(dataInitQ) ||\n                (el.querySelector(questionEl) as HTMLElement | null)?.innerText?.trim() ||\n                '',\n              done: el.classList.contains(expandedClass) || answerText(el.querySelector(answerSel)).length > 30,\n            }))\n            .filter(s => s.q)\n        },\n        itemArgs,\n      )\n\n    const expandOneItemSerially = async (q: string): Promise<'ok' | 'failed'> => {\n      const clicked = await page.evaluate(\n        ({ sel, dataQ, dataInitQ, clickTarget, q: target }: typeof itemArgs & { q: string }) => {\n          const item = Array.from(document.querySelectorAll(sel)).find(el =>\n            (el.getAttribute(dataQ) || el.getAttribute(dataInitQ)) === target)\n          const btn = item?.querySelector(clickTarget) as HTMLElement | null\n          if (!btn) return false\n          btn.scrollIntoView({ block: 'center' })\n          btn.click()\n          return true\n        },\n        { ...itemArgs, q },\n      ).catch(() => false)\n      if (!clicked) return 'failed'\n      try {\n        const outcome = await page.waitForFunction(\n          ({ sel, dataQ, dataInitQ, answerSel, q: target }: typeof itemArgs & { q: string }) => {\n            const answerText = (root: Element | null): string => {\n              if (!root) return ''\n              let out = ''\n              const walk = (n: Node): void => {\n                for (const c of Array.from(n.childNodes)) {\n                  if (c.nodeType === Node.TEXT_NODE) out += c.textContent || ''\n                  else if (c.nodeType === Node.ELEMENT_NODE && (c as Element).tagName !== 'STYLE' && (c as Element).tagName !== 'SCRIPT') walk(c)\n                }\n              }\n              walk(root)\n              return out.replace(/\\s+/g, ' ').trim()\n            }\n            const item = Array.from(document.querySelectorAll(sel)).find(el =>\n              (el.getAttribute(dataQ) || el.getAttribute(dataInitQ)) === target)\n            if (!item) return 'gone'\n            const text = answerText(item.querySelector(answerSel))\n            if (/an error has occurred/i.test(text.slice(0, 80))) return 'error'\n            if (text.length > 30) return 'ok'\n            return false\n          },\n          { ...itemArgs, q },\n          { timeout: 6500, polling: 200 },\n        )\n        return (await outcome.jsonValue()) === 'ok' ? 'ok' : 'failed'\n      } catch {\n        return 'failed'\n      }\n    }\n\n    let round = 0\n    let growthWaits = 0\n    while (true) {\n      if (options.softDeadlineMs && Date.now() >= options.softDeadlineMs) break\n      this.throwIfAborted(signal)\n\n      const states = await readItemStates()\n      for (const s of states) {\n        if (!seenQs.has(s.q)) { seenQs.add(s.q); orderedQs.push(s.q) }\n      }\n\n      const kept = new Set(orderedQs.slice(0, options.maxQuestions))\n      const clickable = states.filter(s => kept.has(s.q) && !clickedOnceEver.has(s.q) && !abandonedNeverReclick.has(s.q))\n      const target = clickable.find(s => !s.done)\n        ?? (seenQs.size < options.maxQuestions ? clickable[0] : undefined)\n      if (!target) {\n        if (seenQs.size >= options.maxQuestions) break\n        if (++growthWaits > 1) break\n        const grew = await page.waitForFunction(\n          ({ sel, min }: { sel: string; min: number }) => document.querySelectorAll(sel).length > min,\n          { sel: PAASelectors.item, min: states.length },\n          { timeout: 3000 },\n        ).catch(() => null)\n        if (!grew) break\n        continue\n      }\n      growthWaits = 0\n\n      this.reporter.onDepth(++round)\n      await this.throwIfCaptcha(page, 'Google PAA expansion')\n      clickedOnceEver.add(target.q)\n      if ((await expandOneItemSerially(target.q)) === 'failed') abandonedNeverReclick.add(target.q)\n    }\n\n    const itemMap = new Map((await this.extractVisibleItems(page)).map(i => [i.question, i]))\n    await this.fillIncompleteItems(page, orderedQs, itemMap, options, abandonedNeverReclick)\n\n    for (const q of orderedQs) {\n      if (results.length >= options.maxQuestions) break\n      const key = this.normalizeQuestion(q)\n      if (seenKeys.has(key)) continue\n      seenKeys.add(key)\n      const item = itemMap.get(q)\n      if (item) {\n        results.push(this.toFlatRow(item, 1, null, options.query))\n        this.reporter.onQuestion({ question: item.question, answer: item.answer ?? null, sourceTitle: item.sourceTitle ?? null, sourceSite: item.sourceSite ?? null, sourceCite: item.sourceCite ?? null, depth: 1, parentQuestion: null, children: [] })\n      } else {\n        results.push(this.toFlatRow({ question: q, answer: undefined, sourceTitle: undefined, sourceSite: undefined, sourceCite: undefined }, 1, null, options.query))\n      }\n    }\n\n    this.completeness.paaWithoutAnswer = results.filter(r => !r.answer).length\n    this.completeness.paaWithoutSource = results.filter(r => !r.source_title && !r.source_site && !r.source_cite).length\n\n    return results\n  }\n\n  private async fillIncompleteItems(\n    page: Page,\n    orderedQs: string[],\n    itemMap: Map<string, RawPAAItem>,\n    options: HarvestOptions,\n    abandonedNeverReclick?: Set<string>,\n  ): Promise<void> {\n    const kept = new Set(orderedQs.slice(0, options.maxQuestions))\n    const missingBefore = new Set(\n      [...kept].filter(q => !itemMap.get(q)?.answer && !abandonedNeverReclick?.has(q)),\n    )\n    if (missingBefore.size === 0) return\n\n    for (let round = 0; round < 2; round++) {\n      if (options.softDeadlineMs && Date.now() >= options.softDeadlineMs) break\n      const stillMissing = [...missingBefore].filter(q => !itemMap.get(q)?.answer)\n      if (stillMissing.length === 0) break\n\n      const reExpanded = await this.reExpandCollapsedItems(page).catch(() => false)\n      await page.waitForTimeout(reExpanded ? 900 : 500)\n\n      let improved = false\n      for (const item of await this.extractVisibleItems(page)) {\n        if (!kept.has(item.question)) continue\n        const existing = itemMap.get(item.question)\n        if (!item.answer || existing?.answer) continue\n        itemMap.set(item.question, {\n          ...item,\n          sourceTitle: item.sourceTitle ?? existing?.sourceTitle,\n          sourceSite:  item.sourceSite ?? existing?.sourceSite,\n          sourceCite:  item.sourceCite ?? existing?.sourceCite,\n        })\n        improved = true\n      }\n      if (!improved) break\n    }\n\n    this.completeness.paaAnswersRecovered = [...missingBefore].filter(q => itemMap.get(q)?.answer).length\n  }\n\n  private async reExpandCollapsedItems(page: Page): Promise<boolean> {\n    const unexpandedSel = `${PAASelectors.item}:not(.${PAASelectors.expandedClass}) ${PAASelectors.clickTarget}`\n    const count = await page.locator(unexpandedSel).count().catch(() => 0)\n    if (count === 0) return false\n    for (let i = 0; i < count; i++) {\n      try {\n        const btn = page.locator(unexpandedSel).first()\n        await btn.scrollIntoViewIfNeeded()\n        await btn.click({ force: true })\n        await page.waitForTimeout(300)\n      } catch { }\n    }\n    return true\n  }\n\n  private async extractVideos(page: Page): Promise<VideoResult[]> {\n    const vsels = VideoSelectors\n    return page.evaluate((sels) => {\n      const results: Array<{ type: 'video' | 'short_video'; title: string; channel: string; platform: string; duration: string; url: string }> = []\n      const containers = Array.from(document.querySelectorAll(sels.container))\n      for (const container of containers) {\n        const headingEl = container.querySelector(sels.sectionHeading)\n        const headingText = headingEl?.textContent?.trim() ?? ''\n        const type: 'video' | 'short_video' = headingText.toLowerCase().includes('short') ? 'short_video' : 'video'\n        const items = Array.from(container.querySelectorAll(sels.item))\n        for (const a of items) {\n          const href = (a as HTMLAnchorElement).href\n          if (!href || (!href.includes('youtube') && !href.includes('youtu.be'))) continue\n          const raw = a.textContent?.trim() ?? ''\n          const ytIdx = raw.indexOf('YouTube')\n          if (ytIdx === -1) continue\n          const title = raw.slice(0, ytIdx).trim()\n          const remainder = raw.slice(ytIdx + 7).replace(/^[·\\s·]+/, '')\n          const channelMatch = remainder.match(/^([^·\\n]+)/)\n          const channel = channelMatch ? channelMatch[1].trim() : ''\n          if (title) results.push({ type, title, channel, platform: 'YouTube', duration: '', url: href })\n        }\n      }\n      return results\n    }, vsels)\n  }\n\n  private async extractForums(page: Page): Promise<ForumResult[]> {\n    const fsels = ForumSelectors\n    return page.evaluate((sels) => {\n      const results: Array<{ title: string; source: string; url: string }> = []\n      const sections = Array.from(document.querySelectorAll(sels.section))\n      const forumSection = sections.find((s) => s.textContent?.includes('Discussions'))\n      if (!forumSection) return results\n      const items = Array.from(forumSection.querySelectorAll(sels.item))\n      for (const a of items) {\n        const href = (a as HTMLAnchorElement).href\n        if (!href) continue\n        const titleEl = a.querySelector(sels.title)\n        const sourceEl = a.querySelector(sels.source)\n        const title = titleEl?.textContent?.trim() ?? ''\n        const source = sourceEl?.textContent?.trim() ?? ''\n        if (title) results.push({ title, source, url: href })\n      }\n      return results\n    }, fsels)\n  }\n\n  private async extractShortVideos(page: Page, shortUrl: string): Promise<VideoResult[]> {\n    try {\n      await page.goto(shortUrl, { waitUntil: 'domcontentloaded' })\n      await page.waitForTimeout(1500)\n    } catch {\n      return []\n    }\n    await this.throwIfCaptcha(page, 'Google short video search')\n\n    const svSels = {\n      item:      ShortVideoSelectors.item,\n      platforms: [...ShortVideoSelectors.platforms] as string[],\n    }\n\n    const raw = await page.evaluate((sels) => {\n      const seen = new Set<string>()\n      const results: Array<{ title: string; channel: string; platform: string; duration: string; url: string }> = []\n      const items = Array.from(document.querySelectorAll(sels.item))\n\n      const videoHosts = ['youtube.com', 'youtu.be', 'tiktok.com', 'instagram.com', 'facebook.com', 'fb.watch']\n\n      const byHref = new Map<string, string[]>()\n      for (const a of items) {\n        const href = (a as HTMLAnchorElement).href\n        if (!href) continue\n        if (!videoHosts.some((h) => href.includes(h))) continue\n        const text = a.textContent?.trim() ?? ''\n        if (!byHref.has(href)) byHref.set(href, [])\n        byHref.get(href)!.push(text)\n      }\n\n      for (const [href, texts] of byHref.entries()) {\n        if (seen.has(href)) continue\n        seen.add(href)\n\n        const duration = texts.find((t) => /^\\d+:\\d+$/.test(t)) ?? ''\n        const titleText = texts.find((t) => !/^\\d+:\\d+$/.test(t) && t.length > 5) ?? ''\n        if (!titleText) continue\n\n        let title = titleText\n        let platform = ''\n        let channel = ''\n\n        for (const p of sels.platforms) {\n          let lastIdx = -1\n          let search = 0\n          while (true) {\n            const found = titleText.indexOf(p, search)\n            if (found === -1) break\n            lastIdx = found\n            search = found + 1\n          }\n          if (lastIdx === -1) continue\n          const after = titleText.slice(lastIdx + p.length)\n          const isSourceTag = /^[\\s·]/.test(after) || after.trim() === ''\n          if (!isSourceTag) continue\n          title = titleText.slice(0, lastIdx).trim()\n          platform = p\n          const stripped = after.replace(/^[\\s·]+/, '')\n          const dotIdx = stripped.indexOf('·')\n          channel = (dotIdx === -1 ? stripped : stripped.slice(0, dotIdx)).trim()\n          break\n        }\n\n        if (title) results.push({ title, channel, platform, duration, url: href })\n      }\n\n      return results\n    }, svSels)\n\n    return raw.map((r) => ({ type: 'short_video' as const, ...r }))\n  }\n\n  private async extractWhatPeopleSaying(page: Page): Promise<WhatPeopleSayingCard[]> {\n    const sels = WhatPeopleSayingSelectors\n    return page.evaluate((s) => {\n      const section = Array.from(document.querySelectorAll(s.sectionTag))\n        .find((el) => el.textContent?.includes(s.sectionHeadingText))\n        ?? document.querySelector('.yG4QQe.TBC9ub.NbhJ1c')\n\n      if (!section) return []\n\n      return Array.from(section.querySelectorAll(s.card)).map((card) => {\n        const link = card.querySelector(s.cardLink) as HTMLAnchorElement | null\n        const url = link?.href ?? ''\n\n        const titleH1 = card.querySelector(s.titleH1)?.textContent?.trim()\n        const titleDiv = card.querySelector(s.titleDiv)?.textContent?.trim()\n        const title = titleH1 ?? titleDiv ?? ''\n\n        const sourceText = card.querySelector(s.source)?.textContent?.trim() ?? ''\n        const platformEl = card.querySelector(s.platformBadge)\n        const platformText = platformEl?.textContent?.trim() ?? ''\n\n        const ytChannel = card.querySelector(s.ytChannel)?.textContent?.trim() ?? ''\n        const ytDate    = card.querySelector(s.ytDate)?.textContent?.trim() ?? ''\n        const authorNote = card.querySelector(s.authorNote)?.textContent?.trim() ?? null\n\n        const commentLabelEl = card.querySelector(s.popularCommentLabel)\n        let popularComment: string | null = null\n        if (commentLabelEl) {\n          let next = commentLabelEl.nextSibling\n          while (next) {\n            const t = next.textContent?.trim()\n            if (t) { popularComment = t; break }\n            next = next.nextSibling\n          }\n        }\n\n        const allSpans = Array.from(card.querySelectorAll('span'))\n        const duration = allSpans.find((s) => /^\\d+:\\d+$/.test(s.textContent?.trim() ?? ''))?.textContent?.trim() ?? null\n        const engagementParts = allSpans\n          .map((s) => s.textContent?.trim() ?? '')\n          .filter((t) =>\n            /\\d/.test(t) && (\n              t.includes('comment') || t.includes('reaction') ||\n              t.includes('view') || t.includes('like') || t.includes('share')\n            )\n          )\n        const engagement = engagementParts[0] ?? ''\n\n        const dateCandidates = allSpans\n          .map((s) => s.textContent?.trim() ?? '')\n          .filter((t) => /\\d+ (day|week|month|year|hour)s? ago|Jan|Feb|Mar|Apr|May|Jun|Jul|Aug|Sep|Oct|Nov|Dec/.test(t))\n        const date = ytDate || (dateCandidates[0] ?? '')\n\n        const platform = platformText || (ytChannel ? 'YouTube' : '')\n        const source = ytChannel || sourceText\n\n        let type: 'reddit' | 'facebook' | 'youtube' | 'instagram' | 'tiktok' | 'news' | 'unknown' = 'unknown'\n        const pl = platform.toLowerCase()\n        const src = source.toLowerCase()\n        const srcRaw = sourceText.toLowerCase()\n        if (pl.includes('reddit') || src.startsWith('r/')) type = 'reddit'\n        else if (pl.includes('facebook') || srcRaw.includes('facebook')) type = 'facebook'\n        else if (pl.includes('instagram') || srcRaw.includes('instagram')) type = 'instagram'\n        else if (pl.includes('tiktok') || srcRaw.includes('tiktok')) type = 'tiktok'\n        else if (pl.includes('youtube') || !!ytChannel) type = 'youtube'\n        else type = 'news'\n\n        return { type, title, url, source, platform, popularComment, engagement, date, duration, authorNote }\n      })\n    }, sels)\n  }\n\n  private async extractOrganicResults(page: Page): Promise<OrganicResult[]> {\n    const sels = OrganicSelectors\n    return page.evaluate((s) => {\n      const out: Array<{ position: number; title: string; url: string; domain: string; cite: string | null; snippet: string | null; isRedditStyle: boolean; inlineRating: { value: string; count: string } | null }> = []\n      let pos = 0\n      document.querySelectorAll(s.result).forEach(card => {\n        const titleEl = card.querySelector(s.title)\n        if (!titleEl) return\n        const title = titleEl.textContent?.trim() ?? ''\n        const linkEl = titleEl.closest('a') as HTMLAnchorElement | null\n        const url = linkEl?.href ?? ''\n        if (!title || !url) return\n        pos++\n        const cite     = card.querySelector(s.cite)?.textContent?.trim() ?? null\n        const snippet  = card.querySelector(s.snippet)?.textContent?.trim() ?? null\n        const isRedditStyle = !!card.querySelector(s.redditCite)\n        const ratingEl = card.querySelector(s.ratingWrap)\n        const inlineRating = ratingEl\n          ? { value: ratingEl.querySelector(s.ratingValue)?.textContent?.trim() ?? '', count: ratingEl.querySelector(s.reviewCount)?.textContent?.trim() ?? '' }\n          : null\n        let domain = ''\n        try { domain = new URL(url).hostname.replace(/^www\\./, '') } catch { domain = card.querySelector(s.siteName)?.textContent?.trim() ?? '' }\n        out.push({ position: pos, title, url, domain, cite, snippet, isRedditStyle, inlineRating })\n      })\n      return out\n    }, sels)\n  }\n\n  private async extractLocalPack(page: Page): Promise<LocalPackBusiness[]> {\n    const sels = LocalPackSelectors\n    return page.evaluate((s) => {\n      const out: Array<{ position: number; name: string; cid: string | null; rating: string | null; reviewCount: string | null; metadata: string[]; websiteUrl: string | null; directionsUrl: string | null }> = []\n      let container: Element | null = null\n      document.querySelectorAll('[role=\"heading\"]').forEach(h => {\n        if (!container && h.textContent?.includes(s.headingText)) container = h.closest('[data-hveid]')\n      })\n      if (!container) return out\n      ;(container as Element).querySelectorAll(s.card).forEach((card, i) => {\n        const name = card.querySelector(s.name)?.textContent?.trim() ?? ''\n        if (!name) return\n        const rating     = card.querySelector(s.ratingValue)?.textContent?.trim() ?? null\n        const reviewRaw  = card.querySelector(s.reviewCount)?.textContent?.trim() ?? null\n        const reviewCount = reviewRaw ? reviewRaw.replace(/[()]/g, '').trim() : null\n        let cid = (card.querySelector('a[data-cid]') as HTMLAnchorElement | null)?.getAttribute('data-cid') ?? null\n        if (!cid) {\n          for (const link of Array.from(card.querySelectorAll('a[href]')) as HTMLAnchorElement[]) {\n            const m1 = link.href.match(/[?&]cid=(\\d+)/)\n            if (m1) { cid = m1[1]; break }\n            const m2 = link.href.match(/!1s0x[0-9a-f]+:0x([0-9a-f]+)/i)\n            if (m2) { try { cid = BigInt('0x' + m2[1]).toString() } catch { } if (cid) break }\n          }\n        }\n        const metadata: string[] = []\n        card.querySelectorAll('div, span').forEach(el => {\n          const text = Array.from(el.childNodes)\n            .filter(n => n.nodeType === 3)\n            .map(n => n.textContent?.trim() ?? '')\n            .filter(t => t.length > 1 && t.length < 120)\n            .join(' ')\n          if (text && !metadata.includes(text)) metadata.push(text)\n        })\n        const links = Array.from(card.querySelectorAll('a[href]')) as HTMLAnchorElement[]\n        const directionsUrl = links.find(a => a.href.includes('google.com/maps'))?.href ?? null\n        const websiteUrl    = links.find(a => !a.href.includes('google.com') && a.href.startsWith('http'))?.href ?? null\n        out.push({ position: i + 1, name, cid, rating, reviewCount, metadata, websiteUrl, directionsUrl })\n      })\n      return out\n    }, sels)\n  }\n\n  private async extractEntityIds(page: Page): Promise<EntityIds> {\n    return page.evaluate(() => {\n      const kgIds = new Set<string>()\n      const cids  = new Set<string>()\n      const gcids = new Set<string>()\n      const recordMap = new Map<string, { name: string; kgId: string | null; cid: string | null; gcid: string | null }>()\n\n      function nameFromWrapper(el: Element): string {\n        const sel = ['.OSrXXb', '.dbg0pd', '.tzt0oe', '[role=\"heading\"]', 'h3']\n        for (const s of sel) {\n          const found = el.querySelector(s)\n          if (found?.textContent?.trim()) return found.textContent.trim()\n        }\n        return ''\n      }\n\n      document.querySelectorAll('[id^=\"pv-/g/\"]').forEach(wrapper => {\n        const raw = wrapper.getAttribute('id')\n        if (!raw) return\n        const kgId = raw.replace('pv-', '')\n        kgIds.add(kgId)\n        const name = nameFromWrapper(wrapper)\n        const cidEl = wrapper.querySelector('a[data-cid]')\n        const cid = cidEl?.getAttribute('data-cid') ?? null\n        if (cid) cids.add(cid)\n        if (name) recordMap.set(kgId, { name, kgId, cid, gcid: null })\n      })\n\n      document.querySelectorAll('[data-mid]').forEach(el => {\n        const mid = el.getAttribute('data-mid')\n        if (!mid?.startsWith('/g/')) return\n        kgIds.add(mid)\n        if (!recordMap.has(mid)) {\n          const name = nameFromWrapper(el)\n          if (name) recordMap.set(mid, { name, kgId: mid, cid: null, gcid: null })\n        }\n      })\n\n      document.querySelectorAll('.w7Dbne').forEach(card => {\n        const cidEl = card.querySelector('a[data-cid]')\n        const cid = cidEl?.getAttribute('data-cid') ?? null\n        if (!cid) return\n        cids.add(cid)\n        const name = card.querySelector('.OSrXXb')?.textContent?.trim() ?? ''\n        if (!name) return\n        const kgIdEl = card.querySelector('[id^=\"pv-/g/\"]')\n        const kgId = kgIdEl ? kgIdEl.getAttribute('id')!.replace('pv-', '') : null\n        const key = kgId ?? `cid:${cid}`\n        if (recordMap.has(key)) {\n          const existing = recordMap.get(key)!\n          if (!existing.cid) recordMap.set(key, { ...existing, cid })\n        } else {\n          recordMap.set(key, { name, kgId, cid, gcid: null })\n        }\n      })\n\n      document.querySelectorAll('a[data-cid]').forEach(el => {\n        const cid = el.getAttribute('data-cid')\n        if (!cid) return\n        cids.add(cid)\n        const alreadyNamed = [...recordMap.values()].some(r => r.cid === cid)\n        if (!alreadyNamed) {\n          let node: Element | null = el.parentElement\n          let name = ''\n          for (let i = 0; i < 8 && node; i++) {\n            const h = node.querySelector('.OSrXXb, .dbg0pd, [role=\"heading\"], h3')\n            if (h?.textContent?.trim()) { name = h.textContent.trim(); break }\n            node = node.parentElement\n          }\n          if (name) recordMap.set(`cid:${cid}`, { name, kgId: null, cid, gcid: null })\n        }\n      })\n\n      const scriptContent = Array.from(document.querySelectorAll('script:not([src])'))\n        .map(s => s.textContent ?? '')\n        .filter(t => t.length > 10_000)\n        .join('\\n')\n\n      for (const m of scriptContent.matchAll(/\\/g\\/[a-zA-Z0-9_-]{5,20}/g)) kgIds.add(m[0])\n      for (const m of scriptContent.matchAll(/gcid:[a-zA-Z0-9_]+/g))        gcids.add(m[0])\n      for (const m of scriptContent.matchAll(/0x[0-9a-f]+:0x([0-9a-f]+)/gi)) {\n        try { cids.add(BigInt('0x' + m[1]).toString()) } catch { }\n      }\n\n      return { entities: [...recordMap.values()], kgIds: [...kgIds], cids: [...cids], gcids: [...gcids] }\n    })\n  }\n\n  private mergeLocalPackIntoEntities(entityIds: EntityIds, localPack: LocalPackBusiness[]): EntityIds {\n    const cidSet = new Set<string>(entityIds.cids)\n    const records = entityIds.entities.map(r => ({ ...r }))\n\n    for (const biz of localPack) {\n      if (!biz.cid) continue\n      cidSet.add(biz.cid)\n      const nameNorm = biz.name.toLowerCase().trim()\n      const byName = records.find(r => r.name.toLowerCase().trim() === nameNorm)\n      if (byName) {\n        if (!byName.cid) byName.cid = biz.cid\n      } else if (!records.find(r => r.cid === biz.cid)) {\n        records.push({ name: biz.name, kgId: null, cid: biz.cid, gcid: null })\n      }\n    }\n\n    return { ...entityIds, entities: records, cids: [...cidSet] }\n  }\n\n  private async extractAISurfaces(page: Page, options?: HarvestOptions): Promise<{\n    surface: GoogleSurface\n    aiOverview: AIOverviewResult\n    aiMode: AIModeResult\n  }> {\n    const surfaces = await page.evaluate(extractAISurfacesFromDocument, {\n      aio: AIOverviewSelectors,\n      aim: AIModeSelectors,\n      expandWaitMs: 1500,\n    })\n\n    if (!surfaces.aiOverview.detected) {\n      this.completeness.aioShareCaptured = null\n      return surfaces\n    }\n\n    const shareUrl = this.aioShareUrlEarly\n      ?? await this.captureAIOverviewShareUrl(page, options).catch(() => null)\n    this.completeness.aioShareCaptured = shareUrl !== null\n    return {\n      ...surfaces,\n      aiOverview: { ...surfaces.aiOverview, shareUrl },\n    }\n  }\n\n  private async captureAIOverviewShareUrl(page: Page, options?: HarvestOptions): Promise<string | null> {\n    if (options?.softDeadlineMs && Date.now() >= options.softDeadlineMs) return null\n\n    const clicked = await page.evaluate(() => {\n      const heads = Array.from(document.querySelectorAll('h1,h2,h3,[role=\"heading\"],div'))\n        .filter(h => (h.textContent || '').trim() === 'AI Overview')\n      for (const head of heads) {\n        let sec: HTMLElement | null = head as HTMLElement\n        for (let up = 0; up < 8 && sec; up++) {\n          const btn = sec.querySelector('[aria-label=\"Share\"]') as HTMLElement | null\n          if (btn) { btn.scrollIntoView({ block: 'center' }); btn.click(); return true }\n          sec = sec.parentElement\n        }\n      }\n      return false\n    }).catch(() => false)\n\n    if (!clicked) return null\n\n    let url: string | null = null\n    try {\n      const found = await page.waitForFunction(() => {\n        const w = document.createTreeWalker(document.body, NodeFilter.SHOW_TEXT)\n        while (w.nextNode()) {\n          const m = (w.currentNode.textContent || '').match(/https:\\/\\/share\\.google\\/aimode\\/\\S+/)\n          if (m) return m[0]\n        }\n        return false\n      }, undefined, { timeout: 4500, polling: 250 })\n      url = (await found.jsonValue()) as string\n    } catch {\n      url = null\n    }\n\n    await page.keyboard.press('Escape').catch(() => {})\n    return url\n  }\n\n  private buildTree(flat: FlatRow[], _seed: string): PAANode[] {\n    const roots: PAANode[] = []\n    const nodeMap = new Map<string, PAANode>()\n\n    for (const row of flat) {\n      const node: PAANode = {\n        question:       row.question,\n        answer:         row.answer || null,\n        sourceTitle:    row.source_title || null,\n        sourceSite:     row.source_site || null,\n        sourceCite:     row.source_cite || null,\n        depth:          row.depth,\n        parentQuestion: row.parent_question || null,\n        children:       [],\n      }\n      nodeMap.set(row.question, node)\n    }\n\n    for (const node of nodeMap.values()) {\n      if (node.parentQuestion && nodeMap.has(node.parentQuestion)) {\n        nodeMap.get(node.parentQuestion)!.children.push(node)\n      } else {\n        roots.push(node)\n      }\n    }\n\n    return roots\n  }\n\n  private getBrowserDebugSnapshot(): BrowserDebugSnapshot {\n    return this.driver.getDebugSnapshot()\n  }\n\n  private buildHarvestDebugSnapshot(\n    options: HarvestOptions,\n    canonicalLocation: string | null,\n    uule: string | null,\n    locationEvidence?: SerpLocationEvidence,\n  ): HarvestDebugSnapshot | undefined {\n    if (!options.debug) return undefined\n    return {\n      enabled: true,\n      request: {\n        query: options.query,\n        locationInput: options.location ?? null,\n        canonicalLocation,\n        uule,\n        gl: options.gl,\n        hl: options.hl,\n        device: options.device,\n        proxyMode: options.proxyMode,\n        proxyZip: options.proxyZip ?? null,\n        serpOnly: options.serpOnly,\n        pages: options.pages ?? 1,\n      },\n      browser: this.getBrowserDebugSnapshot(),\n      ...(locationEvidence ? { locationEvidence } : {}),\n    }\n  }\n\n  async extract(options: HarvestOptions, signal?: AbortSignal): Promise<HarvestResult> {\n    const startMs = Date.now()\n    this.completeness = { paaWithoutAnswer: 0, paaWithoutSource: 0, paaAnswersRecovered: 0, aioShareCaptured: null }\n    this.aioShareUrlEarly = null\n    this.collectedRows = []\n    this.partialContext = { seed: options.query, location: options.location ?? null, startMs }\n    const isMobile = options.device === 'mobile'\n    const config: DriverConfig = {\n      headless:      options.headless,\n      profileDir:    options.profileDir,\n      proxy:         options.proxy,\n      kernelApiKey:  options.kernelApiKey,\n      kernelProxyId: options.kernelProxyId,\n      kernelProxyResolution: options.kernelProxyResolution as DriverConfig['kernelProxyResolution'],\n      proxyMode:     options.proxyMode,\n      keepDefaultProxy: options.keepDefaultProxy,\n      requireUsEgress: options.requireUsEgress,\n      viewport:      isMobile ? { width: 390, height: 844 } : { width: 1280, height: 800 },\n      locale:        `${options.hl}-${options.gl.toUpperCase()}`,\n      deviceScaleFactor: isMobile ? 3 : 1,\n      isMobile,\n      hasTouch: isMobile,\n      debug:         options.debug,\n    }\n\n    let errorCount = 0\n    const diagnosticWarnings: HarvestDiagnosticWarning[] = []\n\n    try {\n      this.throwIfAborted(signal)\n      await this.driver.launch(config)\n      this.throwIfAborted(signal)\n      const canonicalLocation = options.location ? normalizeLocation(options.location) : null\n      const uule = canonicalLocation ? encodeUule(canonicalLocation) : null\n      const executedQuery = buildLocalizedSerpQuery(options.query, options.location, canonicalLocation)\n      const executionOptions = executedQuery === options.query ? options : { ...options, query: executedQuery }\n      const { hasPaa } = await this.driver.navigateToSERP(\n        executionOptions.query,\n        uule,\n        options.gl,\n        options.hl,\n        {\n          ...(options.serpOnly ? { num: 100 } : {}),\n          ...(recencyToTbs(options.recency) ? { tbs: recencyToTbs(options.recency) } : {}),\n          debug: options.debug,\n        },\n      )\n      this.throwIfAborted(signal)\n\n      const page = this.driver.getPage() as Page\n      await this.throwIfCaptcha(page, 'Google SERP')\n\n      this.aioShareUrlEarly = await this.captureAIOverviewShareUrl(page, options).catch(() => null)\n\n      if (options.serpOnly) {\n        const [organicResults, localPack, rawEntityIds] = await Promise.all([\n          this.extractOrganicResults(page),\n          this.extractLocalPack(page),\n          this.extractEntityIds(page),\n        ])\n        const entityIds = this.mergeLocalPackIntoEntities(rawEntityIds, localPack)\n        const aiSurfaces = await this.extractAISurfaces(page, options)\n        let locationEvidence = options.debug\n          ? inferSerpLocationEvidence(canonicalLocation, organicResults, localPack)\n          : undefined\n        let allOrganic = organicResults\n        if ((options.pages ?? 1) >= 2) {\n          const p2params = new URLSearchParams({ q: executionOptions.query, gl: options.gl, hl: options.hl, pws: '0', start: '10' })\n          if (recencyToTbs(options.recency)) p2params.set('tbs', recencyToTbs(options.recency) as string)\n          if (uule) p2params.set('uule', uule)\n          await this.driver.navigateTo('https://www.google.com/search?' + p2params.toString())\n          await this.throwIfCaptcha(page, 'Google SERP page 2')\n          const p2organic = await this.extractOrganicResults(page)\n          allOrganic = [...organicResults, ...p2organic.map(r => ({ ...r, position: r.position + 10 }))]\n          if (options.debug) {\n            locationEvidence = inferSerpLocationEvidence(canonicalLocation, allOrganic, localPack)\n          }\n        }\n        const stats: HarvestStats = {\n          seed: executionOptions.query, totalQuestions: 0, maxDepthReached: 0,\n          durationMs: Date.now() - startMs, errorCount,\n        }\n        this.reporter.onComplete(stats)\n        return {\n          seed: executionOptions.query, location: options.location ?? null,\n          extractedAt: new Date().toISOString(),\n          diagnostics: {\n            completionStatus: 'serp_only',\n            problem: null,\n            completeness: { ...this.completeness },\n            ...(options.debug ? { debug: this.buildHarvestDebugSnapshot(executionOptions, canonicalLocation, uule, locationEvidence) } : {}),\n          },\n          totalQuestions: 0,\n          surface: aiSurfaces.surface,\n          aiOverview: aiSurfaces.aiOverview,\n          aiMode:     aiSurfaces.aiMode,\n          whatPeopleSaying: [], tree: [], flat: [], videos: [], forums: [],\n          organicResults: allOrganic, localPack, entityIds, stats,\n        }\n      }\n\n      const [videos, forums, whatPeopleSaying, rawEntityIds, organicResults, localPack] = await Promise.all([\n        this.extractVideos(page),\n        this.extractForums(page),\n        this.extractWhatPeopleSaying(page),\n        this.extractEntityIds(page),\n        this.extractOrganicResults(page),\n        this.extractLocalPack(page),\n      ])\n      const entityIds = this.mergeLocalPackIntoEntities(rawEntityIds, localPack)\n      const initialLocationEvidence = options.debug\n        ? inferSerpLocationEvidence(canonicalLocation, organicResults, localPack)\n        : undefined\n      this.reporter.onVideos(videos)\n      this.reporter.onForums(forums)\n\n      if (!hasPaa) {\n        let noPaaOrganic = organicResults\n        let locationEvidence = initialLocationEvidence\n        if ((options.pages ?? 1) >= 2) {\n          const p2params = new URLSearchParams({ q: executionOptions.query, gl: options.gl, hl: options.hl, pws: '0', start: '10' })\n          if (recencyToTbs(options.recency)) p2params.set('tbs', recencyToTbs(options.recency) as string)\n          if (uule) p2params.set('uule', uule)\n          await this.driver.navigateTo('https://www.google.com/search?' + p2params.toString())\n          await this.throwIfCaptcha(page, 'Google SERP page 2')\n          const p2organic = await this.extractOrganicResults(page)\n          noPaaOrganic = [...organicResults, ...p2organic.map(r => ({ ...r, position: r.position + 10 }))]\n          if (options.debug) {\n            locationEvidence = inferSerpLocationEvidence(canonicalLocation, noPaaOrganic, localPack)\n          }\n        }\n        const aiSurfaces = await this.extractAISurfaces(page, options)\n        const stats: HarvestStats = {\n          seed: executionOptions.query, totalQuestions: 0, maxDepthReached: 0,\n          durationMs: Date.now() - startMs, errorCount,\n        }\n        this.reporter.onComplete(stats)\n        return {\n          seed: executionOptions.query, location: options.location ?? null,\n          extractedAt: new Date().toISOString(),\n          diagnostics: {\n            completionStatus: 'no_paa',\n            problem: null,\n            completeness: { ...this.completeness },\n            ...(options.debug ? { debug: this.buildHarvestDebugSnapshot(executionOptions, canonicalLocation, uule, locationEvidence) } : {}),\n          },\n          totalQuestions: 0,\n          surface: aiSurfaces.surface,\n          aiOverview: aiSurfaces.aiOverview,\n          aiMode: aiSurfaces.aiMode,\n          whatPeopleSaying, tree: [], flat: [], videos, forums,\n          organicResults: noPaaOrganic, localPack, entityIds, stats,\n        }\n      }\n\n      const flat = await this.runBFS(page, executionOptions, signal)\n      this.throwIfAborted(signal)\n      const aiSurfaces = await this.extractAISurfaces(page, options)\n\n      const shortVidsParams = new URLSearchParams({ q: executionOptions.query, gl: options.gl, hl: options.hl, pws: '0', udm: ShortVideoSelectors.udm })\n      if (uule) shortVidsParams.set('uule', uule)\n      let shortVideos: VideoResult[] = []\n      try {\n        shortVideos = await this.extractShortVideos(page, 'https://www.google.com/search?' + shortVidsParams.toString())\n      } catch (err) {\n        if (!(err instanceof CaptchaError)) throw err\n        errorCount++\n        diagnosticWarnings.push({\n          code: 'short_videos_captcha_skipped',\n          surface: 'short_videos',\n          message: err.message,\n          retryable: true,\n        })\n      }\n      this.reporter.onVideos(shortVideos)\n\n      let allOrganic = organicResults\n      let locationEvidence = initialLocationEvidence\n      if ((options.pages ?? 1) >= 2) {\n        const p2params = new URLSearchParams({ q: executionOptions.query, gl: options.gl, hl: options.hl, pws: '0', start: '10' })\n        if (recencyToTbs(options.recency)) p2params.set('tbs', recencyToTbs(options.recency) as string)\n        if (uule) p2params.set('uule', uule)\n        await this.driver.navigateTo('https://www.google.com/search?' + p2params.toString())\n        await this.throwIfCaptcha(page, 'Google SERP page 2')\n        const p2organic = await this.extractOrganicResults(page)\n        allOrganic = [...organicResults, ...p2organic.map(r => ({ ...r, position: r.position + 10 }))]\n        if (options.debug) {\n          locationEvidence = inferSerpLocationEvidence(canonicalLocation, allOrganic, localPack)\n        }\n      }\n\n      const allVideos = [...videos, ...shortVideos]\n      const tree = this.buildTree(flat, executionOptions.query)\n\n      const stats: HarvestStats = {\n        seed:            executionOptions.query,\n        totalQuestions:  flat.length,\n        maxDepthReached: flat.reduce((m, r) => Math.max(m, r.depth), 0),\n        durationMs:      Date.now() - startMs,\n        errorCount,\n      }\n\n      this.reporter.onComplete(stats)\n\n      return {\n        seed:           executionOptions.query,\n        location:       options.location ?? null,\n        extractedAt:    new Date().toISOString(),\n        diagnostics:    {\n          completionStatus: 'paa_found',\n          problem: null,\n          completeness: { ...this.completeness },\n          ...(diagnosticWarnings.length > 0 ? { warnings: diagnosticWarnings } : {}),\n          ...(options.debug ? { debug: this.buildHarvestDebugSnapshot(executionOptions, canonicalLocation, uule, locationEvidence) } : {}),\n        },\n        totalQuestions: flat.length,\n        surface:           aiSurfaces.surface,\n        aiOverview:        aiSurfaces.aiOverview,\n        aiMode:            aiSurfaces.aiMode,\n        whatPeopleSaying,\n        tree,\n        flat,\n        videos:  allVideos,\n        forums,\n        organicResults: allOrganic,\n        localPack,\n        entityIds,\n        stats,\n      }\n    } catch (err) {\n      errorCount++\n      this.reporter.onError(err instanceof Error ? err : new Error(String(err)))\n      throw err\n    }\n  }\n}\n","import { promises as fs } from 'node:fs'\nimport path from 'node:path'\nimport Papa from 'papaparse'\nimport type { HarvestResult, FlatRow, VideoResult, ForumResult, AICitation, WhatPeopleSayingCard } from '../types.js'\nimport type { IOutputSerializer } from './IOutputSerializer.js'\nimport { postToMemoryLibrary } from './memory-library-sink.js'\n\nexport class OutputSerializer implements IOutputSerializer {\n  async writeJSON(result: HarvestResult, outputDir: string): Promise<string> {\n    await fs.mkdir(outputDir, { recursive: true })\n    const slug = result.seed.toLowerCase().replace(/\\W+/g, '-').slice(0, 40)\n    const filename = `${slug}-${Date.now()}.json`\n    const fullPath = path.join(outputDir, filename)\n    await fs.writeFile(fullPath, JSON.stringify(result, null, 2), 'utf8')\n    await postToMemoryLibrary({ title: `${result.seed} scrape`, content: JSON.stringify(result), source: `mcp-scraper:${fullPath}` })\n    return fullPath\n  }\n\n  async writeCSV(rows: FlatRow[], outputDir: string): Promise<string> {\n    await fs.mkdir(outputDir, { recursive: true })\n    const seedRaw = rows[0]?.seed_query ?? 'paa'\n    const slug = seedRaw.toLowerCase().replace(/\\W+/g, '-').slice(0, 40)\n    const csv = Papa.unparse(rows, { header: true })\n    const filename = `${slug}-${Date.now()}.csv`\n    const fullPath = path.join(outputDir, filename)\n    await fs.writeFile(fullPath, csv, 'utf8')\n    return fullPath\n  }\n\n  async writeVideoCSV(videos: VideoResult[], seed: string, outputDir: string): Promise<string> {\n    await fs.mkdir(outputDir, { recursive: true })\n    const slug = seed.toLowerCase().replace(/\\W+/g, '-').slice(0, 40)\n    const csv = Papa.unparse(videos, { header: true })\n    const filename = `${slug}-videos-${Date.now()}.csv`\n    const fullPath = path.join(outputDir, filename)\n    await fs.writeFile(fullPath, csv, 'utf8')\n    return fullPath\n  }\n\n  async writeForumCSV(forums: ForumResult[], seed: string, outputDir: string): Promise<string> {\n    await fs.mkdir(outputDir, { recursive: true })\n    const slug = seed.toLowerCase().replace(/\\W+/g, '-').slice(0, 40)\n    const csv = Papa.unparse(forums, { header: true })\n    const filename = `${slug}-forums-${Date.now()}.csv`\n    const fullPath = path.join(outputDir, filename)\n    await fs.writeFile(fullPath, csv, 'utf8')\n    return fullPath\n  }\n\n  async writeAIOverviewCSV(citations: AICitation[], text: string | null, seed: string, outputDir: string): Promise<string> {\n    await fs.mkdir(outputDir, { recursive: true })\n    const slug = seed.toLowerCase().replace(/\\W+/g, '-').slice(0, 40)\n    const rows = citations.map((c, i) => ({\n      seed_query:    seed,\n      response_text: i === 0 ? (text ?? '') : '',\n      citation_text: c.text,\n      citation_href: c.href,\n    }))\n    const csv = Papa.unparse(rows, { header: true })\n    const filename = `${slug}-ai-overview-${Date.now()}.csv`\n    const fullPath = path.join(outputDir, filename)\n    await fs.writeFile(fullPath, csv, 'utf8')\n    return fullPath\n  }\n\n  async writeAIModeCSV(citations: AICitation[], text: string | null, seed: string, outputDir: string): Promise<string> {\n    await fs.mkdir(outputDir, { recursive: true })\n    const slug = seed.toLowerCase().replace(/\\W+/g, '-').slice(0, 40)\n    const rows = citations.map((c, i) => ({\n      seed_query:    seed,\n      response_text: i === 0 ? (text ?? '') : '',\n      citation_text: c.text,\n      citation_href: c.href,\n    }))\n    const csv = Papa.unparse(rows, { header: true })\n    const filename = `${slug}-ai-mode-${Date.now()}.csv`\n    const fullPath = path.join(outputDir, filename)\n    await fs.writeFile(fullPath, csv, 'utf8')\n    return fullPath\n  }\n\n  async writeWhatPeopleSayingCSV(cards: WhatPeopleSayingCard[], seed: string, outputDir: string): Promise<string> {\n    await fs.mkdir(outputDir, { recursive: true })\n    const slug = seed.toLowerCase().replace(/\\W+/g, '-').slice(0, 40)\n    const rows = cards.map((c) => ({ seed_query: seed, ...c }))\n    const csv = Papa.unparse(rows, { header: true })\n    const filename = `${slug}-what-people-saying-${Date.now()}.csv`\n    const fullPath = path.join(outputDir, filename)\n    await fs.writeFile(fullPath, csv, 'utf8')\n    return fullPath\n  }\n}\n","export async function postToMemoryLibrary(args: { title: string; content: string; source: string }): Promise<void> {\n  const url = process.env.MCP_MEMORY_INGEST_URL\n  const key = process.env.MCP_MEMORY_KEY\n  if (!url || !key) return\n  try {\n    const res = await fetch(url, {\n      method: 'POST',\n      headers: { 'content-type': 'application/json' },\n      body: JSON.stringify({ data: { apiKey: key, title: args.title, content: args.content, source: args.source } }),\n    })\n    const body = (await res.json().catch(() => null)) as { result?: { error?: boolean; message?: string } } | null\n    if (!res.ok || body?.result?.error) {\n      console.warn('[memory-library-sink] ingest not accepted:', res.status, body?.result?.message ?? '')\n    }\n  } catch (err) {\n    console.warn('[memory-library-sink] ingest failed:', (err as Error)?.message)\n  }\n}\n\nexport function memoryLibrarySinkEnabled(): boolean {\n  return Boolean(process.env.MCP_MEMORY_INGEST_URL && process.env.MCP_MEMORY_KEY)\n}\n","import type { PAANode, HarvestStats, VideoResult, ForumResult } from '../types.js'\nimport type { IProgressReporter } from './IProgressReporter.js'\n\nexport class ProgressReporter implements IProgressReporter {\n  onQuestion(node: PAANode): void {\n    process.stdout.write(JSON.stringify({ event: 'question', depth: node.depth, question: node.question }) + '\\n')\n  }\n\n  onDepth(depth: number): void {\n    process.stdout.write(JSON.stringify({ event: 'depth', depth }) + '\\n')\n  }\n\n  onVideos(videos: VideoResult[]): void {\n    for (const v of videos) {\n      process.stdout.write(JSON.stringify({ event: 'video', type: v.type, platform: v.platform, duration: v.duration, title: v.title, channel: v.channel, url: v.url }) + '\\n')\n    }\n  }\n\n  onForums(forums: ForumResult[]): void {\n    for (const f of forums) {\n      process.stdout.write(JSON.stringify({ event: 'forum', title: f.title, source: f.source, url: f.url }) + '\\n')\n    }\n  }\n\n  onComplete(stats: HarvestStats): void {\n    process.stdout.write(JSON.stringify({ event: 'complete', ...stats }) + '\\n')\n  }\n\n  onError(err: Error): void {\n    process.stderr.write(JSON.stringify({ event: 'error', type: err.constructor.name, message: err.message }) + '\\n')\n  }\n}\n","import Kernel from '@onkernel/sdk'\nimport { backupProxyAvailable, createBackupProxyId, scrubBackupProxyText } from './backup-proxy.js'\nimport { normalizeLocation } from './uule.js'\nimport type { CanonicalLocationName, KernelLocationProxyTarget, KernelProxyResolutionDebug, ProxyMode } from './types.js'\n\ntype KernelProxyConfig = {\n  country?: string\n  state?: string\n  city?: string\n  zip?: string\n}\n\ntype KernelProxyListItem = {\n  id?: string\n  name?: string\n  type?: string\n  status?: string\n  config?: KernelProxyConfig\n}\n\ntype KernelProxyCreateResponse = {\n  id?: string\n  name?: string\n  type?: string\n  status?: string\n  config?: KernelProxyConfig\n}\n\nexport interface ResolveKernelProxyOptions {\n  kernelApiKey?: string\n  proxyMode: ProxyMode\n  configuredKernelProxyId?: string\n  location?: string\n  proxyZip?: string\n  gl: string\n  attemptIndex?: number\n  fresh?: boolean\n  backupOnExhaustion?: boolean\n}\n\nexport interface ResolveKernelProxyResult {\n  kernelProxyId?: string\n  resolution: KernelProxyResolutionDebug\n  disposableProxyId?: string\n}\n\nconst US_STATE_CODES: Record<string, string> = {\n  alabama: 'AL',\n  alaska: 'AK',\n  arizona: 'AZ',\n  arkansas: 'AR',\n  california: 'CA',\n  colorado: 'CO',\n  connecticut: 'CT',\n  delaware: 'DE',\n  florida: 'FL',\n  georgia: 'GA',\n  hawaii: 'HI',\n  idaho: 'ID',\n  illinois: 'IL',\n  indiana: 'IN',\n  iowa: 'IA',\n  kansas: 'KS',\n  kentucky: 'KY',\n  louisiana: 'LA',\n  maine: 'ME',\n  maryland: 'MD',\n  massachusetts: 'MA',\n  michigan: 'MI',\n  minnesota: 'MN',\n  mississippi: 'MS',\n  missouri: 'MO',\n  montana: 'MT',\n  nebraska: 'NE',\n  nevada: 'NV',\n  'new hampshire': 'NH',\n  'new jersey': 'NJ',\n  'new mexico': 'NM',\n  'new york': 'NY',\n  'north carolina': 'NC',\n  'north dakota': 'ND',\n  ohio: 'OH',\n  oklahoma: 'OK',\n  oregon: 'OR',\n  pennsylvania: 'PA',\n  'rhode island': 'RI',\n  'south carolina': 'SC',\n  'south dakota': 'SD',\n  tennessee: 'TN',\n  texas: 'TX',\n  utah: 'UT',\n  vermont: 'VT',\n  virginia: 'VA',\n  washington: 'WA',\n  'west virginia': 'WV',\n  wisconsin: 'WI',\n  wyoming: 'WY',\n}\n\nconst US_CITY_CENTER_ZIPS: Record<string, string> = {\n  'atlanta|GA': '30303',\n  'austin|TX': '78701',\n  'baltimore|MD': '21201',\n  'boston|MA': '02108',\n  'boulder|CO': '80302',\n  'charlotte|NC': '28202',\n  'chicago|IL': '60601',\n  'colorado_springs|CO': '80903',\n  'columbus|OH': '43215',\n  'dallas|TX': '75201',\n  'denver|CO': '80202',\n  'detroit|MI': '48226',\n  'fort_collins|CO': '80524',\n  'fort_worth|TX': '76102',\n  'houston|TX': '77002',\n  'indianapolis|IN': '46204',\n  'jacksonville|FL': '32202',\n  'las_vegas|NV': '89101',\n  'los_angeles|CA': '90012',\n  'louisville|KY': '40202',\n  'loveland|CO': '80537',\n  'memphis|TN': '38103',\n  'miami|FL': '33131',\n  'minneapolis|MN': '55401',\n  'nashville|TN': '37203',\n  'new_york|NY': '10001',\n  'orlando|FL': '32801',\n  'philadelphia|PA': '19103',\n  'phoenix|AZ': '85004',\n  'portland|OR': '97205',\n  'raleigh|NC': '27601',\n  'richmond|VA': '23219',\n  'sacramento|CA': '95814',\n  'salt_lake_city|UT': '84101',\n  'san_antonio|TX': '78205',\n  'san_diego|CA': '92101',\n  'san_francisco|CA': '94103',\n  'san_jose|CA': '95113',\n  'seattle|WA': '98101',\n}\n\nfunction proxyIdSuffix(proxyId: string | null | undefined): string | null {\n  return proxyId ? proxyId.slice(-6) : null\n}\n\nfunction resolution(\n  source: KernelProxyResolutionDebug['source'],\n  proxyMode: ProxyMode,\n  proxyId: string | undefined,\n  target: KernelLocationProxyTarget | null,\n  error: string | null,\n  disposable = false,\n): ResolveKernelProxyResult {\n  const proxyType = source === 'disabled' ? 'none'\n    : source === 'configured_fallback' ? 'configured'\n    : source === 'isp_created' ? 'isp'\n    : source === 'backup_custom' ? 'custom'\n    : source === 'location_created' || source === 'location_reused' ? 'residential'\n    : 'unknown'\n  return {\n    kernelProxyId: proxyId,\n    ...(disposable && proxyId ? { disposableProxyId: proxyId } : {}),\n    resolution: {\n      source,\n      proxyType,\n      proxyMode,\n      proxyIdPresent: Boolean(proxyId),\n      proxyIdSuffix: proxyIdSuffix(proxyId),\n      target,\n      error,\n      disposable,\n    },\n  }\n}\n\nfunction normalizeStateName(value: string): string {\n  return value.trim().toLowerCase().replace(/\\s+/g, ' ')\n}\n\nfunction normalizeCountryName(value: string): string {\n  return value.trim().toLowerCase().replace(/\\./g, '').replace(/\\s+/g, ' ')\n}\n\nfunction isUnitedStates(country: string | undefined): boolean {\n  if (!country) return true\n  const normalized = normalizeCountryName(country)\n  return normalized === 'united states' || normalized === 'united states of america' || normalized === 'usa' || normalized === 'us'\n}\n\nfunction stateCodeFor(region: string): string | null {\n  const trimmed = region.trim()\n  if (/^[A-Za-z]{2}$/.test(trimmed)) return trimmed.toUpperCase()\n  return US_STATE_CODES[normalizeStateName(trimmed)] ?? null\n}\n\nexport function kernelCityIdentifierCandidates(city: string): string[] {\n  const ascii = city\n    .normalize('NFKD')\n    .replace(/[^\\x00-\\x7F]/g, '')\n    .toLowerCase()\n  const words = ascii.split(/[^a-z0-9]+/).filter(Boolean)\n  const underscored = words.join('_')\n  const compact = words.join('')\n  return Array.from(new Set([underscored, compact].filter(Boolean)))\n}\n\nfunction proxyName(country: string, state: string, city?: string): string {\n  return city\n    ? `mcp-serp-residential-${country.toLowerCase()}-${state.toLowerCase()}-${city}`\n    : `mcp-serp-residential-${country.toLowerCase()}-${state.toLowerCase()}`\n}\n\nfunction freshProxyName(baseName: string, attemptIndex: number | undefined): string {\n  const stamp = `${Date.now()}-${attemptIndex ?? 0}-${Math.random().toString(36).slice(2, 8)}`\n  return `${baseName}-fresh-${stamp}`\n}\n\nfunction zipProxyName(zip: string): string {\n  return `mcp-serp-residential-us-zip-${zip}`\n}\n\nexport function parseKernelLocationProxyTarget(location: string | undefined, gl: string): KernelLocationProxyTarget | null {\n  if (!location || gl.toLowerCase() !== 'us') return null\n  const canonicalLocation = normalizeLocation(location)\n  let parts = canonicalLocation.split(',').map(part => part.trim()).filter(Boolean)\n  if (parts.length > 1 && isUnitedStates(parts[parts.length - 1])) {\n    parts = parts.slice(0, -1)\n  }\n\n  if (parts.length === 1) {\n    const stateOnly = stateCodeFor(parts[0])\n    if (!stateOnly) return null\n    return {\n      canonicalLocation: canonicalLocation as CanonicalLocationName,\n      level: 'state',\n      country: 'US',\n      state: stateOnly,\n      city: '',\n      cityCandidates: [],\n      proxyName: proxyName('US', stateOnly),\n      config: {\n        country: 'US',\n        state: stateOnly,\n      },\n    }\n  }\n\n  const [city = '', region = ''] = parts\n  if (!city || !region) return null\n  const state = stateCodeFor(region)\n  if (!state) return null\n  const cityCandidates = kernelCityIdentifierCandidates(city)\n  const primaryCity = cityCandidates[0]\n  if (!primaryCity) return null\n  return {\n    canonicalLocation: canonicalLocation as CanonicalLocationName,\n    level: 'city',\n    country: 'US',\n    state,\n    city: primaryCity,\n    cityCandidates,\n    proxyName: proxyName('US', state, primaryCity),\n    config: {\n      country: 'US',\n      state,\n      city: primaryCity,\n    },\n  }\n}\n\nfunction cityZipKey(target: KernelLocationProxyTarget): string {\n  return `${target.city}|${target.state}`\n}\n\nfunction knownZipFor(target: KernelLocationProxyTarget, explicitZip: string | undefined): string | null {\n  if (explicitZip && /^\\d{5}$/.test(explicitZip)) return explicitZip\n  return US_CITY_CENTER_ZIPS[cityZipKey(target)] ?? null\n}\n\nfunction zipTarget(target: KernelLocationProxyTarget, zip: string): KernelLocationProxyTarget {\n  return {\n    ...target,\n    level: 'zip',\n    zip,\n    proxyName: zipProxyName(zip),\n    config: {\n      country: target.country,\n      state: target.state,\n      zip,\n    },\n  }\n}\n\nfunction withProxyName(target: KernelLocationProxyTarget, name: string): KernelLocationProxyTarget {\n  return {\n    ...target,\n    proxyName: name,\n  }\n}\n\nfunction configMatches(config: KernelProxyConfig | undefined, target: KernelLocationProxyTarget, city?: string): boolean {\n  if (target.level === 'zip') {\n    return config?.country?.toUpperCase() === target.country && config?.zip === target.zip\n  }\n  return config?.country?.toUpperCase() === target.country &&\n    config?.state?.toUpperCase() === target.state &&\n    (city ? config?.city === city : !config?.city)\n}\n\nfunction findExistingTargetProxy(proxies: KernelProxyListItem[], target: KernelLocationProxyTarget): KernelProxyListItem | null {\n  return proxies.find(proxy => (\n    proxy.type === 'residential' &&\n    proxy.status !== 'unavailable' &&\n    Boolean(proxy.id) &&\n    (proxy.name === target.proxyName || configMatches(proxy.config, target, target.level === 'city' ? target.city : undefined))\n  )) ?? null\n}\n\nfunction findExistingProxy(proxies: KernelProxyListItem[], target: KernelLocationProxyTarget): KernelProxyListItem | null {\n  for (const city of target.cityCandidates) {\n    const name = proxyName(target.country, target.state, city)\n    const found = proxies.find(proxy => (\n      proxy.type === 'residential' &&\n      proxy.status !== 'unavailable' &&\n      Boolean(proxy.id) &&\n      (proxy.name === name || configMatches(proxy.config, target, city))\n    ))\n    if (found) return found\n  }\n  return null\n}\n\nfunction stateTarget(target: KernelLocationProxyTarget): KernelLocationProxyTarget {\n  return {\n    ...target,\n    level: 'state',\n    proxyName: proxyName(target.country, target.state),\n    config: {\n      country: target.country,\n      state: target.state,\n    },\n  }\n}\n\nfunction findExistingStateProxy(proxies: KernelProxyListItem[], target: KernelLocationProxyTarget): KernelProxyListItem | null {\n  const name = proxyName(target.country, target.state)\n  return proxies.find(proxy => (\n    proxy.type === 'residential' &&\n    proxy.status !== 'unavailable' &&\n    Boolean(proxy.id) &&\n    (proxy.name === name || configMatches(proxy.config, target))\n  )) ?? null\n}\n\nfunction escalatedTargetLevel(target: KernelLocationProxyTarget, attemptIndex: number): KernelLocationProxyTarget {\n  return stateTarget(target)\n}\n\nfunction errorText(err: unknown): string {\n  return err instanceof Error ? err.message : String(err)\n}\n\nfunction freshTargetCandidates(\n  target: KernelLocationProxyTarget,\n  explicitZip: string | undefined,\n  attemptIndex: number | undefined,\n): KernelLocationProxyTarget[] {\n  const out: KernelLocationProxyTarget[] = []\n  const zip = knownZipFor(target, explicitZip)\n  if (zip) {\n    const targetZip = zipTarget(target, zip)\n    out.push(withProxyName(targetZip, freshProxyName(targetZip.proxyName, attemptIndex)))\n  }\n\n  for (const city of target.cityCandidates) {\n    const cityTarget: KernelLocationProxyTarget = {\n      ...target,\n      level: 'city',\n      city,\n      proxyName: proxyName(target.country, target.state, city),\n      config: {\n        country: target.country,\n        state: target.state,\n        city,\n      },\n    }\n    out.push(withProxyName(cityTarget, freshProxyName(cityTarget.proxyName, attemptIndex)))\n  }\n\n  const fallbackTarget = stateTarget(target)\n  out.push(withProxyName(fallbackTarget, freshProxyName(fallbackTarget.proxyName, attemptIndex)))\n  return out\n}\n\nasync function settleCreateExhaustion(\n  options: ResolveKernelProxyOptions,\n  target: KernelLocationProxyTarget | null,\n  createErrors: string[],\n): Promise<ResolveKernelProxyResult> {\n  if (options.backupOnExhaustion && createErrors.length > 0 && options.kernelApiKey && backupProxyAvailable()) {\n    try {\n      const backupId = await createBackupProxyId(options.kernelApiKey, { attemptIndex: options.attemptIndex })\n      return resolution('backup_custom', options.proxyMode, backupId, target, null, true)\n    } catch (err) {\n      createErrors.push(`backup: ${scrubBackupProxyText(errorText(err))}`)\n    }\n  }\n  return resolution('configured_fallback', options.proxyMode, options.configuredKernelProxyId, target, createErrors.join(' | '))\n}\n\nexport async function resolveBackupKernelProxyId(\n  options: Pick<ResolveKernelProxyOptions, 'kernelApiKey' | 'proxyMode' | 'attemptIndex'>,\n): Promise<ResolveKernelProxyResult | null> {\n  if (!options.kernelApiKey || !backupProxyAvailable()) return null\n  try {\n    const backupId = await createBackupProxyId(options.kernelApiKey, { attemptIndex: options.attemptIndex })\n    return resolution('backup_custom', options.proxyMode, backupId, null, null, true)\n  } catch {\n    return null\n  }\n}\n\nasync function createFreshLocationProxy(\n  kernel: Kernel,\n  options: ResolveKernelProxyOptions,\n  target: KernelLocationProxyTarget,\n): Promise<ResolveKernelProxyResult> {\n  const createErrors: string[] = []\n\n  for (const candidate of freshTargetCandidates(target, options.proxyZip, options.attemptIndex)) {\n    try {\n      const created = await kernel.proxies.create({\n        type: 'residential',\n        name: candidate.proxyName,\n        config: candidate.level === 'zip'\n          ? { country: candidate.country, zip: candidate.zip }\n          : candidate.config,\n      }) as KernelProxyCreateResponse\n      if (created.id) {\n        return resolution('location_created', options.proxyMode, created.id, candidate, null, true)\n      }\n      createErrors.push(`${candidate.proxyName}: Kernel did not return a proxy id`)\n    } catch (err) {\n      createErrors.push(`${candidate.proxyName}: ${errorText(err)}`)\n    }\n  }\n\n  if (process.env.KERNEL_PROXY_TYPE !== 'residential') {\n    const ispTarget = stateTarget(target)\n    try {\n      const created = await kernel.proxies.create({\n        type: 'isp',\n        name: freshProxyName(`mcp-serp-isp-${ispTarget.country.toLowerCase()}-${(ispTarget.state ?? '').toLowerCase()}`, options.attemptIndex),\n        config: { country: ispTarget.country, ...(ispTarget.state ? { state: ispTarget.state } : {}) },\n      } as never) as KernelProxyCreateResponse\n      if (created.id) {\n        return resolution('isp_created', options.proxyMode, created.id, ispTarget, null, true)\n      }\n      createErrors.push('isp: Kernel did not return a proxy id')\n    } catch (err) {\n      createErrors.push(`isp: ${errorText(err)}`)\n    }\n  }\n\n  return settleCreateExhaustion(options, target, createErrors)\n}\n\nexport async function deleteKernelProxyId(kernelApiKey: string | undefined, proxyId: string | undefined): Promise<void> {\n  if (!kernelApiKey || !proxyId) return\n  const kernel = new Kernel({ apiKey: kernelApiKey })\n  await kernel.proxies.delete(proxyId)\n}\n\nexport async function createFreshUsIspProxy(kernelApiKey: string, attemptIndex = 0): Promise<string> {\n  const kernel = new Kernel({ apiKey: kernelApiKey })\n  const created = await kernel.proxies.create({\n    type: 'isp',\n    name: freshProxyName('mcp-serp-isp-us', attemptIndex),\n    config: { country: 'US' },\n  } as never) as KernelProxyCreateResponse\n  if (!created.id) throw new Error('Kernel did not return a US ISP proxy id')\n  return created.id\n}\n\nexport async function resolveKernelProxyId(options: ResolveKernelProxyOptions): Promise<ResolveKernelProxyResult> {\n  if (options.proxyMode === 'none') {\n    return resolution('disabled', options.proxyMode, undefined, null, null)\n  }\n\n  if (options.proxyMode === 'configured') {\n    return resolution('configured_fallback', options.proxyMode, options.configuredKernelProxyId, null, null)\n  }\n\n  const target = parseKernelLocationProxyTarget(options.location, options.gl)\n  if (!target || !options.kernelApiKey) {\n    return resolution('configured_fallback', options.proxyMode, options.configuredKernelProxyId, target, target ? null : 'location could not be normalized to a US city/state proxy target')\n  }\n\n  const kernel = new Kernel({ apiKey: options.kernelApiKey })\n  try {\n    const attemptIndex = options.attemptIndex ?? 0\n\n    if (options.fresh) {\n      return await createFreshLocationProxy(kernel, options, target)\n    }\n\n    if (attemptIndex >= 1) {\n      const escalatedTarget = escalatedTargetLevel(target, attemptIndex)\n      const createErrors: string[] = []\n      try {\n        const created = await kernel.proxies.create({\n          type: 'residential',\n          name: escalatedTarget.proxyName,\n          config: escalatedTarget.config,\n        }) as KernelProxyCreateResponse\n        if (created.id) {\n          return resolution('location_created', options.proxyMode, created.id, escalatedTarget, null)\n        }\n        createErrors.push(`${escalatedTarget.state}: Kernel did not return a proxy id`)\n      } catch (err) {\n        createErrors.push(`${escalatedTarget.state}: ${errorText(err)}`)\n      }\n      return settleCreateExhaustion(options, escalatedTarget, createErrors)\n    }\n\n    const proxies = await kernel.proxies.list() as KernelProxyListItem[]\n    const zip = knownZipFor(target, options.proxyZip)\n    const createErrors: string[] = []\n    if (zip) {\n      const targetZip = zipTarget(target, zip)\n      const existingZip = findExistingTargetProxy(proxies, targetZip)\n      if (existingZip?.id) {\n        return resolution('location_reused', options.proxyMode, existingZip.id, targetZip, null)\n      }\n      try {\n        const created = await kernel.proxies.create({\n          type: 'residential',\n          name: targetZip.proxyName,\n          config: {\n            country: targetZip.country,\n            zip,\n          },\n        }) as KernelProxyCreateResponse\n        if (created.id) {\n          return resolution('location_created', options.proxyMode, created.id, targetZip, null)\n        }\n        createErrors.push(`${zip}: Kernel did not return a proxy id`)\n      } catch (err) {\n        createErrors.push(`${zip}: ${errorText(err)}`)\n      }\n    }\n\n    const existing = findExistingProxy(proxies, target)\n    if (existing?.id) {\n      return resolution('location_reused', options.proxyMode, existing.id, target, createErrors.join(' | ') || null)\n    }\n\n    for (const city of target.cityCandidates) {\n      try {\n        const created = await kernel.proxies.create({\n          type: 'residential',\n          name: proxyName(target.country, target.state, city),\n          config: {\n            country: target.country,\n            state: target.state,\n            city,\n          },\n        }) as KernelProxyCreateResponse\n        if (created.id) {\n          return resolution('location_created', options.proxyMode, created.id, {\n            ...target,\n            level: 'city',\n            city,\n            proxyName: proxyName(target.country, target.state, city),\n            config: {\n              country: target.country,\n              state: target.state,\n              city,\n            },\n          }, null)\n        }\n        createErrors.push(`${city}: Kernel did not return a proxy id`)\n      } catch (err) {\n        createErrors.push(`${city}: ${errorText(err)}`)\n      }\n    }\n    const fallbackTarget = stateTarget(target)\n    const existingState = findExistingStateProxy(proxies, fallbackTarget)\n    if (existingState?.id) {\n      return resolution('location_reused', options.proxyMode, existingState.id, fallbackTarget, createErrors.join(' | '))\n    }\n    try {\n      const created = await kernel.proxies.create({\n        type: 'residential',\n        name: fallbackTarget.proxyName,\n        config: fallbackTarget.config,\n      }) as KernelProxyCreateResponse\n      if (created.id) {\n        return resolution('location_created', options.proxyMode, created.id, fallbackTarget, createErrors.join(' | '))\n      }\n      createErrors.push(`${fallbackTarget.state}: Kernel did not return a proxy id`)\n    } catch (err) {\n      createErrors.push(`${fallbackTarget.state}: ${errorText(err)}`)\n    }\n    return settleCreateExhaustion(options, target, createErrors)\n  } catch (err) {\n    return resolution('configured_fallback', options.proxyMode, options.configuredKernelProxyId, target, errorText(err))\n  }\n}\n","import { randomInt } from 'node:crypto'\nimport Kernel from '@onkernel/sdk'\n\nconst BACKUP_PROXY_HOST = 'pr.oxylabs.io'\nconst BACKUP_PROXY_PORT = 7777\nconst BACKUP_STICKY_SESSION_MINUTES = 10\n\nexport const BACKUP_LAST_RESORT_ATTEMPTS = 2\n\ntype BackupProxyCreateResponse = {\n  id?: string\n}\n\nexport function backupProxyUsername(): string | undefined {\n  const value = process.env.OXYLABS_USERNAME?.trim()\n  return value || undefined\n}\n\nexport function backupProxyPassword(): string | undefined {\n  const value = process.env.OXYLABS_PASSWORD?.trim()\n  return value || undefined\n}\n\nexport function backupProxyCountry(): string {\n  const value = process.env.OXYLABS_COUNTRY?.trim()\n  return (value || 'US').toUpperCase()\n}\n\nexport function backupProxyAvailable(): boolean {\n  return Boolean(backupProxyUsername() && backupProxyPassword())\n}\n\nfunction randomStickySessionId(): string {\n  let digits = ''\n  for (let i = 0; i < 10; i++) digits += String(randomInt(0, 10))\n  return digits\n}\n\nexport function buildBackupUsername(opts: { country?: string; sticky?: boolean } = {}): string | undefined {\n  const user = backupProxyUsername()\n  if (!user) return undefined\n  const base = user.startsWith('customer-') ? user : `customer-${user}`\n  const country = (opts.country?.trim() || backupProxyCountry()).toUpperCase()\n  const sticky = opts.sticky ? `-sessid-${randomStickySessionId()}-sesstime-${BACKUP_STICKY_SESSION_MINUTES}` : ''\n  return `${base}-cc-${country}${sticky}`\n}\n\nexport function scrubBackupProxyText(message: string): string {\n  return message\n    .replace(/pr\\.oxylabs\\.io(:\\d+)?/gi, 'backup proxy')\n    .replace(/oxylabs/gi, 'backup proxy')\n}\n\nfunction backupProxyName(attemptIndex: number | undefined): string {\n  const stamp = `${Date.now()}-${attemptIndex ?? 0}-${Math.random().toString(36).slice(2, 8)}`\n  return `mcp-backup-custom-${stamp}`\n}\n\nexport async function createBackupProxyId(\n  kernelApiKey: string,\n  opts: { sticky?: boolean; attemptIndex?: number } = {},\n): Promise<string> {\n  const username = buildBackupUsername({ sticky: opts.sticky })\n  const password = backupProxyPassword()\n  if (!username || !password) throw new Error('backup proxy pool is not configured')\n  const kernel = new Kernel({ apiKey: kernelApiKey })\n  try {\n    const created = await kernel.proxies.create({\n      type: 'custom',\n      name: backupProxyName(opts.attemptIndex),\n      protocol: 'http',\n      config: {\n        host: BACKUP_PROXY_HOST,\n        port: BACKUP_PROXY_PORT,\n        username,\n        password,\n      },\n    }) as BackupProxyCreateResponse\n    if (!created.id) throw new Error('backup proxy creation did not return a proxy id')\n    return created.id\n  } catch (err) {\n    const message = err instanceof Error ? err.message : String(err)\n    throw new Error(scrubBackupProxyText(message))\n  }\n}\n\nexport async function createBackupProxyIdSafe(\n  kernelApiKey: string | undefined,\n  attemptIndex: number,\n): Promise<string | undefined> {\n  if (!kernelApiKey || !backupProxyAvailable()) return undefined\n  try {\n    return await createBackupProxyId(kernelApiKey, { attemptIndex })\n  } catch {\n    return undefined\n  }\n}\n\nexport async function cleanupBackupProxyId(\n  kernelApiKey: string | undefined,\n  proxyId: string | undefined,\n): Promise<void> {\n  if (!kernelApiKey || !proxyId) return\n  try {\n    const kernel = new Kernel({ apiKey: kernelApiKey })\n    await kernel.proxies.delete(proxyId)\n  } catch {\n    void 0\n  }\n}\n\nexport async function withBackupProxyCleanup<T>(\n  kernelApiKey: string | undefined,\n  proxyId: string | undefined,\n  fn: (proxyId: string | undefined) => Promise<T>,\n): Promise<T> {\n  try {\n    return await fn(proxyId)\n  } finally {\n    await cleanupBackupProxyId(kernelApiKey, proxyId)\n  }\n}\n","import { DEFAULT_PROXY_MODE, HarvestOptionsSchema } from './schemas.js'\nimport { browserServiceApiKey, browserServiceProxyId } from './lib/browser-service-env.js'\nimport type { HarvestDebugSnapshot, HarvestOptions, HarvestResult, ProxyMode } from './types.js'\nimport { BrowserDriver } from './driver/BrowserDriver.js'\nimport { PAAExtractor } from './extractor/PAAExtractor.js'\nimport { OutputSerializer } from './output/OutputSerializer.js'\nimport { ProgressReporter } from './output/ProgressReporter.js'\nimport { CaptchaError, LocationMismatchError, RequestAbortedError, sanitizeVendorName } from './errors.js'\nimport type { BrowserCloseResult } from './driver/IBrowserDriver.js'\nimport { deleteKernelProxyId, resolveKernelProxyId } from './kernel-proxy-resolver.js'\nimport { HEADLESS_HARVEST_TEST_ENV, currentCostContext, runWithCostContext } from './api/cost-context.js'\n\nconst MAX_ATTEMPTS = 3\nconst LOCATION_PROXY_MAX_ATTEMPTS = 5\n\nexport type HarvestAttemptOutcome =\n  | 'paa_found'\n  | 'paa_partial'\n  | 'no_paa'\n  | 'serp_only'\n  | 'degraded_result'\n  | 'captcha'\n  | 'location_mismatch'\n  | 'proxy_tunnel_failed'\n  | 'proxy_unavailable'\n  | 'request_aborted'\n  | 'timeout'\n  | 'error'\n\nexport type HarvestAttemptLogEvent =\n  | {\n      type: 'started'\n      attemptNumber: number\n      maxAttempts: number\n      query: string\n      location: string | null\n      maxQuestions: number\n      startedAt: string\n    }\n  | {\n      type: 'finished'\n      attemptNumber: number\n      maxAttempts: number\n      outcome: HarvestAttemptOutcome\n      kernelSessionId: string | null\n      questionCount: number\n      durationMs: number\n      error: string | null\n      willRetry: boolean\n      cleanup: BrowserCloseResult\n      debug: HarvestDebugSnapshot | null\n      completedAt: string\n    }\n\ntype HarvestAttemptLogSink = (event: HarvestAttemptLogEvent) => void | Promise<void>\n\ntype ExtractOnceOutcome =\n  | { result: HarvestResult; error: null; cleanup: BrowserCloseResult; debug: HarvestDebugSnapshot | null; salvagedFrom?: unknown }\n  | { result: null; error: unknown; cleanup: BrowserCloseResult; debug: HarvestDebugSnapshot | null; salvagedFrom?: undefined }\n\nfunction abortReason(signal: AbortSignal): unknown {\n  if (signal.reason instanceof DOMException && signal.reason.name === 'TimeoutError') return signal.reason\n  return new RequestAbortedError()\n}\n\nfunction getAbortSignal(rawOptions: unknown): AbortSignal | undefined {\n  if (!rawOptions || typeof rawOptions !== 'object') return undefined\n  const signal = (rawOptions as { signal?: unknown }).signal\n  if (signal instanceof AbortSignal) return signal\n  return undefined\n}\n\nfunction getAttemptLogSink(rawOptions: unknown): HarvestAttemptLogSink | undefined {\n  if (!rawOptions || typeof rawOptions !== 'object') return undefined\n  const sink = (rawOptions as { onAttemptEvent?: unknown }).onAttemptEvent\n  return typeof sink === 'function' ? sink as HarvestAttemptLogSink : undefined\n}\n\nasync function emitAttemptEvent(sink: HarvestAttemptLogSink | undefined, event: HarvestAttemptLogEvent): Promise<void> {\n  if (!sink) return\n  try {\n    await sink(event)\n  } catch (err) {\n    console.warn(JSON.stringify({\n      event: 'harvest_attempt_log_failed',\n      attempt_number: event.attemptNumber,\n      message: err instanceof Error ? err.message : String(err),\n    }))\n  }\n}\n\nfunction classifyAttemptError(err: unknown): HarvestAttemptOutcome {\n  if (err instanceof CaptchaError) return 'captcha'\n  if (err instanceof LocationMismatchError) return 'location_mismatch'\n  if (err instanceof RequestAbortedError) return 'request_aborted'\n  if (err instanceof DOMException && (err.name === 'TimeoutError' || err.name === 'AbortError')) return 'timeout'\n  const message = err instanceof Error ? err.message : String(err)\n  if (looksLikeProxyTunnelFailure(message)) return 'proxy_tunnel_failed'\n  if (looksLikeProxyUnavailable(message)) return 'proxy_unavailable'\n  return /timeout|timed out|Timeout \\d+ms exceeded|deadline/i.test(message) ? 'timeout' : 'error'\n}\n\nfunction classifyAttemptResult(result: HarvestResult): HarvestAttemptOutcome {\n  if (result.diagnostics.degradedResult) return 'degraded_result'\n  return result.diagnostics?.completionStatus ?? (result.totalQuestions > 0 ? 'paa_found' : 'no_paa')\n}\n\nfunction resultHasUsableContent(result: HarvestResult): boolean {\n  return result.flat.length > 0 ||\n    result.organicResults.length > 0 ||\n    result.localPack.length > 0 ||\n    result.videos.length > 0 ||\n    result.forums.length > 0 ||\n    result.whatPeopleSaying.length > 0 ||\n    result.aiOverview.detected ||\n    result.aiMode.detected\n}\n\nfunction withResultQuality(result: HarvestResult): HarvestResult {\n  const hasUsableContent = resultHasUsableContent(result)\n  const hasWarnings = (result.diagnostics.warnings?.length ?? 0) > 0\n  const resultQuality = hasUsableContent\n    ? (hasWarnings ? 'partial' : 'complete')\n    : 'degraded'\n  const degradedResult = resultQuality === 'degraded'\n\n  result.diagnostics = {\n    ...result.diagnostics,\n    resultQuality,\n    degradedResult,\n    degradationReasons: degradedResult ? ['empty_primary_serp'] : [],\n    retryRecommended: degradedResult,\n  }\n  return result\n}\n\nfunction degradedResultMessage(result: HarvestResult): string {\n  const reasons = result.diagnostics.degradationReasons ?? []\n  if (reasons.includes('empty_primary_serp')) {\n    return 'No usable primary SERP surface was captured; the result may be incomplete.'\n  }\n  return 'The harvest result was marked degraded.'\n}\n\nfunction errorMessage(err: unknown): string {\n  return err instanceof Error ? err.message : String(err)\n}\n\nfunction maxAttemptsForProxyMode(proxyMode: ProxyMode): number {\n  return proxyMode === 'location' ? LOCATION_PROXY_MAX_ATTEMPTS : MAX_ATTEMPTS\n}\n\nfunction looksLikeProxyTunnelFailure(message: string): boolean {\n  return /ERR_TUNNEL_CONNECTION_FAILED|ERR_PROXY_CONNECTION_FAILED|ERR_SOCKS_CONNECTION_FAILED|tunnel connection failed|proxy connection failed|transport error: proxy/i.test(message)\n}\n\nfunction looksLikeProxyUnavailable(message: string): boolean {\n  return /proxy unavailable|proxy_unavailable|connection_test_failed|did not return a proxy id|configured fallback/i.test(message)\n}\n\nfunction retryableLocationProxyError(outcome: HarvestAttemptOutcome): boolean {\n  return outcome === 'captcha' || outcome === 'proxy_tunnel_failed' || outcome === 'proxy_unavailable'\n}\n\nfunction locationMismatchMessage(result: HarvestResult): string {\n  const evidence = result.diagnostics.debug?.locationEvidence\n  const expected = evidence?.expected?.canonicalLocation ?? result.location ?? 'requested location'\n  const candidates = evidence?.candidates\n    .slice(0, 3)\n    .map(candidate => `${candidate.city}, ${candidate.regionCode}`)\n    .join('; ')\n  return candidates\n    ? `Google returned results for ${candidates}, not ${expected}`\n    : `Google returned results for a different location than ${expected}`\n}\n\nfunction shouldRetryLocationMismatch(result: HarvestResult, proxyMode: ProxyMode): boolean {\n  return proxyMode === 'location' && result.diagnostics.debug?.locationEvidence?.status === 'mismatch'\n}\n\nfunction stripInternalDebug(result: HarvestResult, keepDebug: boolean): HarvestResult {\n  if (keepDebug || !result.diagnostics.debug) return result\n  const diagnostics = { ...result.diagnostics }\n  delete diagnostics.debug\n  return { ...result, diagnostics }\n}\n\nasync function cleanupDisposableProxy(kernelApiKey: string | undefined, proxyId: string | undefined): Promise<void> {\n  if (!kernelApiKey || !proxyId) return\n  try {\n    await deleteKernelProxyId(kernelApiKey, proxyId)\n    console.info(JSON.stringify({\n      event: 'kernel_proxy_deleted',\n      proxy_id_suffix: proxyId.slice(-6),\n    }))\n  } catch (err) {\n    console.warn(JSON.stringify({\n      event: 'kernel_proxy_delete_failed',\n      proxy_id_suffix: proxyId.slice(-6),\n      message: errorMessage(err),\n    }))\n  }\n}\n\nasync function extractOnce(options: HarvestOptions, signal?: AbortSignal): Promise<ExtractOnceOutcome> {\n  const driver = new BrowserDriver()\n  const reporter = new ProgressReporter()\n  const extractor = new PAAExtractor(driver, reporter)\n  if (signal?.aborted) {\n    return {\n      result: null,\n      error: abortReason(signal),\n      cleanup: await driver.close(),\n      debug: null,\n    }\n  }\n\n  let onAbort: (() => void) | undefined\n  const abortPromise = signal\n    ? new Promise<never>((_, reject) => {\n        onAbort = () => reject(abortReason(signal))\n        signal.addEventListener('abort', onAbort, { once: true })\n      })\n    : null\n\n  let result: HarvestResult | null = null\n  let error: unknown = null\n  let cleanup: BrowserCloseResult\n  let debug: HarvestDebugSnapshot | null = null\n  try {\n    const extraction = extractor.extract(options, signal)\n    if (abortPromise) extraction.catch(() => {})\n    result = await (abortPromise ? Promise.race([extraction, abortPromise]) : extraction)\n  } catch (err) {\n    error = err\n  } finally {\n    if (signal && onAbort) signal.removeEventListener('abort', onAbort)\n    debug = result?.diagnostics.debug ?? (options.debug ? {\n      enabled: true,\n      request: {\n        query: options.query,\n        locationInput: options.location ?? null,\n        canonicalLocation: null,\n        uule: null,\n        gl: options.gl,\n        hl: options.hl,\n        device: options.device,\n        proxyMode: options.proxyMode,\n        proxyZip: options.proxyZip ?? null,\n        serpOnly: options.serpOnly,\n        pages: options.pages ?? 1,\n      },\n      browser: driver.getDebugSnapshot(),\n    } : null)\n    cleanup = await driver.close()\n  }\n  if (error) {\n    const outcome = classifyAttemptError(error)\n    const budgetExhausted = outcome === 'timeout' || outcome === 'request_aborted'\n    let salvaged: HarvestResult | null = null\n    if (budgetExhausted) {\n      try {\n        salvaged = typeof extractor.getPartialResult === 'function' ? extractor.getPartialResult() : null\n      } catch {\n        salvaged = null\n      }\n    }\n    if (salvaged) {\n      return { result: salvaged, error: null, cleanup: cleanup!, debug, salvagedFrom: error }\n    }\n    return { result: null, error, cleanup: cleanup!, debug }\n  }\n  return { result: result!, error: null, cleanup: cleanup!, debug }\n}\n\nexport async function harvest(rawOptions: unknown): Promise<HarvestResult> {\n  const raw = typeof rawOptions === 'object' && rawOptions !== null ? rawOptions : {}\n  const signal = getAbortSignal(rawOptions)\n  const onAttemptEvent = getAttemptLogSink(rawOptions)\n  const requestedProxyMode = (raw as { proxyMode?: unknown }).proxyMode\n  const proxyMode = requestedProxyMode === 'location' || requestedProxyMode === 'none' || requestedProxyMode === 'configured'\n    ? requestedProxyMode\n    : DEFAULT_PROXY_MODE\n  const kernelApiKey = typeof (raw as { kernelApiKey?: unknown }).kernelApiKey === 'string'\n    ? ((raw as { kernelApiKey: string }).kernelApiKey).trim()\n    : browserServiceApiKey()\n  const configuredKernelProxyId = typeof (raw as { kernelProxyId?: unknown }).kernelProxyId === 'string'\n    ? ((raw as { kernelProxyId: string }).kernelProxyId).trim()\n    : browserServiceProxyId()\n  const proxyOpts = {\n    kernelApiKey,\n    proxyMode: proxyMode as ProxyMode,\n    configuredKernelProxyId,\n    location: typeof (raw as { location?: unknown }).location === 'string' ? (raw as { location: string }).location : undefined,\n    proxyZip: typeof (raw as { proxyZip?: unknown }).proxyZip === 'string' ? (raw as { proxyZip: string }).proxyZip : undefined,\n    gl: typeof (raw as { gl?: unknown }).gl === 'string' ? (raw as { gl: string }).gl : 'us',\n  }\n  const requestedDebug = typeof (raw as { debug?: unknown }).debug === 'boolean'\n    ? (raw as { debug: boolean }).debug\n    : false\n  const needsLocationEvidence = proxyMode === 'location' && Boolean(proxyOpts.location)\n  const requestedMaxAttempts = (raw as { maxAttempts?: unknown }).maxAttempts\n  const baseMaxAttempts = typeof requestedMaxAttempts === 'number' && Number.isInteger(requestedMaxAttempts) && requestedMaxAttempts >= 1\n    ? requestedMaxAttempts\n    : maxAttemptsForProxyMode(proxyMode as ProxyMode) + 1\n  const maxAttempts = baseMaxAttempts\n  const softDeadlineMs = typeof (raw as { softDeadlineMs?: unknown }).softDeadlineMs === 'number'\n    ? (raw as { softDeadlineMs: number }).softDeadlineMs\n    : null\n  const serializer = new OutputSerializer()\n  let lastError: unknown = null\n  let effectiveProxyMode: ProxyMode = proxyMode as ProxyMode\n  let proxyFailures = 0\n\n  for (let i = 0; i < maxAttempts; i++) {\n    const attemptNumber = i + 1\n    const startedAtMs = Date.now()\n    try {\n      if (signal?.aborted) throw abortReason(signal)\n      const resolution = await resolveKernelProxyId({\n        ...proxyOpts,\n        proxyMode: effectiveProxyMode,\n        attemptIndex: i,\n        fresh: effectiveProxyMode === 'location',\n      })\n      if (!resolution) break\n      const mergedAttempt = {\n        ...raw,\n        kernelApiKey,\n        kernelProxyId: resolution.kernelProxyId,\n        kernelProxyResolution: resolution.resolution,\n        proxyMode: effectiveProxyMode,\n        keepDefaultProxy: i > 0,\n        debug: requestedDebug || needsLocationEvidence,\n      }\n      if (effectiveProxyMode === 'none') (mergedAttempt as Record<string, unknown>).kernelProxyId = undefined\n      const attemptOptions = HarvestOptionsSchema.parse(mergedAttempt)\n      await emitAttemptEvent(onAttemptEvent, {\n        type: 'started',\n        attemptNumber,\n        maxAttempts,\n        query: attemptOptions.query,\n        location: attemptOptions.location ?? null,\n        maxQuestions: attemptOptions.maxQuestions,\n        startedAt: new Date(startedAtMs).toISOString(),\n      })\n      console.info(JSON.stringify({\n        event: 'harvest_attempt_started',\n        attempt_number: attemptNumber,\n        max_attempts: maxAttempts,\n        query: attemptOptions.query,\n        location: attemptOptions.location ?? null,\n        max_questions: attemptOptions.maxQuestions,\n      }))\n      const baseCtx = currentCostContext()\n      const allowHeadlessTest = baseCtx?.harvestBrowserMode === 'headless-test'\n        && process.env[HEADLESS_HARVEST_TEST_ENV] === 'true'\n      const attempt = baseCtx\n        ? await runWithCostContext({\n          ...baseCtx,\n          forceHeadless: allowHeadlessTest,\n          forceHeadful: !allowHeadlessTest,\n        }, () => extractOnce(attemptOptions, signal))\n        : await extractOnce(attemptOptions, signal)\n      if (attempt.error) {\n        const err = attempt.error\n        const outcome = classifyAttemptError(err)\n        if ((outcome === 'captcha' || outcome === 'proxy_tunnel_failed') && effectiveProxyMode === 'location') {\n          proxyFailures++\n          if (proxyFailures >= 2) effectiveProxyMode = 'none'\n        }\n        const willRetry = i < maxAttempts - 1 && (\n          outcome === 'captcha' || (proxyMode === 'location' && retryableLocationProxyError(outcome))\n        )\n        if (outcome === 'captcha') {\n          console.warn(JSON.stringify({\n            event: 'harvest_attempt_captcha',\n            attempt_number: attemptNumber,\n            max_attempts: maxAttempts,\n            message: errorMessage(err),\n            will_retry: willRetry,\n          }))\n        } else if (willRetry) {\n          console.warn(JSON.stringify({\n            event: 'harvest_attempt_proxy_retry',\n            attempt_number: attemptNumber,\n            max_attempts: maxAttempts,\n            outcome,\n            message: errorMessage(err),\n            will_retry: willRetry,\n          }))\n        }\n        await emitAttemptEvent(onAttemptEvent, {\n          type: 'finished',\n          attemptNumber,\n          maxAttempts,\n          outcome,\n          kernelSessionId: attempt.cleanup.kernelSessionId,\n          questionCount: 0,\n          durationMs: Date.now() - startedAtMs,\n          error: errorMessage(err),\n          willRetry,\n          cleanup: attempt.cleanup,\n          debug: attempt.debug,\n          completedAt: new Date().toISOString(),\n        })\n        await cleanupDisposableProxy(kernelApiKey, resolution.disposableProxyId)\n        lastError = err\n        if (willRetry) continue\n        break\n      }\n      const result = attempt.result\n      if (!result) throw new Error('Harvest attempt completed without a result')\n      if (shouldRetryLocationMismatch(result, proxyMode as ProxyMode)) {\n        const err = new LocationMismatchError(locationMismatchMessage(result))\n        const willRetry = i < maxAttempts - 1\n        console.warn(JSON.stringify({\n          event: 'harvest_attempt_location_mismatch',\n          attempt_number: attemptNumber,\n          max_attempts: maxAttempts,\n          message: err.message,\n          will_retry: willRetry,\n        }))\n        await emitAttemptEvent(onAttemptEvent, {\n          type: 'finished',\n          attemptNumber,\n          maxAttempts,\n          outcome: 'location_mismatch',\n          kernelSessionId: attempt.cleanup.kernelSessionId,\n          questionCount: result.totalQuestions,\n          durationMs: Date.now() - startedAtMs,\n          error: err.message,\n          willRetry,\n          cleanup: attempt.cleanup,\n          debug: attempt.debug,\n          completedAt: new Date().toISOString(),\n        })\n        await cleanupDisposableProxy(kernelApiKey, resolution.disposableProxyId)\n        lastError = err\n        if (willRetry) continue\n        break\n      }\n      const finalResult = withResultQuality(stripInternalDebug(result, requestedDebug))\n      const outcome = classifyAttemptResult(finalResult)\n      const willRetry = outcome === 'degraded_result' && i < maxAttempts - 1\n      const resultError = outcome === 'degraded_result' ? degradedResultMessage(finalResult) : null\n      if (willRetry) {\n        console.warn(JSON.stringify({\n          event: 'harvest_attempt_degraded_result',\n          attempt_number: attemptNumber,\n          max_attempts: maxAttempts,\n          degradation_reasons: finalResult.diagnostics.degradationReasons,\n          will_retry: true,\n        }))\n      }\n      await emitAttemptEvent(onAttemptEvent, {\n        type: 'finished',\n        attemptNumber,\n        maxAttempts,\n        outcome,\n        kernelSessionId: attempt.cleanup.kernelSessionId,\n        questionCount: finalResult.totalQuestions,\n        durationMs: Date.now() - startedAtMs,\n        error: resultError,\n        willRetry,\n        cleanup: attempt.cleanup,\n        debug: attempt.debug,\n        completedAt: new Date().toISOString(),\n      })\n      await cleanupDisposableProxy(kernelApiKey, resolution.disposableProxyId)\n      if (willRetry) continue\n      if (attemptOptions.format === 'json' || attemptOptions.format === 'both') {\n        await serializer.writeJSON(finalResult, attemptOptions.outputDir)\n      }\n      if (attemptOptions.format === 'csv' || attemptOptions.format === 'both') {\n        await Promise.all([\n          serializer.writeCSV(finalResult.flat, attemptOptions.outputDir),\n          finalResult.videos.length > 0 ? serializer.writeVideoCSV(finalResult.videos, finalResult.seed, attemptOptions.outputDir) : Promise.resolve(''),\n          finalResult.forums.length > 0 ? serializer.writeForumCSV(finalResult.forums, finalResult.seed, attemptOptions.outputDir) : Promise.resolve(''),\n          finalResult.aiOverview.detected        ? serializer.writeAIOverviewCSV(finalResult.aiOverview.citations, finalResult.aiOverview.text, finalResult.seed, attemptOptions.outputDir) : Promise.resolve(''),\n          finalResult.aiMode.detected            ? serializer.writeAIModeCSV(finalResult.aiMode.citations, finalResult.aiMode.text, finalResult.seed, attemptOptions.outputDir)    : Promise.resolve(''),\n          finalResult.whatPeopleSaying.length > 0 ? serializer.writeWhatPeopleSayingCSV(finalResult.whatPeopleSaying, finalResult.seed, attemptOptions.outputDir)            : Promise.resolve(''),\n        ])\n      }\n      return finalResult\n    } catch (err) {\n      const outcome = classifyAttemptError(err)\n      if ((outcome === 'captcha' || outcome === 'proxy_tunnel_failed') && effectiveProxyMode === 'location') {\n        proxyFailures++\n        if (proxyFailures >= 2) effectiveProxyMode = 'none'\n      }\n      const willRetry = i < maxAttempts - 1 && (\n        outcome === 'captcha' || (proxyMode === 'location' && retryableLocationProxyError(outcome))\n      )\n      if (outcome === 'captcha') {\n        console.warn(JSON.stringify({\n          event: 'harvest_attempt_captcha',\n          attempt_number: attemptNumber,\n          max_attempts: maxAttempts,\n          message: errorMessage(err),\n          will_retry: willRetry,\n        }))\n      } else if (willRetry) {\n        console.warn(JSON.stringify({\n          event: 'harvest_attempt_proxy_retry',\n          attempt_number: attemptNumber,\n          max_attempts: maxAttempts,\n          outcome,\n          message: errorMessage(err),\n          will_retry: willRetry,\n        }))\n      }\n      await emitAttemptEvent(onAttemptEvent, {\n        type: 'finished',\n        attemptNumber,\n        maxAttempts,\n        outcome,\n        kernelSessionId: null,\n        questionCount: 0,\n        durationMs: Date.now() - startedAtMs,\n        error: errorMessage(err),\n        willRetry,\n        cleanup: {\n          kernelSessionId: null,\n          kernelDeleteStarted: false,\n          kernelDeleteSucceeded: null,\n          kernelDeleteError: null,\n          browserCloseSucceeded: null,\n          browserCloseError: null,\n        },\n        debug: null,\n        completedAt: new Date().toISOString(),\n      })\n      lastError = err\n      if (willRetry) continue\n      if (outcome === 'captcha') break\n      throw err\n    }\n  }\n  if (lastError && !(lastError instanceof CaptchaError)) throw lastError\n  console.warn(JSON.stringify({\n    event: 'harvest_captcha_exhausted',\n    max_attempts: maxAttempts,\n    session_kind: kernelApiKey ? 'kernel' : 'local',\n  }))\n  throw new CaptchaError(sanitizeVendorName(`CAPTCHA on all ${maxAttempts} fresh sessions. Try again in a few minutes.`))\n}\n","import { execSync } from 'node:child_process'\nimport { readFileSync, writeFileSync, unlinkSync, mkdirSync } from 'node:fs'\nimport { tmpdir } from 'node:os'\nimport { join } from 'node:path'\nimport { fal } from '@fal-ai/client'\nimport { buildClipPrompts, extractEpisodePrompts } from './promptBuilder.js'\nimport type { EpisodeBrief } from './episodeBrief.js'\nimport { generateVoiceover, addBackgroundAudio } from './AudioGenerator.js'\nimport { concatenateClips, uploadToFal, overlayVoiceover } from './VideoMixer.js'\n\nexport interface ClipPairOptions {\n  resolution?:           '480p' | '720p' | '1080p'\n  aspectRatio?:          '16:9' | '9:16' | '1:1'\n  clipDurationSeconds?:  number\n  generateAudio?:        boolean\n  seed?:                 number\n  outputDir?:            string\n}\n\nexport interface ClipPairResult {\n  clip1Url:       string\n  clip2Url:       string\n  finalVideoPath: string\n  seed:           number\n  promptClip1:    string\n  promptClip2:    string\n  voiceover:      string\n  audioMood:      string\n}\n\ninterface VideoOutput { video: { url: string }; seed: number }\n\nconst T2V = 'bytedance/seedance-2.0/text-to-video'\nconst I2V = 'bytedance/seedance-2.0/image-to-video'\n\nfunction buildInput(prompt: string, opts: ClipPairOptions, seed?: number, imageUrl?: string) {\n  return {\n    prompt,\n    resolution:     opts.resolution ?? '720p',\n    duration:       opts.clipDurationSeconds ?? 8,\n    aspect_ratio:   opts.aspectRatio ?? '16:9',\n    generate_audio: false,\n    ...(seed     !== undefined ? { seed }               : {}),\n    ...(imageUrl !== undefined ? { image_url: imageUrl } : {}),\n  }\n}\n\nasync function generate(model: string, input: Record<string, unknown>): Promise<VideoOutput> {\n  const { request_id } = await fal.queue.submit(model, { input })\n  console.log(`[fal] submitted ${model} → ${request_id}`)\n  while (true) {\n    await new Promise(r => setTimeout(r, 5000))\n    const s = await fal.queue.status(model, { requestId: request_id, logs: false })\n    console.log(`[fal] ${request_id} → ${s.status}`)\n    if ((s.status as string) === 'FAILED') throw new Error(`Request ${request_id} failed`)\n    if ((s.status as string) !== 'COMPLETED') continue\n    const result = await fal.queue.result(model, { requestId: request_id })\n    return result.data as VideoOutput\n  }\n}\n\nasync function extractLastFrame(videoUrl: string, outDir: string): Promise<string> {\n  const ts      = Date.now()\n  const mp4Path = join(outDir, `clip1-raw-${ts}.mp4`)\n  const jpgPath = join(outDir, `last-frame-${ts}.jpg`)\n\n  const res = await fetch(videoUrl)\n  if (!res.ok) throw new Error(`Failed to download clip 1 (${res.status})`)\n  writeFileSync(mp4Path, Buffer.from(await res.arrayBuffer()))\n\n  try {\n    execSync(`ffmpeg -sseof -0.1 -i \"${mp4Path}\" -vframes 1 -y \"${jpgPath}\" -loglevel error`)\n  } finally {\n    try { unlinkSync(mp4Path) } catch {}\n  }\n  return jpgPath\n}\n\nexport class VideoGenerator {\n  constructor(apiKey?: string) {\n    const key = apiKey ?? process.env['FAL_KEY']\n    if (!key) throw new Error('FAL_KEY is required')\n    fal.config({ credentials: key })\n  }\n\n  async generateClipPair(\n    question: string,\n    answer:   string,\n    opts:     ClipPairOptions = {},\n  ): Promise<ClipPairResult> {\n    const outDir = opts.outputDir ?? join(tmpdir(), `paa-video-${Date.now()}`)\n    mkdirSync(outDir, { recursive: true })\n\n    console.log('\\n[1/7] Generating prompts via QWEN 3.6...')\n    const prompts = await buildClipPrompts(question, answer)\n    console.log('  Voiceover:', prompts.voiceover)\n    console.log('  Audio mood:', prompts.audioMood)\n\n    console.log('\\n[2/7] Generating clip 1 (text-to-video)...')\n    const result1 = await generate(T2V, buildInput(prompts.clip1, opts, opts.seed))\n\n    console.log('\\n[3/7] Extracting last frame → clip 2 start...')\n    const jpgPath  = await extractLastFrame(result1.video.url, outDir)\n    const imageBlob = new Blob([readFileSync(jpgPath)], { type: 'image/jpeg' })\n    const frameUrl  = await fal.storage.upload(imageBlob)\n    try { unlinkSync(jpgPath) } catch {}\n\n    console.log('\\n[4/7] Generating clip 2 (image-to-video from last frame)...')\n    const seed2   = opts.seed !== undefined ? opts.seed + 1 : undefined\n    const result2 = await generate(I2V, buildInput(prompts.clip2, opts, seed2, frameUrl))\n\n    console.log('\\n[5/7] Concatenating clips + generating voiceover (parallel)...')\n    const [combinedPath, voiceoverUrl] = await Promise.all([\n      concatenateClips(result1.video.url, result2.video.url, outDir),\n      generateVoiceover(prompts.voiceover),\n    ])\n\n    console.log('\\n[6/7] Adding background audio via MMAudio V2...')\n    const falVideoUrl        = await uploadToFal(combinedPath)\n    const totalDuration      = (opts.clipDurationSeconds ?? 8) * 2\n    const videoWithAudioUrl  = await addBackgroundAudio(falVideoUrl, prompts.audioMood, totalDuration)\n\n    console.log('\\n[7/7] Overlaying voiceover on final video...')\n    const videoWithAudioPath = join(outDir, `with-bg-audio-${Date.now()}.mp4`)\n    const bgRes = await fetch(videoWithAudioUrl)\n    writeFileSync(videoWithAudioPath, Buffer.from(await bgRes.arrayBuffer()))\n    const finalVideoPath = await overlayVoiceover(videoWithAudioPath, voiceoverUrl, outDir)\n\n    return {\n      clip1Url:       result1.video.url,\n      clip2Url:       result2.video.url,\n      finalVideoPath,\n      seed:           result1.seed,\n      promptClip1:    prompts.clip1,\n      promptClip2:    prompts.clip2,\n      voiceover:      prompts.voiceover,\n      audioMood:      prompts.audioMood,\n    }\n  }\n\n  async generateEpisode(\n    brief: EpisodeBrief,\n    opts:  ClipPairOptions = {},\n  ): Promise<ClipPairResult> {\n    const outDir = opts.outputDir ?? join(tmpdir(), `episode-${brief.episodeNumber}-${Date.now()}`)\n    mkdirSync(outDir, { recursive: true })\n\n    const prompts = extractEpisodePrompts(brief)\n    console.log(`\\n[Episode ${brief.episodeNumber}/${brief.episodeCount}] ${brief.sectionTitle}`)\n    console.log('  Voiceover:', prompts.voiceover)\n\n    console.log('\\n[2/7] Generating clip 1 (text-to-video)...')\n    const result1 = await generate(T2V, buildInput(prompts.clip1, opts, opts.seed))\n\n    console.log('\\n[3/7] Extracting last frame → clip 2 start...')\n    const jpgPath  = await extractLastFrame(result1.video.url, outDir)\n    const imageBlob = new Blob([readFileSync(jpgPath)], { type: 'image/jpeg' })\n    const frameUrl  = await fal.storage.upload(imageBlob)\n    try { unlinkSync(jpgPath) } catch {}\n\n    console.log('\\n[4/7] Generating clip 2 (image-to-video from last frame)...')\n    const seed2   = opts.seed !== undefined ? opts.seed + 1 : undefined\n    const result2 = await generate(I2V, buildInput(prompts.clip2, opts, seed2, frameUrl))\n\n    console.log('\\n[5/7] Concatenating clips + generating voiceover (parallel)...')\n    const [combinedPath, voiceoverUrl] = await Promise.all([\n      concatenateClips(result1.video.url, result2.video.url, outDir),\n      generateVoiceover(prompts.voiceover),\n    ])\n\n    console.log('\\n[6/7] Adding background audio via MMAudio V2...')\n    const falVideoUrl        = await uploadToFal(combinedPath)\n    const totalDuration      = (opts.clipDurationSeconds ?? 8) * 2\n    const videoWithAudioUrl  = await addBackgroundAudio(falVideoUrl, prompts.audioMood, totalDuration)\n\n    console.log('\\n[7/7] Overlaying voiceover on final video...')\n    const videoWithAudioPath = join(outDir, `with-bg-audio-${Date.now()}.mp4`)\n    const bgRes = await fetch(videoWithAudioUrl)\n    writeFileSync(videoWithAudioPath, Buffer.from(await bgRes.arrayBuffer()))\n    const finalVideoPath = await overlayVoiceover(videoWithAudioPath, voiceoverUrl, outDir)\n\n    return {\n      clip1Url:       result1.video.url,\n      clip2Url:       result2.video.url,\n      finalVideoPath,\n      seed:           result1.seed,\n      promptClip1:    prompts.clip1,\n      promptClip2:    prompts.clip2,\n      voiceover:      prompts.voiceover,\n      audioMood:      prompts.audioMood,\n    }\n  }\n}\n","import { recordVendorUsage } from '../api/cost-telemetry.js'\n\nexport interface ClipPromptPair {\n  clip1:      string\n  clip2:      string\n  voiceover:  string\n  audioMood:  string\n}\n\nexport interface EpisodeBriefInput {\n  storyMoment:   string\n  characterName: string\n  sectionTitle:  string\n  clip1:         string\n  clip2:         string\n  voiceover:     string\n  audioMood:     string\n}\n\nconst DEEPINFRA_URL  = 'https://api.deepinfra.com/v1/openai/chat/completions'\nconst OPENROUTER_URL = 'https://openrouter.ai/api/v1/chat/completions'\nconst QWEN_MODEL     = 'Qwen/Qwen3.6-35B-A3B'\n\nconst SYSTEM_PROMPT = `You are a video prompt engineer for Seedance 2.0 text-to-video AI.\n\nIMPORTANT TECHNICAL CONTEXT: Clip 2 will be generated using image-to-video, meaning it will start from the exact last frame of Clip 1. The two clips will be visually seamless — same location, same characters, continuous motion. You must write prompts that make this feel like one uninterrupted 16-second video.\n\nYour job: turn a PAA question and its answer into a complete short-form video with visuals, narration, and a background audio mood.\n\nProduce four things:\n\n1. clip1 (~8s): Show a real person experiencing the situation the question describes. End on a specific frozen moment — a held expression, a paused action — that clip 2 continues from.\n\n2. clip2 (~8s): Continue from that exact frozen frame. Deliver the informational payoff from the answer. Show specific facts playing out visually. Describe motion continuing from clip 1's ending frame, not a new scene.\n\n3. voiceover: A spoken narration that fits in 10 seconds of TTS audio. HARD LIMIT: 22 words maximum. Count every word before finalizing — if it exceeds 22 words, cut it. Starts with the problem, ends with the answer. No filler, no \"in conclusion\". Informational and direct.\n\n4. audioMood: 6-10 words describing INSTRUMENTAL background music only — no vocals, no voice, no lyrics. Describe instruments and mood (e.g. \"warm acoustic guitar, uplifting, professional home service, no vocals\"). Will be passed to an AI audio model.\n\nRules for visuals:\n- No text, captions, graphics, or overlays\n- Photorealistic, natural lighting, specific details\n- Describe exactly what the camera sees\n\nRespond with JSON only:\n{\"clip1\": \"...\", \"clip2\": \"...\", \"voiceover\": \"...\", \"audioMood\": \"...\"}`\n\ninterface ChatResponse {\n  choices: Array<{ message: { content: string } }>\n  usage?: { cost?: number; completion_tokens?: number }\n}\n\nasync function callLLM(apiKey: string, baseUrl: string, question: string, answer: string): Promise<ClipPromptPair> {\n  const res = await fetch(baseUrl, {\n    method:  'POST',\n    headers: { 'Authorization': `Bearer ${apiKey}`, 'Content-Type': 'application/json' },\n    body: JSON.stringify({\n      model:       QWEN_MODEL,\n      temperature: 0.7,\n      messages: [\n        { role: 'system', content: SYSTEM_PROMPT },\n        { role: 'user',   content: `Question: ${question}\\n\\nAnswer: ${answer.slice(0, 500)}` },\n      ],\n    }),\n  })\n  if (!res.ok) throw new Error(`LLM call failed (${res.status}): ${await res.text()}`)\n\n  const data  = (await res.json()) as ChatResponse\n  if (baseUrl === OPENROUTER_URL) {\n    void recordVendorUsage({ vendor: 'openrouter', model: QWEN_MODEL, units: data.usage?.cost ?? 0, unitType: 'usd' })\n  } else {\n    void recordVendorUsage({ vendor: 'deepinfra_qwen', model: QWEN_MODEL, units: data.usage?.completion_tokens ?? 0, unitType: 'tokens' })\n  }\n  const raw   = data.choices[0]?.message?.content?.trim() ?? ''\n  const match = raw.match(/\\{[\\s\\S]*\\}/)\n  if (!match) throw new Error(`No JSON in QWEN response: ${raw.slice(0, 200)}`)\n\n  const parsed = JSON.parse(match[0]) as Partial<ClipPromptPair>\n  if (!parsed.clip1 || !parsed.clip2 || !parsed.voiceover || !parsed.audioMood) {\n    throw new Error(`QWEN response missing fields: ${raw.slice(0, 200)}`)\n  }\n  return parsed as ClipPromptPair\n}\n\nexport async function buildClipPrompts(question: string, answer: string): Promise<ClipPromptPair> {\n  const deepinfraKey  = process.env['DEEPINFRA_API_KEY']\n  const openrouterKey = process.env['OPENROUTER_API_KEY']\n\n  if (deepinfraKey) {\n    try {\n      return await callLLM(deepinfraKey, DEEPINFRA_URL, question, answer)\n    } catch (err) {\n      console.warn('[promptBuilder] DeepInfra failed, trying OpenRouter:', (err as Error).message)\n    }\n  }\n  if (openrouterKey) {\n    return await callLLM(openrouterKey, OPENROUTER_URL, question, answer)\n  }\n  throw new Error('No LLM key — set DEEPINFRA_API_KEY or OPENROUTER_API_KEY')\n}\n\nexport function extractEpisodePrompts(brief: EpisodeBriefInput): ClipPromptPair {\n  if (!brief.clip1 || !brief.clip2 || !brief.voiceover || !brief.audioMood) {\n    throw new Error('Episode brief is missing prompt fields — run blog-to-video skill to regenerate')\n  }\n  return { clip1: brief.clip1, clip2: brief.clip2, voiceover: brief.voiceover, audioMood: brief.audioMood }\n}\n\nconst EPISODE_SYSTEM_PROMPT = `You are a video prompt engineer for Seedance 2.0 text-to-video AI.\n\nYou are working with a character-driven narrative brief. The clips must feel like a continuous 16-second moment in one person's story — not an explainer video.\n\nIMPORTANT TECHNICAL CONTEXT: Clip 2 will start from the exact last frame of Clip 1. Both clips must show the same person, same location, same lighting. Clip 2 continues motion from Clip 1's frozen ending frame.\n\nUse the Kling prompt format for every clip:\n[Shot type] of [character name] [action]. The scene starts at [keyframe 1]. Camera Motion: [motion] ending in [keyframe 2]. Lighting: [style]. Style: [style].\n\nShot types: Close-up, Medium shot, Over-the-shoulder, Wide shot, Tracking shot.\nCamera motions: Slow push in, Slow pull back, Rack focus, Static hold, Subtle drift right/left, Pan left/right.\n\nRules for visuals:\n- No text, no captions, no UI overlays, no readable code on screen\n- Photorealistic, specific objects, natural-feeling details\n- Keyframe 2 (clip 1's end) must be a specific held moment clip 2 continues from\n\nvoiceover: The character's internal tension, in 22 words maximum. Not a summary. Count every word.\naudioMood: Specific instruments + tempo + emotional register + \"no vocals, no lyrics\".\n\nRespond with JSON only: {\"clip1\": \"...\", \"clip2\": \"...\", \"voiceover\": \"...\", \"audioMood\": \"...\"}`\n\nasync function callEpisodeLLM(apiKey: string, baseUrl: string, brief: EpisodeBriefInput): Promise<ClipPromptPair> {\n  const userContent = `Character: ${brief.characterName}\nSection: ${brief.sectionTitle}\nStory moment: ${brief.storyMoment}\n\nExisting clip1 (refine if needed): ${brief.clip1}\nExisting clip2 (refine if needed): ${brief.clip2}\nExisting voiceover: ${brief.voiceover}\nExisting audioMood: ${brief.audioMood}\n\nProduce refined Kling-format prompts that follow all rules. Keep what's good, fix what violates the format.`\n\n  const res = await fetch(baseUrl, {\n    method:  'POST',\n    headers: { 'Authorization': `Bearer ${apiKey}`, 'Content-Type': 'application/json' },\n    body: JSON.stringify({\n      model:       QWEN_MODEL,\n      temperature: 0.5,\n      messages: [\n        { role: 'system', content: EPISODE_SYSTEM_PROMPT },\n        { role: 'user',   content: userContent },\n      ],\n    }),\n  })\n  if (!res.ok) throw new Error(`LLM episode call failed (${res.status}): ${await res.text()}`)\n\n  const data  = (await res.json()) as ChatResponse\n  const raw   = data.choices[0]?.message?.content?.trim() ?? ''\n  const match = raw.match(/\\{[\\s\\S]*\\}/)\n  if (!match) throw new Error(`No JSON in episode response: ${raw.slice(0, 200)}`)\n\n  const parsed = JSON.parse(match[0]) as Partial<ClipPromptPair>\n  if (!parsed.clip1 || !parsed.clip2 || !parsed.voiceover || !parsed.audioMood) {\n    throw new Error(`Episode response missing fields: ${raw.slice(0, 200)}`)\n  }\n  return parsed as ClipPromptPair\n}\n\nexport async function regenerateEpisodePrompts(brief: EpisodeBriefInput): Promise<ClipPromptPair> {\n  const deepinfraKey  = process.env['DEEPINFRA_API_KEY']\n  const openrouterKey = process.env['OPENROUTER_API_KEY']\n\n  if (deepinfraKey) {\n    try {\n      return await callEpisodeLLM(deepinfraKey, DEEPINFRA_URL, brief)\n    } catch (err) {\n      console.warn('[promptBuilder] DeepInfra failed, trying OpenRouter:', (err as Error).message)\n    }\n  }\n  if (openrouterKey) {\n    return await callEpisodeLLM(openrouterKey, OPENROUTER_URL, brief)\n  }\n  throw new Error('No LLM key — set DEEPINFRA_API_KEY or OPENROUTER_API_KEY')\n}\n","import { writeFileSync, mkdirSync } from 'node:fs'\nimport { join } from 'node:path'\nimport { tmpdir } from 'node:os'\nimport { fal } from '@fal-ai/client'\n\nconst MMAUDIO_MODEL    = 'fal-ai/mmaudio-v2'\nconst ELEVENLABS_MODEL = 'fal-ai/elevenlabs/tts'\nconst GEMINI_TTS_MODEL = 'fal-ai/google/gemini-2.5-flash-preview-tts'\n\ninterface VideoOutput { video: { url: string } }\ninterface TTSOutput   { audio: { url: string } }\n\nasync function downloadAudio(url: string): Promise<Buffer> {\n  const res = await fetch(url)\n  if (!res.ok) throw new Error(`Failed to download TTS audio (${res.status})`)\n  return Buffer.from(await res.arrayBuffer())\n}\n\nexport async function generateVoiceover(text: string): Promise<string> {\n  console.log('[AudioGenerator] Generating voiceover...')\n\n  const outDir  = join(tmpdir(), `tts-${Date.now()}`)\n  mkdirSync(outDir, { recursive: true })\n  const outPath = join(outDir, 'voiceover.mp3')\n\n  try {\n    const voiceId = process.env['ELEVENLABS_VOICE_ID'] ?? 'pNInz6obpgDQGcFmaJgB'\n    const result  = await fal.run(ELEVENLABS_MODEL, {\n      input: { text, voice_id: voiceId, model_id: 'eleven_v3' },\n    }) as unknown as TTSOutput\n    writeFileSync(outPath, await downloadAudio(result.audio.url))\n    console.log('[AudioGenerator] TTS: ElevenLabs via fal')\n    return outPath\n  } catch (err) {\n    console.warn('[AudioGenerator] ElevenLabs via fal failed, trying Gemini:', (err as Error).message)\n  }\n\n  const voice  = process.env['GEMINI_TTS_VOICE'] ?? 'Kore'\n  const result = await fal.run(GEMINI_TTS_MODEL, { input: { text, voice } }) as unknown as TTSOutput\n  writeFileSync(outPath, await downloadAudio(result.audio.url))\n  console.log('[AudioGenerator] TTS: Gemini via fal')\n  return outPath\n}\n\nexport async function addBackgroundAudio(videoUrl: string, mood: string, durationSeconds: number): Promise<string> {\n  console.log('[AudioGenerator] Adding background audio via MMAudio V2...')\n  const result = await fal.run(MMAUDIO_MODEL, {\n    input: {\n      video_url:       videoUrl,\n      prompt:          mood,\n      negative_prompt: 'speech, voice, talking, dialogue, narration, vocals, singing, human voice, conversation, words, lyrics, announcer, commentary',\n      duration:        durationSeconds,\n      cfg_strength:    4.5,\n    },\n  }) as unknown as VideoOutput\n  return result.video.url\n}\n","import { execSync } from 'node:child_process'\nimport { writeFileSync, mkdirSync } from 'node:fs'\nimport { join } from 'node:path'\nimport { fal } from '@fal-ai/client'\n\nasync function download(url: string, destPath: string): Promise<void> {\n  const res = await fetch(url)\n  if (!res.ok) throw new Error(`Download failed (${res.status}): ${url}`)\n  writeFileSync(destPath, Buffer.from(await res.arrayBuffer()))\n}\n\nexport async function concatenateClips(clip1Url: string, clip2Url: string, outDir: string): Promise<string> {\n  mkdirSync(outDir, { recursive: true })\n  const ts   = Date.now()\n  const p1   = join(outDir, `clip1-${ts}.mp4`)\n  const p2   = join(outDir, `clip2-${ts}.mp4`)\n  const out  = join(outDir, `combined-${ts}.mp4`)\n\n  console.log('[VideoMixer] Downloading clips...')\n  await Promise.all([download(clip1Url, p1), download(clip2Url, p2)])\n\n  console.log('[VideoMixer] Concatenating...')\n  execSync(\n    `ffmpeg -i \"${p1}\" -i \"${p2}\" -filter_complex \"[0:v][1:v]concat=n=2:v=1:a=0[v]\" -map \"[v]\" -y \"${out}\" -loglevel error`\n  )\n  return out\n}\n\nexport async function uploadToFal(localPath: string): Promise<string> {\n  const { readFileSync } = await import('node:fs')\n  const blob = new Blob([readFileSync(localPath)], { type: 'video/mp4' })\n  const url  = await fal.storage.upload(blob)\n  console.log('[VideoMixer] Uploaded to fal:', url)\n  return url\n}\n\nexport async function overlayVoiceover(\n  videoPath:     string,\n  voiceoverUrl:  string,\n  outDir:        string,\n): Promise<string> {\n  const ts  = Date.now()\n  const wav = join(outDir, `voiceover-${ts}.wav`)\n  const out = join(outDir, `final-${ts}.mp4`)\n\n  console.log('[VideoMixer] Downloading voiceover...')\n  await download(voiceoverUrl, wav)\n\n  console.log('[VideoMixer] Mixing voiceover over background audio...')\n  execSync(\n    `ffmpeg -i \"${videoPath}\" -i \"${wav}\" ` +\n    `-filter_complex \"[0:a]volume=0.2[bg];[1:a]volume=1.0[vo];[bg][vo]amix=inputs=2:duration=first[a]\" ` +\n    `-map 0:v -map \"[a]\" -c:v copy -y \"${out}\" -loglevel error`\n  )\n  return out\n}\n"],"mappings":"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","names":["import_node_crypto","import_zod","StealthPlugin","Kernel","playwrightChromium","s","organicResults","localPack","rawEntityIds","entityIds","aiSurfaces","locationEvidence","allOrganic","stats","fs","path","Papa","import_sdk","import_node_crypto","import_sdk","Kernel","proxyIdSuffix","errorText","errorText","Kernel","Kernel","createErrors","errorText","resolution","outcome","willRetry","import_node_child_process","import_node_fs","import_node_os","import_node_path","import_client","import_node_fs","import_node_path","result","import_node_fs","import_node_path","import_client","readFileSync"]}