{
  "version": 3,
  "sources": ["../src/index.ts", "../src/logger.ts", "../src/page-context.ts", "../src/version.ts", "../src/client.ts", "../src/voice-machine.ts", "../src/tool-name.ts", "../src/voice-transcript.ts", "../src/voice-session.ts", "../src/embed.ts", "../src/toolset.ts"],
  "sourcesContent": ["// Public types\n\nexport type { YakClientConfig } from \"./client.js\";\n// Public client API\nexport { YakClient } from \"./client.js\";\nexport type {\n  TriggerButtonConfig,\n  WidgetMode,\n  YakEmbedConfig,\n  YakEmbedState,\n  YakEmbedStateListener,\n} from \"./embed.js\";\n// Embed (DOM rendering layer \u2014 chat + voice)\nexport { YakEmbed } from \"./embed.js\";\n// Logging utilities\nexport { disableYakLogging, enableYakLogging, isYakLoggingEnabled, logger } from \"./logger.js\";\n// Client-side tool composition (one merged manifest + one routed onToolCall)\nexport type { YakServerAdapterConfig } from \"./toolset.js\";\nexport { createYakServerAdapter, createYakToolset } from \"./toolset.js\";\nexport * from \"./types/config.js\";\nexport * from \"./types/messaging.js\";\nexport * from \"./types/routes.js\";\nexport * from \"./types/tools.js\";\nexport type { EmbedProtocolVersion } from \"./version.js\";\n// Version\nexport { EMBED_PROTOCOL_VERSION } from \"./version.js\";\nexport type { VoiceEvent, VoiceMachine, VoiceState } from \"./voice-machine.js\";\nexport {\n  handleRealtimeMessage,\n  INITIAL_VOICE_MACHINE,\n  voiceReducer,\n} from \"./voice-machine.js\";\n// Voice session (composed by YakEmbed; exposed for advanced consumers)\nexport type { VoiceStateListener, YakVoiceSessionConfig } from \"./voice-session.js\";\nexport { YakVoiceSession } from \"./voice-session.js\";\n", "/**\n * Simple logger utility for the yak client SDK.\n * Debug/info logs are controlled by __YAK_LOGGING_ENABLED__.\n * Warnings and errors are always logged.\n *\n * Logging is controlled via:\n * 1. localStorage key \"yakLogging\" (persists across reloads)\n * 2. window.__YAK_LOGGING_ENABLED__ (runtime override)\n *\n * Use the helper functions to enable/disable:\n *   enableYakLogging()  - turns on logging, persists in localStorage\n *   disableYakLogging() - turns off logging, clears localStorage\n */\n\nconst STORAGE_KEY = \"yakLogging\";\n\nfunction isLoggingEnabled(): boolean {\n  if (typeof window === \"undefined\") {\n    return false;\n  }\n\n  // Window flag takes precedence (runtime override)\n  if (typeof window.__YAK_LOGGING_ENABLED__ === \"boolean\") {\n    return window.__YAK_LOGGING_ENABLED__;\n  }\n\n  // Fall back to localStorage for persistence across reloads\n  try {\n    return localStorage.getItem(STORAGE_KEY) === \"true\";\n  } catch {\n    // localStorage may be unavailable (e.g., private browsing)\n    return false;\n  }\n}\n\n/**\n * Check if Yak logging is currently enabled.\n * Useful for checking state before creating iframes.\n */\nexport function isYakLoggingEnabled(): boolean {\n  return isLoggingEnabled();\n}\n\n/**\n * Enable Yak debug logging.\n * Persists in localStorage so it survives page reloads.\n * Call this from browser console: `enableYakLogging()`\n */\nexport function enableYakLogging(): void {\n  if (typeof window === \"undefined\") {\n    return;\n  }\n  window.__YAK_LOGGING_ENABLED__ = true;\n  try {\n    localStorage.setItem(STORAGE_KEY, \"true\");\n  } catch {\n    // localStorage may be unavailable\n  }\n  console.info(\"[yak] Logging enabled\");\n}\n\n/**\n * Disable Yak debug logging.\n * Clears localStorage and window flag.\n * Call this from browser console: `disableYakLogging()`\n */\nexport function disableYakLogging(): void {\n  if (typeof window === \"undefined\") {\n    return;\n  }\n  window.__YAK_LOGGING_ENABLED__ = false;\n  try {\n    localStorage.removeItem(STORAGE_KEY);\n  } catch {\n    // localStorage may be unavailable\n  }\n  console.info(\"[yak] Logging disabled\");\n}\n\n// Expose helpers globally so they can be called from browser console\nif (typeof window !== \"undefined\") {\n  (\n    window as Window & {\n      enableYakLogging?: typeof enableYakLogging;\n      disableYakLogging?: typeof disableYakLogging;\n    }\n  ).enableYakLogging = enableYakLogging;\n  (\n    window as Window & {\n      enableYakLogging?: typeof enableYakLogging;\n      disableYakLogging?: typeof disableYakLogging;\n    }\n  ).disableYakLogging = disableYakLogging;\n}\n\nexport const logger = {\n  debug: (message: string, data?: unknown): void => {\n    if (isLoggingEnabled()) {\n      if (data !== undefined) {\n        console.log(`[yak-host] ${message}`, data);\n      } else {\n        console.log(`[yak-host] ${message}`);\n      }\n    }\n  },\n\n  info: (message: string, data?: unknown): void => {\n    if (isLoggingEnabled()) {\n      if (data !== undefined) {\n        console.info(`[yak-host] ${message}`, data);\n      } else {\n        console.info(`[yak-host] ${message}`);\n      }\n    }\n  },\n\n  warn: (message: string, data?: unknown): void => {\n    if (data !== undefined) {\n      console.warn(`[yak-host] ${message}`, data);\n    } else {\n      console.warn(`[yak-host] ${message}`);\n    }\n  },\n\n  error: (message: string, data?: unknown): void => {\n    if (data !== undefined) {\n      console.error(`[yak-host] ${message}`, data);\n    } else {\n      console.error(`[yak-host] ${message}`);\n    }\n  },\n};\n", "import type { PageContext } from \"./types/messaging.js\";\n\n/**\n * Extract visible text content from the DOM, filtering out non-content elements\n */\nfunction extractPageText(): string {\n  if (typeof document === \"undefined\") return \"\";\n\n  // Clone the body to avoid modifying the actual DOM\n  const bodyClone = document.body.cloneNode(true) as HTMLElement;\n\n  // Remove script, style, noscript tags and hidden elements\n  const unwantedSelectors = [\n    \"script\",\n    \"style\",\n    \"noscript\",\n    \"iframe\",\n    \"[style*='display: none']\",\n    \"[style*='display:none']\",\n    \"[hidden]\",\n    \".yak-chat-widget\",\n  ];\n\n  for (const selector of unwantedSelectors) {\n    const elements = bodyClone.querySelectorAll(selector);\n    for (const el of elements) {\n      el.remove();\n    }\n  }\n\n  // Get text content and clean it up\n  let text = bodyClone.textContent || bodyClone.innerText || \"\";\n\n  // Normalize whitespace: replace multiple spaces/newlines with single space\n  text = text.replace(/\\s+/g, \" \").trim();\n\n  // Limit to reasonable size (100KB max)\n  const maxLength = 100000;\n  if (text.length > maxLength) {\n    text = `${text.substring(0, maxLength)}... [truncated]`;\n  }\n\n  return text;\n}\n\n/**\n * Extract page context including URL, title, and visible text content\n */\nexport function extractPageContext(): PageContext {\n  if (typeof window === \"undefined\") {\n    return {\n      url: \"\",\n      title: \"\",\n      text: \"\",\n      timestamp: Date.now(),\n    };\n  }\n  return {\n    url: window.location.href,\n    title: document.title,\n    text: extractPageText(),\n    timestamp: Date.now(),\n  };\n}\n\n/**\n * Debounce function to limit how often a function is called\n */\nexport function debounce<T extends (...args: unknown[]) => void>(\n  func: T,\n  wait: number\n): (...args: Parameters<T>) => void {\n  let timeout: NodeJS.Timeout | null = null;\n\n  return function executedFunction(...args: Parameters<T>) {\n    const later = () => {\n      timeout = null;\n      func(...args);\n    };\n\n    if (timeout) {\n      clearTimeout(timeout);\n    }\n    timeout = setTimeout(later, wait);\n  };\n}\n", "/**\n * Embed protocol version.\n *\n * This version is used in the embed URL path (e.g., /embed/v1/[appId])\n * and in the message protocol to ensure compatibility between the\n * host packages (@yak-io/javascript, @yak-io/react, @yak-io/nextjs)\n * and the embedded chat UI.\n *\n * Increment this version when making breaking changes to:\n * - The postMessage protocol (IframeMessageFromHost, IframeMessageToHost)\n * - The tool manifest format\n * - The route manifest format\n * - The config payload structure\n *\n * Version history:\n * - v1: Initial versioned protocol\n */\nexport const EMBED_PROTOCOL_VERSION = \"1\" as const;\n\n/**\n * Type for the embed protocol version\n */\nexport type EmbedProtocolVersion = typeof EMBED_PROTOCOL_VERSION;\n", "import { isYakLoggingEnabled, logger } from \"./logger.js\";\nimport { debounce, extractPageContext } from \"./page-context.js\";\nimport type { ChatConfig } from \"./types/config.js\";\nimport type {\n  IframeMessageFromHost,\n  IframeMessageToHost,\n  Theme,\n  UserIdentity,\n} from \"./types/messaging.js\";\nimport type { ToolCallEventListener, ToolCallHandler } from \"./types/tools.js\";\nimport { EMBED_PROTOCOL_VERSION } from \"./version.js\";\n\n/** localStorage key for the per-app signed session token. */\nconst SESSION_STORAGE_KEY = (appId: string) => `yak:session:${appId}`;\n/** localStorage key for the per-app active-conversation pointer. */\nconst CONVERSATION_POINTER_KEY = (appId: string) => `yak:conversation:${appId}`;\n\n/** Read a stored session token for this app, if any. SSR-safe. */\nfunction readStoredSessionToken(appId: string): string | undefined {\n  if (typeof window === \"undefined\" || typeof window.localStorage === \"undefined\") return undefined;\n  try {\n    return window.localStorage.getItem(SESSION_STORAGE_KEY(appId)) ?? undefined;\n  } catch {\n    return undefined;\n  }\n}\n\nfunction writeStoredSessionToken(appId: string, token: string): void {\n  if (typeof window === \"undefined\" || typeof window.localStorage === \"undefined\") return;\n  try {\n    window.localStorage.setItem(SESSION_STORAGE_KEY(appId), token);\n  } catch {\n    // localStorage can throw in private-browsing modes; silently ignore.\n  }\n}\n\nfunction clearStoredSessionToken(appId: string): void {\n  if (typeof window === \"undefined\" || typeof window.localStorage === \"undefined\") return;\n  try {\n    window.localStorage.removeItem(SESSION_STORAGE_KEY(appId));\n  } catch {\n    // localStorage can throw in private-browsing modes; silently ignore.\n  }\n}\n\nfunction readStoredConversationPointer(appId: string): string | undefined {\n  if (typeof window === \"undefined\" || typeof window.localStorage === \"undefined\") return undefined;\n  try {\n    return window.localStorage.getItem(CONVERSATION_POINTER_KEY(appId)) ?? undefined;\n  } catch {\n    return undefined;\n  }\n}\n\nfunction writeStoredConversationPointer(appId: string, pointer: string): void {\n  if (typeof window === \"undefined\" || typeof window.localStorage === \"undefined\") return;\n  try {\n    window.localStorage.setItem(CONVERSATION_POINTER_KEY(appId), pointer);\n  } catch {\n    // localStorage can throw in private-browsing modes; silently ignore.\n  }\n}\n\nfunction clearStoredConversationPointer(appId: string): void {\n  if (typeof window === \"undefined\" || typeof window.localStorage === \"undefined\") return;\n  try {\n    window.localStorage.removeItem(CONVERSATION_POINTER_KEY(appId));\n  } catch {\n    // localStorage can throw in private-browsing modes; silently ignore.\n  }\n}\n\n/** Production chat origin \u2014 where the widget iframe loads from by default. */\nconst DEFAULT_CHAT_ORIGIN = \"https://chat.yak.io\";\n\n// Local development flag, set by yak's own apps to point the widget at a chat\n// UI running on localhost. `__YAK_LOGGING_ENABLED__` toggles verbose SDK logs.\ndeclare global {\n  interface Window {\n    __YAK_INTERNAL_DEV__?: boolean;\n    __YAK_LOGGING_ENABLED__?: boolean;\n  }\n}\n\n/**\n * Resolves the iframe origin when no explicit `origin` is configured. Points at\n * a local chat UI during yak's own local development; production otherwise.\n * Integrators override the result with the `origin` config option.\n */\nfunction getDefaultIframeOrigin(): string {\n  if (\n    typeof window !== \"undefined\" &&\n    (window.location.hostname === \"localhost\" || window.location.hostname === \"127.0.0.1\") &&\n    typeof window.__YAK_INTERNAL_DEV__ !== \"undefined\"\n  ) {\n    return \"http://localhost:3001\";\n  }\n  return DEFAULT_CHAT_ORIGIN;\n}\n\nexport interface YakClientConfig {\n  appId: string;\n  /**\n   * Override the origin the chat widget iframe loads from. Defaults to\n   * `https://chat.yak.io`. Most integrators never set this \u2014 it exists for\n   * non-production environments (e.g. a chat UI running on localhost).\n   */\n  origin?: string;\n  /**\n   * Handler for tool calls from the chat widget.\n   * The consuming platform decides how to execute (browser, server fetch, etc.)\n   *\n   * @example Browser-only execution\n   * ```ts\n   * onToolCall: async (name, args) => {\n   *   if (name === \"ui.scrollTo\") {\n   *     document.getElementById(args.id)?.scrollIntoView();\n   *     return { success: true };\n   *   }\n   *   throw new Error(`Unknown tool: ${name}`);\n   * }\n   * ```\n   *\n   * @example Server delegation\n   * ```ts\n   * onToolCall: async (name, args) => {\n   *   const res = await fetch(\"/api/yak/tools\", {\n   *     method: \"POST\",\n   *     body: JSON.stringify({ name, args }),\n   *   });\n   *   const data = await res.json();\n   *   if (!data.ok) throw new Error(data.error);\n   *   return data.result;\n   * }\n   * ```\n   */\n  onToolCall?: ToolCallHandler;\n  theme?: Theme;\n  chatConfig?: ChatConfig;\n  onRedirect?: (path: string) => void;\n  onClose?: () => void;\n  onReady?: () => void;\n  /**\n   * Called after every tool call completes (success or failure).\n   * Useful for page-level cache invalidation based on which tools were called.\n   *\n   * @example\n   * ```ts\n   * onToolCallComplete: (event) => {\n   *   if (event.ok && event.name.startsWith(\"order.\")) {\n   *     queryClient.invalidateQueries({ queryKey: [\"orders\"] });\n   *   }\n   * }\n   * ```\n   */\n  onToolCallComplete?: ToolCallEventListener;\n  /** Chat configuration options */\n  options?: {\n    /** Disable the restart session button in the header */\n    disableRestartButton?: boolean;\n    /**\n     * Stop sending any page context (URL, title, visible text) to the assistant.\n     * The widget still works; it just won't be aware of the current page, so it\n     * can't answer questions like \"what is this page?\". Use this when page content\n     * must not leave the customer's site for privacy reasons.\n     */\n    disablePageContent?: boolean;\n  };\n  /**\n   * Signed end-user identity. When supplied, the widget persists conversations\n   * server-side keyed to this user and surfaces a history pane. The `hash`\n   * must be HMAC-SHA256(apiSecret, id) computed on the integrator's backend \u2014\n   * never expose `apiSecret` to the browser.\n   *\n   * @example\n   * ```ts\n   * // Integrator backend (Node.js)\n   * const hash = crypto\n   *   .createHmac(\"sha256\", process.env.YAK_API_SECRET)\n   *   .update(currentUser.id)\n   *   .digest(\"hex\");\n   *\n   * // Browser\n   * new YakClient({\n   *   appId: \"app_abc\",\n   *   user: { id: currentUser.id, hash },\n   * });\n   * ```\n   */\n  user?: UserIdentity;\n}\n\nexport class YakClient {\n  private config: YakClientConfig;\n  private iframeWindow: Window | null = null;\n  private isWidgetOpen = false;\n  private readyTarget: { window: Window; origin: string } | null = null;\n  private unexpectedOriginLogged = false;\n  private lastUrl = \" \";\n  private debouncedSendContext: () => void;\n  private observer: MutationObserver | null = null;\n\n  constructor(config: YakClientConfig) {\n    this.config = config;\n    this.debouncedSendContext = debounce(() => {\n      logger.debug(\"DOM mutation detected, sending page context\");\n      this.sendPageContext();\n    }, 2000);\n  }\n\n  public updateConfig(newConfig: Partial<YakClientConfig>) {\n    // Proactively rotate the session when the host swaps end-users, so a token\n    // claimed by one user is never reused under another. The server enforces\n    // this too (a bound token is rejected for a different identity), but\n    // clearing here means the common case never hits that rejection.\n    //\n    // Only act when `newConfig` actually carries a `user` key \u2014 many callers\n    // pass unrelated partials (e.g. `{ chatConfig }`) that must not disturb the\n    // token. Clear on a change to a *different* identity or a logout; never on\n    // the anonymous\u2192identified first login, which must keep the session so the\n    // server can claim the visitor's pre-login history.\n    if (\"user\" in newConfig) {\n      const previousUserId = this.config.user?.id;\n      const nextUserId = newConfig.user?.id;\n      const changedToDifferentUser = previousUserId && previousUserId !== nextUserId;\n      if (changedToDifferentUser) {\n        clearStoredSessionToken(this.config.appId);\n        clearStoredConversationPointer(this.config.appId);\n      }\n    }\n\n    this.config = { ...this.config, ...newConfig };\n    // Resend config when the iframe is ready and we have anything to deliver \u2014\n    // tool/route manifests, or a user identity that needs to land in the\n    // widget so persistence/history light up.\n    if (this.readyTarget && (this.config.chatConfig || this.config.user)) {\n      this.sendConfigToIframe(this.readyTarget.window, this.readyTarget.origin);\n    }\n  }\n\n  /**\n   * Get the iframe origin URL (base URL for the chat widget). Recomputed on\n   * each call so environment-dependent defaults resolve correctly.\n   */\n  public getIframeOrigin(): string {\n    return this.config.origin ?? getDefaultIframeOrigin();\n  }\n\n  /**\n   * Get the full iframe embed URL for the chatbot\n   *\n   * @example\n   * ```ts\n   * const client = new YakClient({ appId: \"my-app\" });\n   * const iframeSrc = client.getEmbedUrl();\n   * // Returns: \"https://chat.yak.io/embed/v1/my-app\"\n   * ```\n   */\n  public getEmbedUrl(): string {\n    const origin = this.getIframeOrigin();\n    const baseUrl = `${origin}/embed/v${EMBED_PROTOCOL_VERSION}/${encodeURIComponent(this.config.appId)}`;\n\n    const params = new URLSearchParams();\n    const theme = this.config.theme;\n\n    if (theme?.colorMode && theme.colorMode !== \"system\") {\n      params.set(\"colorMode\", theme.colorMode);\n    }\n\n    this.appendThemeColors(params, theme?.light, \"light\");\n    this.appendThemeColors(params, theme?.dark, \"dark\");\n\n    // Pass debug flag to iframe so it can enable logging immediately\n    if (isYakLoggingEnabled()) {\n      params.set(\"yakDebug\", \"1\");\n    }\n\n    const queryString = params.toString();\n    return queryString ? `${baseUrl}?${queryString}` : baseUrl;\n  }\n\n  /**\n   * Append theme color parameters to a URLSearchParams object\n   */\n  private appendThemeColors(\n    params: URLSearchParams,\n    colors: Theme[\"light\"] | Theme[\"dark\"] | undefined,\n    prefix: \"light\" | \"dark\"\n  ): void {\n    if (!colors) return;\n    const map: [string, string | undefined][] = [\n      [`${prefix}Bg`, colors.background],\n      [`${prefix}Border`, colors.border],\n      [`${prefix}MessageBg`, colors.messageBackground],\n      [`${prefix}Placeholder`, colors.placeholderColor],\n      [`${prefix}SubmitBtn`, colors.submitButtonColor],\n      [`${prefix}SubmitBtnText`, colors.submitButtonTextColor],\n      [`${prefix}HeaderIcon`, colors.headerIconColor],\n    ];\n    for (const [key, value] of map) {\n      if (value) params.set(key, value);\n    }\n  }\n\n  /**\n   * Get the app ID\n   */\n  public getAppId(): string {\n    return this.config.appId;\n  }\n\n  /**\n   * Get the current theme configuration\n   */\n  public getTheme(): Theme | undefined {\n    return this.config.theme;\n  }\n\n  /**\n   * Send a prompt message to the chatbot iframe\n   * Note: The iframe must be ready to receive messages (onReady callback must have fired)\n   *\n   * @example\n   * ```ts\n   * const client = new YakClient({ appId: \"my-app\", onReady: () => {\n   *   client.sendPrompt(\"Help me with my order\");\n   * }});\n   * ```\n   */\n  public sendPrompt(prompt: string): void {\n    const target = this.getActiveTarget();\n    if (!target) {\n      logger.warn(\"Cannot send prompt: iframe not ready\");\n      return;\n    }\n\n    const message: IframeMessageFromHost = {\n      type: \"yak:prompt\",\n      payload: { prompt },\n    };\n    target.window.postMessage(message, target.origin);\n  }\n\n  /**\n   * Send a focus request to the chatbot iframe\n   * This will focus the chat input field\n   */\n  public sendFocus(): void {\n    const target = this.getActiveTarget();\n    if (!target) {\n      logger.warn(\"Cannot send focus: iframe not ready\");\n      return;\n    }\n\n    const message: IframeMessageFromHost = {\n      type: \"yak:focus\",\n    };\n    target.window.postMessage(message, target.origin);\n  }\n\n  /**\n   * The live postMessage target. Prefers `readyTarget` \u2014 the window that\n   * completed the `yak:ready` handshake \u2014 and falls back to the iframe's\n   * `contentWindow`. The two are normally identical, but they're populated by\n   * different events (the ready postMessage vs. the DOM `load` event) and can\n   * briefly diverge (e.g. a remount under React StrictMode, where `isReady`\n   * goes true before \u2014 or without \u2014 the load event repopulating\n   * `iframeWindow`). Trusting `readyTarget` keeps `sendFocus`/`sendPrompt`\n   * consistent with `isReady()` and avoids spurious \"iframe not ready\" warns.\n   */\n  private getActiveTarget(): { window: Window; origin: string } | null {\n    if (this.readyTarget) return this.readyTarget;\n    if (this.iframeWindow) return { window: this.iframeWindow, origin: this.getIframeOrigin() };\n    return null;\n  }\n\n  /**\n   * Check if the iframe is ready to receive messages\n   */\n  public isReady(): boolean {\n    return this.readyTarget !== null;\n  }\n\n  public setIframeWindow(window: Window | null) {\n    this.iframeWindow = window;\n    if (!window) {\n      this.readyTarget = null;\n    }\n  }\n\n  public setWidgetOpen(isOpen: boolean) {\n    this.isWidgetOpen = isOpen;\n    if (isOpen && this.readyTarget && this.config.chatConfig) {\n      this.sendConfigToIframe(this.readyTarget.window, this.readyTarget.origin);\n    }\n\n    if (isOpen) {\n      this.startObserving();\n    } else {\n      this.stopObserving();\n    }\n  }\n\n  public mount() {\n    if (typeof window !== \"undefined\") {\n      window.addEventListener(\"message\", this.handleMessage);\n      window.addEventListener(\"popstate\", this.handlePopState);\n    }\n  }\n\n  public unmount() {\n    if (typeof window !== \"undefined\") {\n      window.removeEventListener(\"message\", this.handleMessage);\n      window.removeEventListener(\"popstate\", this.handlePopState);\n    }\n    this.stopObserving();\n  }\n\n  private startObserving() {\n    // Gate on the same resolved target as sendPageContext \u2014 the observer resends\n    // context on DOM/URL changes, so it's pointless without a target, but it must\n    // not bail just because `iframeWindow` briefly trails `readyTarget`.\n    if (typeof window === \"undefined\" || !this.getActiveTarget()) return;\n    // No page context is sent when disabled, so there's nothing to observe for.\n    if (this.config.options?.disablePageContent) return;\n\n    // Initial check\n    const currentUrl = window.location.href;\n    if (currentUrl !== this.lastUrl) {\n      this.lastUrl = currentUrl;\n      logger.debug(\"URL changed, sending page context\");\n      this.sendPageContext();\n    }\n\n    if (this.observer) return;\n\n    this.observer = new MutationObserver((mutations) => {\n      const hasSubstantialChanges = mutations.some(\n        (mutation) =>\n          mutation.type === \"childList\" &&\n          (mutation.addedNodes.length > 0 || mutation.removedNodes.length > 0)\n      );\n\n      if (hasSubstantialChanges) {\n        this.debouncedSendContext();\n      }\n    });\n\n    this.observer.observe(document.body, {\n      childList: true,\n      subtree: true,\n    });\n  }\n\n  private stopObserving() {\n    if (this.observer) {\n      this.observer.disconnect();\n      this.observer = null;\n    }\n  }\n\n  private handlePopState = () => {\n    logger.debug(\"Navigation detected, sending page context\");\n    this.sendPageContext();\n  };\n\n  private handleMessage = (event: MessageEvent) => {\n    if (typeof window === \"undefined\") return;\n    if (!this.isWidgetOpen && event.data?.type !== \"yak:ready\") {\n      return;\n    }\n\n    const hostOrigin = window.location.origin;\n    const allowedOrigins = new Set<string>();\n    allowedOrigins.add(this.getIframeOrigin());\n    if (hostOrigin) {\n      allowedOrigins.add(hostOrigin);\n    }\n\n    if (!allowedOrigins.has(event.origin)) {\n      if (!this.unexpectedOriginLogged) {\n        logger.warn(\n          `Ignoring message from unexpected origin: ${event.origin}, allowed: ${Array.from(allowedOrigins).join(\", \")}`\n        );\n        this.unexpectedOriginLogged = true;\n      }\n      return;\n    }\n\n    // Validate message structure\n    if (!event.data || typeof event.data !== \"object\" || !(\"type\" in event.data)) {\n      // Filter out known browser extension messages\n      const data = event.data as Record<string, unknown>;\n      const isReactDevTools =\n        data?.source === \"react-devtools-content-script\" ||\n        data?.source === \"react-devtools-bridge\" ||\n        data?.source === \"react-devtools-inject-backend\";\n      const isReduxDevTools = data?.source === \"@devtools-page\";\n\n      if (isReactDevTools || isReduxDevTools) {\n        return;\n      }\n      return;\n    }\n\n    const message = event.data as IframeMessageToHost;\n    const targetWindow =\n      (event.source && \"postMessage\" in event.source ? (event.source as Window) : null) ??\n      this.iframeWindow;\n    const targetOrigin = this.getIframeOrigin();\n\n    logger.debug(\"Message received from iframe:\", message.type);\n\n    switch (message.type) {\n      case \"yak:ready\": {\n        logger.debug(\"Iframe ready, sending config\");\n\n        if (targetWindow) {\n          this.readyTarget = { window: targetWindow, origin: targetOrigin };\n          this.sendConfigToIframe(targetWindow, targetOrigin);\n\n          // Send initial page context after config\n          setTimeout(() => this.sendPageContext(), 100);\n\n          // Mark iframe as ready after config is sent\n          setTimeout(() => this.config.onReady?.(), 200);\n        } else {\n          logger.warn(\"Unable to send config: iframe window not registered yet\");\n        }\n        break;\n      }\n\n      case \"yak:tool_call\": {\n        const { id, name, args } = message.payload;\n        void this.handleToolCall(id, name, args);\n        break;\n      }\n\n      case \"yak:redirect\": {\n        const { path } = message.payload;\n        logger.debug(\"Redirect request received:\", path);\n\n        if (!this.isAllowedRedirect(path)) {\n          logger.warn(\"Blocked potentially unsafe redirect:\", path);\n          break;\n        }\n\n        if (this.config.onRedirect) {\n          this.config.onRedirect(path);\n        } else if (typeof window !== \"undefined\") {\n          window.location.assign(path);\n        }\n        break;\n      }\n\n      case \"yak:session\": {\n        const { sessionToken } = message.payload;\n        logger.debug(\"Session token received from iframe; persisting\");\n        writeStoredSessionToken(this.config.appId, sessionToken);\n        break;\n      }\n\n      case \"yak:conversation\": {\n        const { pointer } = message.payload;\n        if (pointer === null) {\n          logger.debug(\"Conversation pointer cleared by iframe\");\n          clearStoredConversationPointer(this.config.appId);\n        } else {\n          logger.debug(\"Conversation pointer received from iframe; persisting\");\n          writeStoredConversationPointer(this.config.appId, pointer);\n        }\n        break;\n      }\n\n      case \"yak:close\": {\n        logger.debug(\"Close message received from iframe\");\n        this.config.onClose?.();\n        break;\n      }\n\n      default:\n        logger.debug(\"Unknown message type:\", (message as { type: string }).type);\n        break;\n    }\n  };\n\n  private sendConfigToIframe(targetWindow: Window, targetOrigin: string) {\n    // Get logging enabled state from window flag\n    const loggingEnabled =\n      typeof window !== \"undefined\" && typeof window.__YAK_LOGGING_ENABLED__ === \"boolean\"\n        ? window.__YAK_LOGGING_ENABLED__\n        : undefined;\n\n    const storedSessionToken = readStoredSessionToken(this.config.appId);\n    const storedConversationPointer = readStoredConversationPointer(this.config.appId);\n\n    const configMessage: IframeMessageFromHost = {\n      type: \"yak:config\",\n      payload: {\n        version: EMBED_PROTOCOL_VERSION,\n        appId: this.config.appId,\n        theme: this.config.theme,\n        toolManifest: this.config.chatConfig?.tools ?? undefined,\n        routeManifest: this.config.chatConfig?.routes ?? undefined,\n        options: this.config.options,\n        loggingEnabled,\n        user: this.config.user,\n        sessionToken: storedSessionToken,\n        conversationPointer: storedConversationPointer,\n      },\n    };\n\n    logger.debug(\"Posting config to iframe origin:\", {\n      origin: targetOrigin,\n      version: EMBED_PROTOCOL_VERSION,\n      hasToolManifest: Boolean(this.config.chatConfig?.tools),\n      toolCount: this.config.chatConfig?.tools?.tools.length ?? 0,\n      hasRouteManifest: Boolean(this.config.chatConfig?.routes),\n    });\n\n    targetWindow.postMessage(configMessage, targetOrigin);\n  }\n\n  private sendPageContext() {\n    // Resolve the live target the way sendFocus/sendPrompt do. Relying on\n    // `iframeWindow` alone raced the initial send: it's populated by the iframe's\n    // DOM `load` event, which can lag the `yak:ready` postMessage (or not refire\n    // on a StrictMode remount), so the 100ms-after-ready send sometimes found it\n    // null and silently dropped the page context \u2014 while config, sent via the\n    // ready target, still arrived. `readyTarget` is set the instant ready lands.\n    const target = this.getActiveTarget();\n    if (!target) return;\n    // Privacy opt-out: never read or transmit page context for this app.\n    if (this.config.options?.disablePageContent) return;\n\n    try {\n      const pageContext = extractPageContext();\n      const message: IframeMessageFromHost = {\n        type: \"yak:page_context\",\n        payload: pageContext,\n      };\n      logger.debug(\"Sending page context to iframe:\", {\n        url: pageContext.url,\n        title: pageContext.title,\n        textLength: pageContext.text.length,\n      });\n      target.window.postMessage(message, target.origin);\n    } catch (error) {\n      logger.error(\"Error extracting page context:\", error);\n    }\n  }\n\n  private async handleToolCall(id: string, name: string, args: unknown): Promise<void> {\n    logger.debug(`Tool call received: ${name}`, { id, args });\n\n    if (!this.config.onToolCall) {\n      logger.error(\"Tool call received but no onToolCall handler configured\");\n      this.sendToolResultToIframe(id, false, undefined, \"No tool call handler configured\");\n      this.config.onToolCallComplete?.({\n        name,\n        args,\n        ok: false,\n        error: \"No tool call handler configured\",\n      });\n      return;\n    }\n\n    try {\n      const result = await this.config.onToolCall(name, args);\n      logger.debug(`Tool call succeeded: ${name}`, { id });\n      this.sendToolResultToIframe(id, true, result);\n      this.config.onToolCallComplete?.({ name, args, ok: true, result });\n    } catch (error) {\n      const errorMessage = this.extractErrorMessage(error);\n      // A thrown tool error is the documented way to surface a failure to the\n      // model (see the adapter `execute` contract) \u2014 it's captured, returned to\n      // the widget, and reported to the integrator via `onToolCallComplete`.\n      // That makes it expected data flow, not a console-worthy fault, so log it\n      // at debug to avoid noise in the host page's console.\n      logger.debug(`Tool call failed: ${name}`, { id, error });\n      this.sendToolResultToIframe(id, false, undefined, errorMessage);\n      this.config.onToolCallComplete?.({ name, args, ok: false, error: errorMessage });\n    }\n  }\n\n  private sendToolResultToIframe(id: string, ok: true, result: unknown): void;\n  private sendToolResultToIframe(id: string, ok: false, result: undefined, error: string): void;\n  private sendToolResultToIframe(id: string, ok: boolean, result: unknown, error?: string): void {\n    if (!this.iframeWindow) return;\n\n    if (ok) {\n      // Serialize result to remove non-cloneable values (functions, etc.)\n      // postMessage uses structured clone which cannot handle functions\n      const safeResult = this.toSerializable(result);\n      const message: IframeMessageFromHost = {\n        type: \"yak:tool_result\",\n        payload: { id, ok: true, result: safeResult },\n      };\n      this.iframeWindow.postMessage(message, this.getIframeOrigin());\n    } else {\n      const message: IframeMessageFromHost = {\n        type: \"yak:tool_result\",\n        payload: { id, ok: false, error: error ?? \"Unknown error\" },\n      };\n      this.iframeWindow.postMessage(message, this.getIframeOrigin());\n    }\n  }\n\n  /**\n   * Convert a value to a serializable form by stripping functions and other non-cloneable values.\n   * Uses JSON.parse(JSON.stringify()) which handles most cases.\n   */\n  private toSerializable(value: unknown): unknown {\n    if (value === undefined || value === null) {\n      return value;\n    }\n    try {\n      return JSON.parse(JSON.stringify(value));\n    } catch {\n      // If JSON serialization fails, return a string representation\n      logger.warn(\"Failed to serialize tool result, returning string representation\");\n      return String(value);\n    }\n  }\n\n  private extractErrorMessage(error: unknown): string {\n    if (error instanceof Error) {\n      return error.message;\n    }\n    if (typeof error === \"string\") {\n      return error;\n    }\n    return \"Unknown error\";\n  }\n\n  /**\n   * Validates that a redirect path is safe (relative path or same-origin).\n   * Blocks absolute URLs to external domains to prevent open redirect attacks.\n   */\n  private isAllowedRedirect(path: string): boolean {\n    // Allow relative paths that don't start with // (protocol-relative URLs)\n    if (path.startsWith(\"/\") && !path.startsWith(\"//\")) {\n      return true;\n    }\n    // Allow hash-only and query-only paths\n    if (path.startsWith(\"#\") || path.startsWith(\"?\")) {\n      return true;\n    }\n    // For absolute URLs, verify same origin\n    if (typeof window !== \"undefined\") {\n      try {\n        const url = new URL(path, window.location.origin);\n        return url.origin === window.location.origin;\n      } catch {\n        // Invalid URL - block it\n        return false;\n      }\n    }\n    // In non-browser environments, only allow relative paths\n    return false;\n  }\n}\n", "/**\n * Pure state machine for a single voice session.\n *\n * The reducer has no DOM or WebRTC dependencies \u2014 it can be unit-tested by\n * driving events through `voiceReducer` and checking the resulting state.\n *\n * The companion `handleRealtimeMessage` parses an OpenAI Realtime data-channel\n * message and dispatches reducer events plus side effects (tool dispatch,\n * sending follow-up events back over the data channel). Side effects are\n * delegated to the injected `RealtimeMessageContext` so the function is\n * testable with a plain in-memory mock.\n */\n\n/**\n * Lifecycle of a voice session, in order:\n * - `idle` \u2014 no session (the starting and stopped state).\n * - `connecting` \u2014 establishing the WebRTC connection.\n * - `listening` \u2014 connected and capturing the user's speech.\n * - `thinking` \u2014 the model is processing / generating a response.\n * - `speaking` \u2014 the assistant is playing audio back.\n * - `error` \u2014 the session failed; see {@link VoiceMachine.errorMessage}.\n */\nexport type VoiceState = \"idle\" | \"connecting\" | \"listening\" | \"thinking\" | \"speaking\" | \"error\";\n\nexport type VoiceEvent =\n  | { type: \"start\" }\n  | { type: \"connected\" }\n  /** An assistant-initiated turn was requested (e.g. the opening greeting). */\n  | { type: \"response_requested\" }\n  | { type: \"speech_started\" }\n  | { type: \"speech_stopped\" }\n  | { type: \"audio_delta\" }\n  | { type: \"audio_stopped\" }\n  | { type: \"stop\" }\n  | { type: \"error\"; message: string };\n\n/** Snapshot of a voice session's state machine. */\nexport interface VoiceMachine {\n  /** Current lifecycle state of the session. */\n  state: VoiceState;\n  /** Human-readable failure reason \u2014 set only when `state === \"error\"`. */\n  errorMessage?: string;\n}\n\nexport const INITIAL_VOICE_MACHINE: VoiceMachine = { state: \"idle\" };\n\nexport function voiceReducer(machine: VoiceMachine, event: VoiceEvent): VoiceMachine {\n  switch (event.type) {\n    case \"start\":\n      return machine.state === \"idle\" ? { state: \"connecting\" } : machine;\n    case \"connected\":\n      return machine.state === \"connecting\" ? { state: \"listening\" } : machine;\n    case \"response_requested\":\n      // The assistant is generating an unprompted turn (the opening greeting).\n      // Move to `thinking` so the subsequent `audio_delta` lands on `speaking`,\n      // matching a normal turn; no-op if we're not idling in `listening`.\n      return machine.state === \"listening\" ? { state: \"thinking\" } : machine;\n    case \"speech_started\":\n      if (machine.state === \"idle\" || machine.state === \"error\") return machine;\n      return { state: \"listening\" };\n    case \"speech_stopped\":\n      return machine.state === \"listening\" ? { state: \"thinking\" } : machine;\n    case \"audio_delta\":\n      if (machine.state === \"thinking\" || machine.state === \"speaking\") {\n        return { state: \"speaking\" };\n      }\n      return machine;\n    case \"audio_stopped\":\n      return machine.state === \"speaking\" ? { state: \"listening\" } : machine;\n    case \"stop\":\n      return { state: \"idle\" };\n    case \"error\":\n      return { state: \"error\", errorMessage: event.message };\n    default: {\n      const _exhaustive: never = event;\n      void _exhaustive;\n      return machine;\n    }\n  }\n}\n\n/**\n * Per-`response.done` token usage emitted by OpenAI's Realtime API.\n * The SDK accumulates these across the session and ships the totals to the\n * `session-event` stop endpoint so billing has the dimensions it needs.\n */\nexport interface RealtimeResponseUsage {\n  /** Total input tokens for this response (text + audio combined). */\n  inputTokens?: number;\n  /** Cached input tokens \u2014 subset of `inputTokens`. */\n  cachedInputTokens?: number;\n  /** Total output tokens for this response (text + audio combined). */\n  outputTokens?: number;\n  /** Audio-input tokens (subset of `inputTokens`). */\n  audioInputTokens?: number;\n  /** Audio-output tokens (subset of `outputTokens`). */\n  audioOutputTokens?: number;\n  /** Text-input tokens (subset of `inputTokens`). */\n  textInputTokens?: number;\n  /** Text-output tokens (subset of `outputTokens`). */\n  textOutputTokens?: number;\n}\n\n/**\n * Transcript-side events the session uses to persist a voice conversation in\n * the same shape as text chat. Distinct from the state-machine `VoiceEvent`s:\n * these carry text + ordering, not lifecycle state.\n *\n * - `user_turn` \u2014 a user utterance just finished (VAD `speech_stopped`). Emitted\n *   *before* the user's transcript completes (which is async and can land after\n *   the assistant has already replied), so the session reserves an ordered slot\n *   here and back-fills the text when the `transcript` event arrives.\n * - `user_transcript_failed` \u2014 input transcription failed for an utterance; the\n *   reserved slot must be voided so it doesn't block later turns.\n * - `transcript` \u2014 a settled user/assistant transcript, keyed by the OpenAI\n *   conversation `item_id` (stable; used to derive the message id + dedupe).\n */\nexport type VoiceTranscriptEvent =\n  | { kind: \"user_turn\" }\n  | { kind: \"user_transcript_failed\" }\n  | { kind: \"transcript\"; itemId: string; role: \"user\" | \"assistant\"; text: string };\n\nexport interface RealtimeMessageContext {\n  send: (event: VoiceEvent) => void;\n  sendData: (payload: unknown) => void;\n  dispatchToolCall: (name: string, args: unknown) => Promise<unknown>;\n  isDispatched: (callId: string) => boolean;\n  markDispatched: (callId: string) => void;\n  /** Forward a per-response usage payload to the session for accumulation. */\n  recordUsage?: (usage: RealtimeResponseUsage) => void;\n  /** Forward transcript/ordering events so the session can persist the conversation. */\n  onTranscriptEvent?: (event: VoiceTranscriptEvent) => void;\n}\n\ninterface RealtimeFunctionCallItem {\n  type: \"function_call\";\n  call_id?: string;\n  name?: string;\n  arguments?: string;\n}\n\ninterface RealtimeResponseDoneItem {\n  type?: string;\n  call_id?: string;\n  name?: string;\n  arguments?: string;\n}\n\ninterface RealtimeUsageTokenDetails {\n  cached_tokens?: number;\n  audio_tokens?: number;\n  text_tokens?: number;\n}\n\ninterface RealtimeUsage {\n  input_tokens?: number;\n  output_tokens?: number;\n  total_tokens?: number;\n  input_token_details?: RealtimeUsageTokenDetails;\n  output_token_details?: RealtimeUsageTokenDetails;\n}\n\ninterface RealtimeResponseDone {\n  output?: RealtimeResponseDoneItem[];\n  usage?: RealtimeUsage;\n}\n\ninterface RealtimeMessage {\n  type?: string;\n  response?: RealtimeResponseDone;\n  error?: { message?: string };\n  /** Conversation item id \u2014 present on transcript-completion messages. */\n  item_id?: string;\n  /** Final transcript text \u2014 present on `*.completed` / `*.transcript.done`. */\n  transcript?: string;\n}\n\n/** Emit a settled transcript turn, skipping empties. Best-effort; never throws. */\nfunction emitTranscript(\n  ctx: RealtimeMessageContext,\n  role: \"user\" | \"assistant\",\n  itemId: string | undefined,\n  transcript: string | undefined\n): void {\n  if (!ctx.onTranscriptEvent) return;\n  const text = (transcript ?? \"\").trim();\n  if (!itemId || !text) return;\n  try {\n    ctx.onTranscriptEvent({ kind: \"transcript\", itemId, role, text });\n  } catch {\n    // Persistence wiring is best-effort; never let it break the session loop.\n  }\n}\n\nfunction isFunctionCall(item: RealtimeResponseDoneItem): item is RealtimeFunctionCallItem {\n  return item.type === \"function_call\";\n}\n\nfunction parseToolArgs(raw: string | undefined): unknown {\n  if (!raw) return {};\n  try {\n    return JSON.parse(raw);\n  } catch {\n    return {};\n  }\n}\n\nasync function dispatchFunctionCall(\n  call: RealtimeFunctionCallItem,\n  ctx: RealtimeMessageContext\n): Promise<void> {\n  const callId = call.call_id;\n  const name = call.name;\n  if (!callId || !name) return;\n  if (ctx.isDispatched(callId)) return;\n  ctx.markDispatched(callId);\n\n  const args = parseToolArgs(call.arguments);\n\n  let output: string;\n  try {\n    const result = await ctx.dispatchToolCall(name, args);\n    output = JSON.stringify(result ?? null);\n  } catch (error) {\n    output = JSON.stringify({\n      error: error instanceof Error ? error.message : \"Tool execution failed\",\n    });\n  }\n\n  ctx.sendData({\n    type: \"conversation.item.create\",\n    item: { type: \"function_call_output\", call_id: callId, output },\n  });\n  ctx.sendData({ type: \"response.create\" });\n}\n\nfunction extractUsage(raw: RealtimeUsage | undefined): RealtimeResponseUsage | null {\n  if (!raw) return null;\n  const usage: RealtimeResponseUsage = {};\n  if (typeof raw.input_tokens === \"number\") usage.inputTokens = raw.input_tokens;\n  if (typeof raw.output_tokens === \"number\") usage.outputTokens = raw.output_tokens;\n  const inDetails = raw.input_token_details;\n  if (inDetails) {\n    if (typeof inDetails.cached_tokens === \"number\") {\n      usage.cachedInputTokens = inDetails.cached_tokens;\n    }\n    if (typeof inDetails.audio_tokens === \"number\") {\n      usage.audioInputTokens = inDetails.audio_tokens;\n    }\n    if (typeof inDetails.text_tokens === \"number\") {\n      usage.textInputTokens = inDetails.text_tokens;\n    }\n  }\n  const outDetails = raw.output_token_details;\n  if (outDetails) {\n    if (typeof outDetails.audio_tokens === \"number\") {\n      usage.audioOutputTokens = outDetails.audio_tokens;\n    }\n    if (typeof outDetails.text_tokens === \"number\") {\n      usage.textOutputTokens = outDetails.text_tokens;\n    }\n  }\n  return Object.keys(usage).length > 0 ? usage : null;\n}\n\nasync function handleResponseDone(\n  response: RealtimeResponseDone | undefined,\n  ctx: RealtimeMessageContext\n): Promise<void> {\n  const usage = extractUsage(response?.usage);\n  if (usage && ctx.recordUsage) {\n    try {\n      ctx.recordUsage(usage);\n    } catch {\n      // recordUsage is best-effort; never let it break the session loop.\n    }\n  }\n\n  const calls = (response?.output ?? []).filter(isFunctionCall);\n  for (const call of calls) {\n    await dispatchFunctionCall(call, ctx);\n  }\n}\n\nexport async function handleRealtimeMessage(\n  raw: string,\n  ctx: RealtimeMessageContext\n): Promise<void> {\n  let message: RealtimeMessage;\n  try {\n    message = JSON.parse(raw) as RealtimeMessage;\n  } catch {\n    return;\n  }\n\n  switch (message.type) {\n    case \"input_audio_buffer.speech_started\":\n      ctx.send({ type: \"speech_started\" });\n      return;\n    case \"input_audio_buffer.speech_stopped\":\n      ctx.send({ type: \"speech_stopped\" });\n      // Reserve an ordered slot for this user turn now; its transcript completes\n      // asynchronously and may land after the assistant has already replied.\n      ctx.onTranscriptEvent?.({ kind: \"user_turn\" });\n      return;\n    case \"response.output_audio_transcript.delta\":\n    case \"response.audio_transcript.delta\":\n      ctx.send({ type: \"audio_delta\" });\n      return;\n    case \"conversation.item.input_audio_transcription.completed\":\n      emitTranscript(ctx, \"user\", message.item_id, message.transcript);\n      return;\n    case \"conversation.item.input_audio_transcription.failed\":\n      ctx.onTranscriptEvent?.({ kind: \"user_transcript_failed\" });\n      return;\n    case \"response.output_audio_transcript.done\":\n    case \"response.audio_transcript.done\":\n      emitTranscript(ctx, \"assistant\", message.item_id, message.transcript);\n      return;\n    case \"output_audio_buffer.stopped\":\n    case \"response.output_audio_buffer.stopped\":\n      ctx.send({ type: \"audio_stopped\" });\n      return;\n    case \"response.done\":\n      await handleResponseDone(message.response, ctx);\n      return;\n    case \"error\":\n      ctx.send({\n        type: \"error\",\n        message: message.error?.message ?? \"Voice session error\",\n      });\n      return;\n    default:\n      return;\n  }\n}\n", "/** OpenAI/Realtime function names must match `^[a-zA-Z0-9_-]{1,64}$`. */\nconst MAX_TOOL_NAME_LENGTH = 64;\n\n/**\n * Convert a host tool name into a model-facing function name.\n *\n * We sanitise to the allowed function-name charset (rather than hashing to an opaque\n * `yt_<hex>`), so the model keeps the semantic signal of a readable name \u2014 e.g.\n * `orders.list` \u2192 `orders_list`.\n *\n * This is a pure per-name transform; uniqueness across a manifest (and avoiding the\n * reserved `redirect` name / `mcp__` namespace) is handled by the caller via\n * {@link uniqueToolId}. Each runtime (the chat-ui iframe and this SDK's voice session)\n * decorates and reverse-maps with its own manifest, so the ids never need to match\n * across paths \u2014 only to be self-consistent within one.\n */\nexport function generateToolId(originalName: string): string {\n  const sanitized = originalName.replace(/[^A-Za-z0-9_-]/g, \"_\").slice(0, MAX_TOOL_NAME_LENGTH);\n  return sanitized.length > 0 ? sanitized : \"tool\";\n}\n\n/** The MCP namespace prefix the voice/chat dispatchers route on. */\nconst MCP_NAMESPACE_PREFIX = \"mcp__\";\n\n/**\n * Resolve a collision-free, model-safe id for a host tool name, given the ids already\n * taken in this manifest (seed `used` with reserved names like `redirect`). Host ids must\n * never start with the `mcp__` namespace, which the dispatcher routes on.\n */\nexport function uniqueToolId(originalName: string, used: Set<string>): string {\n  let base = generateToolId(originalName);\n  if (base.startsWith(MCP_NAMESPACE_PREFIX)) {\n    base = `t_${base}`.slice(0, MAX_TOOL_NAME_LENGTH);\n  }\n  if (!used.has(base)) return base;\n\n  for (let i = 2; i < 1000; i++) {\n    const suffix = `_${i}`;\n    const candidate = `${base.slice(0, MAX_TOOL_NAME_LENGTH - suffix.length)}${suffix}`;\n    if (!used.has(candidate)) return candidate;\n  }\n  // Pathological fallback \u2014 vanishingly unlikely in practice.\n  return `${base.slice(0, 56)}_${used.size}`;\n}\n", "/** A message appended to the conversation. `ts` is the turn's boundary timestamp\n * (unix ms), captured when the turn happened \u2014 the server mints a K-sortable id\n * from it, so order is fixed independently of when the transcript text arrives. */\nexport interface VoiceTranscriptMessage {\n  /** OpenAI conversation item id \u2014 the idempotency seed for the minted message id. */\n  itemId: string;\n  /** Turn boundary time (unix ms). Sorts the message; trusted within margin. */\n  ts: number;\n  role: \"user\" | \"assistant\";\n  parts: Array<{ type: \"text\"; text: string }>;\n}\n\n/**\n * Turns out-of-order Realtime transcript events into an **append-only** stream of\n * independently-ordered messages.\n *\n * User input transcription is asynchronous and can complete *after* the assistant\n * has already replied, so a user turn reserves its boundary timestamp at the VAD\n * speech boundary (`userTurn(ts)`) \u2014 that time sorts it before the assistant\n * reply even though its text lands later. Each settled transcript becomes one\n * message; there is no buffer holding the whole conversation and no\n * contiguous-prefix gate, so a missing or empty transcript drops only that one\n * message, never the rest.\n *\n * Messages stay in `unacked` (keyed by `itemId`) until the server confirms them;\n * that set doubles as the retry queue (failed POST), the defer queue (a greeting\n * that arrives before any user turn), and the `pagehide` beacon payload. It holds\n * only in-flight messages \u2014 normally zero or one \u2014 never history.\n */\nexport class VoiceTranscriptSequencer {\n  /** Reserved boundary timestamps for user turns whose transcript hasn't arrived (FIFO). */\n  private pendingUserTs: number[] = [];\n  /** Item ids already recorded, to drop duplicate transcript-completion events. */\n  private seenItemIds = new Set<string>();\n  /** Messages awaiting server confirmation, keyed by itemId. */\n  private unacked = new Map<string, VoiceTranscriptMessage>();\n\n  /** Reserve the boundary timestamp for a user turn (its transcript arrives later). */\n  userTurn(ts: number): void {\n    this.pendingUserTs.push(ts);\n  }\n\n  /** Release the oldest reserved user boundary when its input transcription failed. */\n  userTranscriptFailed(): void {\n    this.pendingUserTs.shift();\n  }\n\n  /**\n   * Record a settled transcript as a message and queue it for sending. Returns the\n   * message, or null if it was a duplicate or had no text. A user message takes its\n   * reserved boundary timestamp (so it sorts before the assistant reply); anything\n   * else takes `ts` (the moment it settled).\n   */\n  record(\n    itemId: string,\n    role: \"user\" | \"assistant\",\n    text: string,\n    ts: number\n  ): VoiceTranscriptMessage | null {\n    if (!itemId || !text.trim()) return null;\n    if (this.seenItemIds.has(itemId)) return null;\n    this.seenItemIds.add(itemId);\n    const boundaryTs =\n      role === \"user\" && this.pendingUserTs.length > 0\n        ? (this.pendingUserTs.shift() as number)\n        : ts;\n    const message: VoiceTranscriptMessage = {\n      itemId,\n      ts: boundaryTs,\n      role,\n      parts: [{ type: \"text\", text }],\n    };\n    this.unacked.set(itemId, message);\n    return message;\n  }\n\n  /** Messages awaiting confirmation, oldest first \u2014 the batch to send or beacon. */\n  pending(): VoiceTranscriptMessage[] {\n    return [...this.unacked.values()].sort((a, b) => a.ts - b.ts);\n  }\n\n  /** Drop the given item ids once the server confirms them. */\n  ack(itemIds: string[]): void {\n    for (const itemId of itemIds) this.unacked.delete(itemId);\n  }\n}\n", "import { logger } from \"./logger.js\";\nimport { extractPageContext } from \"./page-context.js\";\nimport { uniqueToolId } from \"./tool-name.js\";\nimport type { ChatConfig, ChatConfigProvider } from \"./types/config.js\";\nimport type { PageContext, UserIdentity } from \"./types/messaging.js\";\nimport type { ToolCallHandler, ToolDefinition } from \"./types/tools.js\";\nimport {\n  handleRealtimeMessage,\n  INITIAL_VOICE_MACHINE,\n  type RealtimeMessageContext,\n  type RealtimeResponseUsage,\n  type VoiceMachine,\n  type VoiceTranscriptEvent,\n  voiceReducer,\n} from \"./voice-machine.js\";\nimport { type VoiceTranscriptMessage, VoiceTranscriptSequencer } from \"./voice-transcript.js\";\n\nconst DEFAULT_REALTIME_MODEL = \"gpt-realtime\";\nconst REALTIME_CALLS_URL = \"https://api.openai.com/v1/realtime/calls\";\nconst DEFAULT_API_ORIGIN = \"https://chat.yak.io\";\n\n// Local development flag, set by yak's own apps to point voice sessions at a\n// chat UI running on localhost.\ndeclare global {\n  interface Window {\n    __YAK_INTERNAL_DEV__?: boolean;\n  }\n}\n\n/**\n * Resolves the API origin when no explicit `apiOrigin` is configured. Points at\n * a local chat UI during yak's own local development; production otherwise.\n */\nfunction getDefaultApiOrigin(): string {\n  if (\n    typeof window !== \"undefined\" &&\n    (window.location.hostname === \"localhost\" || window.location.hostname === \"127.0.0.1\") &&\n    typeof window.__YAK_INTERNAL_DEV__ !== \"undefined\"\n  ) {\n    return \"http://localhost:3001\";\n  }\n  return DEFAULT_API_ORIGIN;\n}\n\nexport type VoiceStateListener = (machine: VoiceMachine) => void;\n\nexport interface YakVoiceSessionConfig {\n  appId: string;\n  /** Tool call handler. Same shape as `YakClientConfig.onToolCall`. */\n  onToolCall?: ToolCallHandler;\n  onRedirect?: (path: string) => void;\n  /**\n   * Static chat config (routes + tools). Sent to the mint endpoint so the LLM\n   * knows what tools are available. Use this OR `getConfig`.\n   */\n  chatConfig?: ChatConfig;\n  /**\n   * Async provider for chat config. Called on every session start \u2014 useful when\n   * tools/routes depend on the current page or user. Takes precedence over\n   * `chatConfig` if both are provided.\n   */\n  getConfig?: ChatConfigProvider;\n  /**\n   * Override the API origin (where voice sessions are minted). Defaults to\n   * `https://chat.yak.io`. Most integrators never set this.\n   */\n  apiOrigin?: string;\n  /**\n   * Privacy opt-out: stop sending any page context (URL, title, visible text)\n   * with the voice session. The session still works; the assistant just won't\n   * be aware of the current page.\n   */\n  disablePageContent?: boolean;\n  /**\n   * Signed end-user identity. Same shape and contract as `YakClientConfig.user`:\n   * when supplied, the voice session persists its transcript server-side keyed\n   * to this user (so insights + memory work) and the model recalls the user's\n   * memory at session start. The `hash` must be HMAC-SHA256(apiSecret, id)\n   * computed on the integrator's backend \u2014 never expose `apiSecret` to the\n   * browser.\n   */\n  user?: UserIdentity;\n}\n\ninterface MintResponse {\n  clientSecret: string;\n  expiresAt: number;\n  voiceSessionId: string;\n  /**\n   * Whether to auto-greet on connect. Driven by the app's voice intro setting:\n   * `false` only when the operator chose \"no intro\". A missing flag (older\n   * server) is treated as `true` so existing apps keep greeting.\n   */\n  autoGreet?: boolean;\n}\n\ninterface SessionResources {\n  pc: RTCPeerConnection | null;\n  dataChannel: RTCDataChannel | null;\n  micStream: MediaStream | null;\n  audioElement: HTMLAudioElement | null;\n  voiceSessionId: string | null;\n}\n\nconst EMPTY_RESOURCES: SessionResources = {\n  pc: null,\n  dataChannel: null,\n  micStream: null,\n  audioElement: null,\n  voiceSessionId: null,\n};\n\n/**\n * Manages a single voice session against the OpenAI Realtime API.\n *\n * Runs entirely on the host page \u2014 no iframe. The mint endpoint\n * (`POST /api/voice/realtime-token`) is called cross-origin to chat.yak.io;\n * origin validation happens server-side via the app's `allowedOrigins`.\n *\n * Tool calls are dispatched to the configured handlers \u2014 the same ones\n * customers wire up for `YakEmbed`. The class manages WebRTC lifecycle,\n * full teardown on stop, and `pagehide` cleanup via `navigator.sendBeacon`.\n */\ninterface AccumulatedUsage {\n  inputTokens: number;\n  cachedInputTokens: number;\n  outputTokens: number;\n  audioInputTokens: number;\n  audioOutputTokens: number;\n  textInputTokens: number;\n  textOutputTokens: number;\n  responseCount: number;\n}\n\nfunction emptyUsage(): AccumulatedUsage {\n  return {\n    inputTokens: 0,\n    cachedInputTokens: 0,\n    outputTokens: 0,\n    audioInputTokens: 0,\n    audioOutputTokens: 0,\n    textInputTokens: 0,\n    textOutputTokens: 0,\n    responseCount: 0,\n  };\n}\n\nexport class YakVoiceSession {\n  private config: YakVoiceSessionConfig;\n  private machine: VoiceMachine = INITIAL_VOICE_MACHINE;\n  private resources: SessionResources = EMPTY_RESOURCES;\n  private dispatchedCallIds = new Set<string>();\n  private listeners = new Set<VoiceStateListener>();\n  private pageHideHandler: (() => void) | null = null;\n  /** Per-session token totals, accumulated from each `response.done` event. */\n  private usage: AccumulatedUsage = emptyUsage();\n  /**\n   * Reverse map: hashed tool id (what OpenAI calls back with) \u2192 original host\n   * tool name (what `onToolCall` expects). Populated on every `start()` from\n   * the resolved chat config.\n   */\n  private toolNameById = new Map<string, string>();\n\n  // \u2500\u2500 Transcript persistence \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n  /** Assigns each settled transcript a stable order key and tracks unacked sends. */\n  private transcript = new VoiceTranscriptSequencer();\n  /** Latched once the server says this session won't persist (storage off / no row). */\n  private persistenceOff = false;\n  /** At most one persist POST in flight; a newer flush coalesces behind it. */\n  private persistInFlight: Promise<void> | null = null;\n  private persistAgain = false;\n  /**\n   * The voice session id used for transcript persistence. Mirrors\n   * `resources.voiceSessionId` but is kept separate so a final flush during\n   * `teardown()` (which clears `resources`) can still address the session.\n   */\n  private persistSessionId: string | null = null;\n\n  constructor(config: YakVoiceSessionConfig) {\n    this.config = config;\n    this.attachPageHide();\n  }\n\n  /**\n   * Resolve the API origin lazily on each call. Environment-dependent defaults\n   * (e.g. a local chat UI) may not be ready at construction time, so resolving\n   * eagerly would risk baking in the production URL.\n   */\n  private get apiOrigin(): string {\n    return this.config.apiOrigin ?? getDefaultApiOrigin();\n  }\n\n  /** Update mutable config fields (handlers, getConfig). */\n  public updateConfig(patch: Partial<YakVoiceSessionConfig>): void {\n    this.config = { ...this.config, ...patch };\n  }\n\n  public getState(): VoiceMachine {\n    return this.machine;\n  }\n\n  /**\n   * The current API origin (defaults to `https://chat.yak.io`). Useful for\n   * building URLs to static assets like the brand logo.\n   */\n  public getApiOrigin(): string {\n    return this.apiOrigin;\n  }\n\n  public onStateChange(listener: VoiceStateListener): () => void {\n    this.listeners.add(listener);\n    return () => {\n      this.listeners.delete(listener);\n    };\n  }\n\n  /**\n   * Begin a voice session. Should be invoked from a user gesture (button\n   * click) so `getUserMedia` and audio playback both have transient activation.\n   */\n  public async start(): Promise<void> {\n    if (this.machine.state !== \"idle\") return;\n    logger.debug(\"Voice: start() called\");\n    this.usage = emptyUsage();\n    this.resetTranscriptState();\n    this.dispatch({ type: \"start\" });\n\n    let chatConfig: ChatConfig | undefined = this.config.chatConfig;\n    if (this.config.getConfig) {\n      try {\n        chatConfig = await this.config.getConfig();\n        logger.debug(\"Voice: getConfig() resolved\", {\n          toolCount: chatConfig?.tools?.tools.length ?? 0,\n          routeCount: chatConfig?.routes?.routes.length ?? 0,\n        });\n      } catch (err) {\n        logger.warn(\"Voice: getConfig() failed\", err);\n      }\n    } else if (chatConfig) {\n      logger.debug(\"Voice: using static chatConfig\", {\n        toolCount: chatConfig.tools?.tools.length ?? 0,\n        routeCount: chatConfig.routes?.routes.length ?? 0,\n      });\n    } else {\n      logger.debug(\"Voice: no chatConfig or getConfig \u2014 only built-in tools will be available\");\n    }\n\n    // Decorate host tools with hash ids and build the reverse lookup so we\n    // can map id-named tool calls back to the original host name when the\n    // model invokes them. Mirrors the chat-ui iframe's decoration step.\n    const decoratedManifest = this.buildDecoratedManifest(chatConfig);\n    logger.debug(\"Voice: decorated tools\", {\n      ids: decoratedManifest.tools.map((t) => `${t.id}=${t.name}`),\n    });\n\n    const pageContext = this.safeExtractPageContext();\n    logger.debug(\"Voice: page context extracted\", {\n      url: pageContext?.url,\n      title: pageContext?.title,\n      textLength: pageContext?.text?.length ?? 0,\n    });\n\n    let mint: MintResponse;\n    try {\n      logger.debug(\"Voice: requesting ephemeral token from mint endpoint\");\n      mint = await this.mintToken(chatConfig, decoratedManifest, pageContext);\n      logger.debug(\"Voice: mint succeeded\", {\n        voiceSessionId: mint.voiceSessionId,\n        expiresAt: mint.expiresAt,\n      });\n    } catch (err) {\n      await this.failWith(err instanceof Error ? err.message : \"Failed to start voice session\");\n      return;\n    }\n\n    let micStream: MediaStream;\n    try {\n      logger.debug(\"Voice: requesting microphone access\");\n      micStream = await navigator.mediaDevices.getUserMedia({ audio: true });\n      logger.debug(\"Voice: microphone access granted\");\n    } catch (err) {\n      const name = err instanceof Error ? err.name : \"\";\n      const message =\n        name === \"NotAllowedError\" || name === \"PermissionDeniedError\"\n          ? \"Microphone permission was denied. Enable microphone access in your browser settings to use voice mode.\"\n          : \"Could not access microphone.\";\n      await this.failWith(message);\n      return;\n    }\n\n    const pc = new RTCPeerConnection();\n    const audioElement = document.createElement(\"audio\");\n    audioElement.autoplay = true;\n    audioElement.style.display = \"none\";\n    document.body.appendChild(audioElement);\n\n    pc.ontrack = (event) => {\n      logger.debug(\"Voice: pc.ontrack received remote audio stream\");\n      if (event.streams[0]) {\n        audioElement.srcObject = event.streams[0];\n      }\n    };\n\n    pc.oniceconnectionstatechange = () => {\n      const s = pc.iceConnectionState;\n      logger.debug(\"Voice: ICE connection state \u2192\", s);\n      if (s === \"failed\" || s === \"disconnected\") {\n        void this.failWith(`WebRTC connection ${s}`);\n      }\n    };\n    pc.onconnectionstatechange = () => {\n      logger.debug(\"Voice: peer connection state \u2192\", pc.connectionState);\n      if (pc.connectionState === \"failed\") {\n        void this.failWith(\"WebRTC connection failed\");\n      }\n    };\n\n    for (const track of micStream.getAudioTracks()) {\n      pc.addTrack(track, micStream);\n    }\n\n    const dataChannel = pc.createDataChannel(\"oai-events\");\n    dataChannel.onmessage = (event) => {\n      const raw = typeof event.data === \"string\" ? event.data : \"\";\n      if (!raw) return;\n      logger.debug(\"Voice: \u2190 data channel message\", raw.slice(0, 200));\n      void handleRealtimeMessage(raw, this.buildMessageContext());\n    };\n    dataChannel.onopen = () => {\n      logger.debug(\"Voice: data channel opened\");\n      this.dispatch({ type: \"connected\" });\n      // Kick off an opening greeting so the assistant speaks first instead of\n      // waiting silently for the user. The minted session already carries the\n      // full instructions (persona, governance, language, \"first turn\" rule),\n      // so a bare `response.create` is enough \u2014 do NOT attach response-level\n      // instructions here, which would replace the session instructions.\n      // Skip it when the app's voice intro is \"none\" (`autoGreet === false`);\n      // a missing flag defaults to greeting for back-compat.\n      if (mint.autoGreet !== false) {\n        this.dispatch({ type: \"response_requested\" });\n        this.sendOverDataChannel({ type: \"response.create\" });\n      }\n    };\n    dataChannel.onclose = () => {\n      logger.debug(\"Voice: data channel closed\");\n    };\n\n    this.resources = {\n      pc,\n      dataChannel,\n      micStream,\n      audioElement,\n      voiceSessionId: mint.voiceSessionId,\n    };\n    this.persistSessionId = mint.voiceSessionId;\n\n    try {\n      logger.debug(\"Voice: creating WebRTC offer\");\n      const offer = await pc.createOffer();\n      await pc.setLocalDescription(offer);\n      logger.debug(\"Voice: exchanging SDP with OpenAI Realtime\");\n      const answerSdp = await this.exchangeSdp(offer, mint.clientSecret);\n      await pc.setRemoteDescription({ type: \"answer\", sdp: answerSdp });\n      logger.debug(\"Voice: WebRTC negotiation complete\");\n    } catch (err) {\n      await this.failWith(\n        err instanceof Error ? err.message : \"Failed to negotiate voice connection\"\n      );\n      return;\n    }\n\n    void this.postSessionEvent(\"start\", mint.voiceSessionId, pageContext);\n  }\n\n  /** Stop the session and tear down all resources. */\n  public async stop(): Promise<void> {\n    logger.debug(\"Voice: stop() called\");\n    this.dispatch({ type: \"stop\" });\n    await this.teardown();\n  }\n\n  /** Tear down everything and remove listeners. Call once before discarding the instance. */\n  public destroy(): void {\n    void this.teardown();\n    if (this.pageHideHandler) {\n      window.removeEventListener(\"pagehide\", this.pageHideHandler);\n      this.pageHideHandler = null;\n    }\n    this.listeners.clear();\n  }\n\n  // \u2500\u2500 Internals \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\n  private buildMessageContext(): RealtimeMessageContext {\n    return {\n      send: (event) => this.dispatch(event),\n      sendData: (payload) => this.sendOverDataChannel(payload),\n      dispatchToolCall: (name, args) => this.routeToolCall(name, args),\n      isDispatched: (id) => this.dispatchedCallIds.has(id),\n      markDispatched: (id) => {\n        this.dispatchedCallIds.add(id);\n      },\n      recordUsage: (usage) => this.accumulateUsage(usage),\n      onTranscriptEvent: (event) => this.handleTranscriptEvent(event),\n    };\n  }\n\n  private accumulateUsage(usage: RealtimeResponseUsage): void {\n    this.usage.responseCount += 1;\n    if (typeof usage.inputTokens === \"number\") this.usage.inputTokens += usage.inputTokens;\n    if (typeof usage.cachedInputTokens === \"number\") {\n      this.usage.cachedInputTokens += usage.cachedInputTokens;\n    }\n    if (typeof usage.outputTokens === \"number\") this.usage.outputTokens += usage.outputTokens;\n    if (typeof usage.audioInputTokens === \"number\") {\n      this.usage.audioInputTokens += usage.audioInputTokens;\n    }\n    if (typeof usage.audioOutputTokens === \"number\") {\n      this.usage.audioOutputTokens += usage.audioOutputTokens;\n    }\n    if (typeof usage.textInputTokens === \"number\") {\n      this.usage.textInputTokens += usage.textInputTokens;\n    }\n    if (typeof usage.textOutputTokens === \"number\") {\n      this.usage.textOutputTokens += usage.textOutputTokens;\n    }\n  }\n\n  private resetTranscriptState(): void {\n    this.transcript = new VoiceTranscriptSequencer();\n    this.persistenceOff = false;\n    this.persistInFlight = null;\n    this.persistAgain = false;\n    this.persistSessionId = null;\n  }\n\n  /**\n   * Drive the sequencer from a transcript event, then flush. A `user_turn`\n   * reserves an ordering slot (its transcript completes asynchronously and can\n   * land after the assistant has replied); a failed transcription releases that\n   * slot; a settled `transcript` becomes a message to append. Each settled\n   * message triggers a flush of everything still unacked.\n   */\n  private handleTranscriptEvent(event: VoiceTranscriptEvent): void {\n    switch (event.kind) {\n      case \"user_turn\":\n        // Stamp the boundary time now \u2014 well before the user's transcript text\n        // settles \u2014 so the message sorts by when the user actually spoke.\n        this.transcript.userTurn(Date.now());\n        return;\n      case \"user_transcript_failed\":\n        this.transcript.userTranscriptFailed();\n        return;\n      case \"transcript\":\n        if (this.transcript.record(event.itemId, event.role, event.text, Date.now())) {\n          this.flushTranscripts();\n        }\n        return;\n      default: {\n        const _exhaustive: never = event;\n        void _exhaustive;\n      }\n    }\n  }\n\n  /**\n   * Send everything still unacked as one additive batch. The server mints each\n   * message's id from its boundary `ts` (idempotent, K-sortable) and creates the\n   * conversation on the first user turn \u2014 so a re-sent batch is a safe no-op, and\n   * the unacked set doubles as the retry + greeting-defer + `pagehide`-beacon\n   * queue. Normally the batch is a single just-settled message.\n   */\n  private flushTranscripts(useBeacon = false): void {\n    if (this.persistenceOff || !this.persistSessionId) return;\n    const messages = this.transcript.pending();\n    if (messages.length === 0) return;\n    if (useBeacon) {\n      this.beaconTranscript(messages);\n      return;\n    }\n    void this.persistMessages(messages);\n  }\n\n  private buildTranscriptBody(messages: VoiceTranscriptMessage[]): string {\n    return JSON.stringify({\n      appId: this.config.appId,\n      voiceSessionId: this.persistSessionId,\n      messages,\n    });\n  }\n\n  private async persistMessages(messages: VoiceTranscriptMessage[]): Promise<void> {\n    if (this.persistInFlight) {\n      // Coalesce: a newer flush runs once the in-flight POST settles.\n      this.persistAgain = true;\n      return;\n    }\n    const itemIds = messages.map((m) => m.itemId);\n    const run = async (): Promise<void> => {\n      try {\n        const res = await fetch(`${this.apiOrigin}/api/voice/transcript`, {\n          method: \"POST\",\n          headers: { \"Content-Type\": \"application/json\" },\n          body: this.buildTranscriptBody(messages),\n        });\n        if (res.ok) {\n          const body = (await res.json().catch(() => ({}))) as {\n            persisted?: boolean;\n            disabled?: boolean;\n          };\n          if (body.disabled) {\n            // Storage off for this app \u2014 stop trying for the rest of the session.\n            this.persistenceOff = true;\n          } else if (body.persisted) {\n            this.transcript.ack(itemIds);\n          }\n          // Otherwise the batch was deferred (a greeting before any user turn);\n          // it stays unacked and rides along with the next flush.\n        } else if (res.status === 401 || res.status === 403 || res.status === 404) {\n          // Session not recognised / not permitted \u2014 don't hammer the endpoint.\n          this.persistenceOff = true;\n        }\n      } catch (err) {\n        logger.warn(\"Voice: failed to persist transcript\", err);\n      }\n    };\n    this.persistInFlight = run().finally(() => {\n      this.persistInFlight = null;\n      if (this.persistAgain) {\n        this.persistAgain = false;\n        this.flushTranscripts();\n      }\n    });\n  }\n\n  private beaconTranscript(messages: VoiceTranscriptMessage[]): void {\n    if (typeof navigator === \"undefined\" || !navigator.sendBeacon) return;\n    try {\n      navigator.sendBeacon(\n        `${this.apiOrigin}/api/voice/transcript`,\n        new Blob([this.buildTranscriptBody(messages)], { type: \"application/json\" })\n      );\n    } catch (err) {\n      logger.warn(\"Voice: failed to beacon transcript\", err);\n    }\n  }\n\n  private sendOverDataChannel(payload: unknown): void {\n    const channel = this.resources.dataChannel;\n    if (!channel || channel.readyState !== \"open\") {\n      logger.warn(\"Voice data channel not ready; dropping payload\");\n      return;\n    }\n    try {\n      const serialized = JSON.stringify(payload);\n      logger.debug(\"Voice: \u2192 data channel send\", serialized.slice(0, 200));\n      channel.send(serialized);\n    } catch (err) {\n      logger.warn(\"Failed to send on voice data channel\", err);\n    }\n  }\n\n  private async routeToolCall(idOrName: string, args: unknown): Promise<unknown> {\n    // The model calls us back using the decorated id (e.g. orders_list).\n    // Resolve it to the original host tool name; fall back to the raw value\n    // (it might be `redirect` or some non-decorated name).\n    const name = this.toolNameById.get(idOrName) ?? idOrName;\n    logger.debug(\"Voice: tool call dispatched\", { id: idOrName, name, args });\n    // MCP tools execute server-side (the org token never reaches the browser).\n    // The model calls back with the `mcp__\u2026` name minted by the server; relay\n    // it to the exec endpoint and feed the result back over the data channel.\n    if (name.startsWith(\"mcp__\")) {\n      return await this.execMcpTool(name, args);\n    }\n    if (name === \"redirect\") {\n      const path = (args as { path?: unknown })?.path;\n      if (typeof path !== \"string\") {\n        throw new Error(\"redirect tool requires a string `path` argument\");\n      }\n      if (this.config.onRedirect) {\n        this.config.onRedirect(path);\n      } else if (typeof window !== \"undefined\") {\n        window.location.assign(path);\n      }\n      return { success: true, redirected: true, path };\n    }\n    if (this.config.onToolCall) {\n      return await this.config.onToolCall(name, args);\n    }\n    throw new Error(`No handler configured for tool: ${name}`);\n  }\n\n  /**\n   * Relay an MCP tool call to the server, which holds the org's credentials\n   * and executes against the remote MCP server. The browser only ever passes\n   * through the tool name, args, and the opaque result.\n   */\n  private async execMcpTool(toolName: string, args: unknown): Promise<unknown> {\n    try {\n      const res = await fetch(`${this.apiOrigin}/api/voice/mcp-exec`, {\n        method: \"POST\",\n        headers: { \"Content-Type\": \"application/json\" },\n        body: JSON.stringify({\n          appId: this.config.appId,\n          toolName,\n          args: args ?? {},\n          pageContext: this.safeExtractPageContext(),\n        }),\n      });\n      if (!res.ok) {\n        const body = (await res.json().catch(() => ({}))) as { error?: string };\n        return { error: body.error ?? `MCP tool failed (${res.status})` };\n      }\n      const body = (await res.json()) as { result?: unknown };\n      return body.result ?? {};\n    } catch (err) {\n      // The relay failed, but we hand a structured `{ error }` straight back to\n      // the model to recover from \u2014 expected data flow, not a console-worthy\n      // fault. Log at debug to match the text-chat tool-call path (client.ts).\n      logger.debug(\"Voice: MCP tool relay failed\", err);\n      return { error: \"The integration could not complete this request.\" };\n    }\n  }\n\n  private async mintToken(\n    chatConfig: ChatConfig | undefined,\n    decoratedManifest: { tools: Array<ToolDefinition & { id: string }> },\n    pageContext: PageContext | undefined\n  ): Promise<MintResponse> {\n    const res = await fetch(`${this.apiOrigin}/api/voice/realtime-token`, {\n      method: \"POST\",\n      headers: { \"Content-Type\": \"application/json\" },\n      body: JSON.stringify({\n        appId: this.config.appId,\n        pageContext,\n        toolManifest: decoratedManifest,\n        routeManifest: chatConfig?.routes,\n        user: this.config.user,\n      }),\n    });\n    if (!res.ok) {\n      const body = (await res.json().catch(() => ({}))) as { error?: string };\n      throw new Error(body.error ?? `Mint failed (${res.status})`);\n    }\n    return (await res.json()) as MintResponse;\n  }\n\n  /**\n   * Decorate the host's tool manifest with readable, collision-free model-facing ids\n   * and populate `this.toolNameById` for reverse lookup. Mirrors the decoration the\n   * chat-ui iframe applies before sending tools to `/api/chat`. GraphQL/REST tools are\n   * ordinary manifest entries here (contributed by their adapters), so no special-casing\n   * is needed.\n   */\n  private buildDecoratedManifest(chatConfig: ChatConfig | undefined): {\n    tools: Array<ToolDefinition & { id: string }>;\n  } {\n    this.toolNameById.clear();\n    const used = new Set<string>([\"redirect\"]);\n\n    const decoratedHostTools = (chatConfig?.tools?.tools ?? []).map((t: ToolDefinition) => {\n      const id = uniqueToolId(t.name, used);\n      used.add(id);\n      this.toolNameById.set(id, t.name);\n      return { ...t, id };\n    });\n\n    return { tools: decoratedHostTools };\n  }\n\n  private async exchangeSdp(\n    offer: RTCSessionDescriptionInit,\n    clientSecret: string\n  ): Promise<string> {\n    const sdpResponse = await fetch(`${REALTIME_CALLS_URL}?model=${DEFAULT_REALTIME_MODEL}`, {\n      method: \"POST\",\n      headers: {\n        Authorization: `Bearer ${clientSecret}`,\n        \"Content-Type\": \"application/sdp\",\n      },\n      body: offer.sdp,\n    });\n    if (!sdpResponse.ok) {\n      const body = await sdpResponse.text().catch(() => \"\");\n      throw new Error(`SDP exchange failed (${sdpResponse.status}): ${body}`);\n    }\n    return await sdpResponse.text();\n  }\n\n  private buildStopEventBody(voiceSessionId: string, pageContext: PageContext | undefined) {\n    return {\n      appId: this.config.appId,\n      voiceSessionId,\n      event: \"stop\" as const,\n      clientTimestamp: Date.now(),\n      pageContext,\n      usage: { ...this.usage },\n    };\n  }\n\n  private async postSessionEvent(\n    event: \"start\" | \"stop\",\n    voiceSessionId: string,\n    pageContext: PageContext | undefined\n  ): Promise<void> {\n    try {\n      const body =\n        event === \"stop\"\n          ? this.buildStopEventBody(voiceSessionId, pageContext)\n          : {\n              appId: this.config.appId,\n              voiceSessionId,\n              event,\n              clientTimestamp: Date.now(),\n              pageContext,\n            };\n      await fetch(`${this.apiOrigin}/api/voice/session-event`, {\n        method: \"POST\",\n        headers: { \"Content-Type\": \"application/json\" },\n        body: JSON.stringify(body),\n        keepalive: event === \"stop\",\n      });\n    } catch (err) {\n      logger.warn(`Failed to post voice.session.${event}`, err);\n    }\n  }\n\n  private async teardown(): Promise<void> {\n    // Final transcript flush while the session id is still addressable (a POST\n    // already in flight will re-run via the coalescing tail). persistSessionId\n    // outlives the resources clear below, so this is safe to call first.\n    this.flushTranscripts();\n\n    const r = this.resources;\n    this.resources = EMPTY_RESOURCES;\n    this.dispatchedCallIds = new Set();\n\n    try {\n      r.dataChannel?.close();\n    } catch (err) {\n      logger.warn(\"Error closing data channel\", err);\n    }\n    try {\n      for (const track of r.micStream?.getTracks() ?? []) {\n        track.stop();\n      }\n    } catch (err) {\n      logger.warn(\"Error stopping mic tracks\", err);\n    }\n    try {\n      for (const sender of r.pc?.getSenders() ?? []) {\n        sender.track?.stop();\n      }\n      r.pc?.close();\n    } catch (err) {\n      logger.warn(\"Error closing peer connection\", err);\n    }\n    if (r.audioElement) {\n      try {\n        r.audioElement.srcObject = null;\n        r.audioElement.remove();\n      } catch (err) {\n        logger.warn(\"Error removing audio element\", err);\n      }\n    }\n\n    if (r.voiceSessionId) {\n      await this.postSessionEvent(\"stop\", r.voiceSessionId, this.safeExtractPageContext());\n    }\n  }\n\n  private async failWith(message: string): Promise<void> {\n    logger.warn(\"Voice session error:\", message);\n    this.dispatch({ type: \"error\", message });\n    await this.teardown();\n  }\n\n  private dispatch(event: Parameters<typeof voiceReducer>[1]): void {\n    const next = voiceReducer(this.machine, event);\n    if (next === this.machine) return;\n    this.machine = next;\n    for (const listener of this.listeners) {\n      try {\n        listener(next);\n      } catch (err) {\n        logger.warn(\"Voice state listener threw\", err);\n      }\n    }\n  }\n\n  private safeExtractPageContext(): PageContext | undefined {\n    // Privacy opt-out: never read or transmit page context for this app.\n    if (this.config.disablePageContent) return undefined;\n    try {\n      return extractPageContext();\n    } catch {\n      return undefined;\n    }\n  }\n\n  private attachPageHide(): void {\n    if (typeof window === \"undefined\") return;\n    this.pageHideHandler = () => {\n      const r = this.resources;\n      if (!r.voiceSessionId) return;\n      // Beacon the final transcript turn before the stop event, so a tab close\n      // mid-conversation doesn't drop the last exchange.\n      this.flushTranscripts(true);\n      const body = JSON.stringify(this.buildStopEventBody(r.voiceSessionId, undefined));\n      if (navigator.sendBeacon) {\n        navigator.sendBeacon(\n          `${this.apiOrigin}/api/voice/session-event`,\n          new Blob([body], { type: \"application/json\" })\n        );\n      }\n    };\n    window.addEventListener(\"pagehide\", this.pageHideHandler);\n  }\n}\n", "import { YakClient, type YakClientConfig } from \"./client.js\";\nimport { logger } from \"./logger.js\";\nimport type { ChatConfigProvider } from \"./types/config.js\";\nimport type { IframeMessageFromHost, WidgetPosition } from \"./types/messaging.js\";\nimport { INITIAL_VOICE_MACHINE, type VoiceMachine, type VoiceState } from \"./voice-machine.js\";\nimport {\n  type VoiceStateListener,\n  YakVoiceSession,\n  type YakVoiceSessionConfig,\n} from \"./voice-session.js\";\n\n// \u2500\u2500 Widget mode \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\n/**\n * Which experiences the widget exposes:\n * - `chat` \u2014 chat icon only (opens the chat iframe panel). The default.\n * - `voice` \u2014 voice icon only (starts a WebRTC voice session).\n * - `both` \u2014 both icons, sharing one trigger pill.\n */\nexport type WidgetMode = \"chat\" | \"voice\" | \"both\";\n\n// Single source of truth for the default trigger + panel corner.\nconst DEFAULT_POSITION: WidgetPosition = \"bottom-right\";\n\n// \u2500\u2500 Trigger button configuration \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\nexport type TriggerButtonConfig = {\n  /** Custom color overrides for light mode */\n  lightButton?: { background?: string; color?: string; border?: string };\n  /** Custom color overrides for dark mode */\n  darkButton?: { background?: string; color?: string; border?: string };\n};\n\n// \u2500\u2500 YakEmbed configuration \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\nexport type YakEmbedConfig = YakClientConfig & {\n  /** DOM element to append the widget into. Defaults to document.body. */\n  target?: HTMLElement;\n  /** Show the floating trigger button. Default: true */\n  trigger?: boolean | TriggerButtonConfig;\n  /**\n   * Which experiences this widget exposes.\n   *   \"chat\"  \u2014 chat icon only (opens the chat iframe).\n   *   \"voice\" \u2014 voice icon only (starts a WebRTC voice session).\n   *   \"both\"  \u2014 both icons in one pill.\n   * Default: \"chat\".\n   */\n  mode?: WidgetMode;\n  /**\n   * Async provider for chat config (routes + tools). Used by the voice\n   * session on every `start()` and by the iframe via postMessage. Takes\n   * precedence over the static `chatConfig` on `YakClientConfig`.\n   */\n  getConfig?: ChatConfigProvider;\n};\n\n// \u2500\u2500 State listener \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\nexport type YakEmbedState = {\n  /** Whether the chat panel is open. */\n  isOpen: boolean;\n  /** Whether the chat iframe has handshaked and can receive messages. */\n  isReady: boolean;\n  /**\n   * Whether the chat is opening but not yet interactive \u2014 `isOpen && !isReady`.\n   * Stays true from the moment the panel opens until the iframe reports ready,\n   * so custom triggers can show a spinner without re-deriving it.\n   */\n  isLoading: boolean;\n  /** Whether the panel is expanded to (near) full-screen. */\n  isExpanded: boolean;\n};\n\nexport type YakEmbedStateListener = (state: YakEmbedState) => void;\n\n// \u2500\u2500 Inline SVG icons (lucide) \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\nconst MESSAGE_CIRCLE_SVG = `<svg viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\" aria-hidden=\"true\"><path d=\"M7.9 20A9 9 0 1 0 4 16.1L2 22Z\"/></svg>`;\n\nconst AUDIO_LINES_SVG = `<svg viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\" aria-hidden=\"true\"><path d=\"M2 10v3\"/><path d=\"M6 6v11\"/><path d=\"M10 3v18\"/><path d=\"M14 8v7\"/><path d=\"M18 5v13\"/><path d=\"M22 10v3\"/></svg>`;\n\nconst STOP_SVG = `<svg viewBox=\"0 0 24 24\" fill=\"currentColor\" aria-hidden=\"true\"><rect x=\"6\" y=\"6\" width=\"12\" height=\"12\" rx=\"2\"/></svg>`;\n\nconst VOICE_STATE_ARIA: Record<VoiceState, string> = {\n  idle: \"Start voice mode\",\n  connecting: \"Connecting voice session\",\n  listening: \"Voice listening \u2014 tap to stop\",\n  thinking: \"Voice thinking \u2014 tap to stop\",\n  speaking: \"Voice speaking \u2014 tap to stop\",\n  error: \"Voice error \u2014 tap to retry\",\n};\n\n// \u2500\u2500 CSS \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\nfunction getPanelStyles(): string {\n  return `\n    .yak-panel-root {\n      position: fixed;\n      top: 0; left: 0; right: 0; bottom: 0;\n      width: 100vw; height: 100vh;\n      pointer-events: none;\n      z-index: 9998;\n    }\n\n    .yak-panel-container {\n      position: absolute;\n      width: 500px; height: 600px;\n      max-width: calc(100vw - 40px);\n      max-height: calc(100vh - 120px);\n      border-radius: 15px;\n      overflow: hidden;\n      background-color: transparent;\n      pointer-events: auto;\n    }\n\n    .yak-panel-container[data-position=\"top-left\"]:not(.yak-panel-drawer)      { top: 16px; left: 16px; }\n    .yak-panel-container[data-position=\"top-center\"]:not(.yak-panel-drawer)    { top: 16px; left: 50%; transform: translateX(-50%); }\n    .yak-panel-container[data-position=\"top-right\"]:not(.yak-panel-drawer)     { top: 16px; right: 16px; }\n    .yak-panel-container[data-position=\"left-center\"]:not(.yak-panel-drawer)   { top: 50%; left: 16px; transform: translateY(-50%); }\n    .yak-panel-container[data-position=\"right-center\"]:not(.yak-panel-drawer)  { top: 50%; right: 16px; transform: translateY(-50%); }\n    .yak-panel-container[data-position=\"bottom-left\"]:not(.yak-panel-drawer)   { bottom: 16px; left: 16px; }\n    .yak-panel-container[data-position=\"bottom-center\"]:not(.yak-panel-drawer) { bottom: 16px; left: 50%; transform: translateX(-50%); }\n    .yak-panel-container[data-position=\"bottom-right\"]:not(.yak-panel-drawer)  { bottom: 16px; right: 16px; }\n\n    .yak-panel-container:not(.yak-panel-drawer)                    { display: none; }\n    .yak-panel-container:not(.yak-panel-drawer)[data-open=\"true\"]  { display: block; }\n\n    .yak-panel-container[data-expanded=\"true\"] {\n      width: calc(100vw - 32px) !important;\n      height: calc(100vh - 32px) !important;\n      max-width: none !important; max-height: none !important;\n      top: 16px !important; left: 16px !important; right: 16px !important; bottom: 16px !important;\n      border-radius: 15px !important;\n      border: 1px solid rgba(0, 0, 0, 0.1) !important;\n      box-shadow: 0 4px 24px rgba(0, 0, 0, 0.15) !important;\n    }\n    @media (prefers-color-scheme: dark) {\n      .yak-panel-container[data-expanded=\"true\"]:not(.yak-panel-light) { border-color: rgba(255,255,255,0.1) !important; }\n    }\n    .yak-panel-container.yak-panel-dark[data-expanded=\"true\"]  { border-color: rgba(255,255,255,0.1) !important; }\n    .yak-panel-container.yak-panel-light[data-expanded=\"true\"] { border-color: rgba(0,0,0,0.1) !important; }\n\n    .yak-panel-container.yak-panel-drawer {\n      height: calc(100% - 32px); max-width: 100vw; max-height: none;\n      border-radius: 15px;\n      transition: transform 0.3s cubic-bezier(0.16, 1, 0.3, 1);\n    }\n    .yak-panel-container.yak-panel-drawer[data-position=\"left-center\"],\n    .yak-panel-container.yak-panel-drawer[data-position=\"top-left\"],\n    .yak-panel-container.yak-panel-drawer[data-position=\"bottom-left\"] {\n      top: 16px; left: 16px; bottom: 16px;\n      transform: translateX(calc(-100% - 16px));\n    }\n    .yak-panel-container.yak-panel-drawer[data-position=\"right-center\"],\n    .yak-panel-container.yak-panel-drawer[data-position=\"top-right\"],\n    .yak-panel-container.yak-panel-drawer[data-position=\"bottom-right\"] {\n      top: 16px; right: 16px; bottom: 16px;\n      transform: translateX(calc(100% + 16px));\n    }\n    .yak-panel-container.yak-panel-drawer[data-position=\"top-center\"],\n    .yak-panel-container.yak-panel-drawer[data-position=\"bottom-center\"] {\n      top: 16px; right: 16px; bottom: 16px;\n      transform: translateX(calc(100% + 16px));\n    }\n    .yak-panel-container.yak-panel-drawer[data-open=\"true\"] { transform: translateX(0); }\n\n    .yak-panel-iframe {\n      position: absolute; inset: 0;\n      width: 100%; height: 100%; border: none;\n    }\n\n    @media (max-width: 640px) {\n      .yak-panel-container:not(.yak-panel-drawer) {\n        width: 100% !important; height: 100% !important; height: 100dvh !important;\n        max-width: none !important; max-height: none !important;\n        top: 0 !important; left: 0 !important; right: 0 !important; bottom: 0 !important;\n        border-radius: 0 !important;\n      }\n      .yak-panel-container.yak-panel-drawer { width: 100% !important; max-width: none !important; }\n    }\n  `;\n}\n\nfunction getTriggerStyles(): string {\n  return `\n    .yak-widget-trigger {\n      position: fixed; z-index: 9997;\n      display: inline-flex; align-items: center; gap: 8px;\n      border: none; border-radius: 30px;\n      padding: 5px; height: 45px;\n      transition: all 0.3s cubic-bezier(0.16, 1, 0.3, 1);\n      background-color: #000; color: #fff;\n      box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15);\n      font-family: system-ui, -apple-system, sans-serif;\n    }\n\n    .yak-widget-trigger[data-position=\"top-left\"]      { top: 28px; left: 28px; }\n    .yak-widget-trigger[data-position=\"top-center\"]    { top: 28px; left: 50%; transform: translateX(-50%); }\n    .yak-widget-trigger[data-position=\"top-right\"]     { top: 28px; right: 28px; }\n    .yak-widget-trigger[data-position=\"left-center\"]   { top: 50%; left: 28px; transform: translateY(-50%); }\n    .yak-widget-trigger[data-position=\"right-center\"]  { top: 50%; right: 28px; transform: translateY(-50%); }\n    .yak-widget-trigger[data-position=\"bottom-left\"]   { bottom: 28px; left: 28px; }\n    .yak-widget-trigger[data-position=\"bottom-center\"] { bottom: 28px; left: 50%; transform: translateX(-50%); }\n    .yak-widget-trigger[data-position=\"bottom-right\"]  { bottom: 28px; right: 28px; }\n\n    .yak-widget-icon-bg {\n      display: flex; align-items: center; justify-content: center;\n      width: 36px; height: 36px; border-radius: 50%;\n      background-color: rgba(255, 255, 255, 0.1);\n      flex-shrink: 0;\n    }\n\n    .yak-widget-icon { width: 24px; height: 24px; color: currentColor; }\n\n    .yak-widget-trigger-icon-btn {\n      display: inline-flex; align-items: center; justify-content: center;\n      width: 36px; height: 36px; border-radius: 50%;\n      border: none; padding: 0;\n      background-color: transparent;\n      color: inherit;\n      cursor: pointer;\n      position: relative;\n      transition: background-color 0.15s ease;\n      flex-shrink: 0;\n    }\n    .yak-widget-trigger-icon-btn:hover { background-color: rgba(255, 255, 255, 0.12); }\n    .yak-widget-trigger-icon-btn:disabled { cursor: wait; opacity: 0.7; }\n    .yak-widget-trigger-icon-btn svg { width: 20px; height: 20px; display: block; }\n\n    .yak-widget-trigger-icon-btn[data-action=\"voice\"][data-state=\"error\"] {\n      background-color: rgba(185, 28, 28, 0.18);\n    }\n    .yak-widget-trigger-icon-btn[data-action=\"voice\"][data-state=\"listening\"]::after,\n    .yak-widget-trigger-icon-btn[data-action=\"voice\"][data-state=\"speaking\"]::after {\n      content: \"\";\n      position: absolute; inset: 2px;\n      border-radius: 50%;\n      border: 2px solid currentColor;\n      pointer-events: none;\n    }\n    .yak-widget-trigger-icon-btn[data-action=\"voice\"][data-state=\"listening\"]::after {\n      opacity: 0.4; animation: yak-widget-pulse 1.2s ease-out infinite;\n    }\n    .yak-widget-trigger-icon-btn[data-action=\"voice\"][data-state=\"speaking\"]::after {\n      opacity: 0.5; animation: yak-widget-wave 0.8s ease-in-out infinite;\n    }\n\n    @media (prefers-color-scheme: dark) {\n      .yak-widget-trigger:not(.yak-widget-light) .yak-widget-icon { filter: invert(1); }\n      .yak-widget-trigger:not(.yak-widget-light) .yak-widget-trigger-icon-btn:hover {\n        background-color: rgba(255, 255, 255, 0.12);\n      }\n    }\n    .yak-widget-trigger.yak-widget-dark .yak-widget-icon  { filter: invert(1); }\n    .yak-widget-trigger.yak-widget-light .yak-widget-icon  { filter: none; }\n    .yak-widget-trigger.yak-widget-light .yak-widget-trigger-icon-btn:hover {\n      background-color: rgba(0, 0, 0, 0.06);\n    }\n\n    .yak-widget-spinner {\n      width: 20px; height: 20px;\n      border: 2px solid currentColor; border-top-color: transparent; border-radius: 50%;\n      animation: yak-widget-spin 0.8s linear infinite;\n    }\n    @keyframes yak-widget-spin { to { transform: rotate(360deg); } }\n    @keyframes yak-widget-pulse {\n      0%   { transform: scale(1);    opacity: 0.5; }\n      100% { transform: scale(1.45); opacity: 0;   }\n    }\n    @keyframes yak-widget-wave {\n      0%, 100% { transform: scale(1);    opacity: 0.5; }\n      50%      { transform: scale(1.25); opacity: 0.9; }\n    }\n\n    .yak-widget-trigger.yak-widget-custom-light {\n      background-color: var(--yak-btn-light-bg, #fff); color: var(--yak-btn-light-color, #000);\n      border: 1px solid var(--yak-btn-light-border, transparent);\n      box-shadow: 0 4px 12px rgba(0, 0, 0, 0.1);\n    }\n    .yak-widget-trigger.yak-widget-custom-dark {\n      background-color: var(--yak-btn-dark-bg, #000); color: var(--yak-btn-dark-color, #fff);\n      border: 1px solid var(--yak-btn-dark-border, transparent);\n      box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15);\n    }\n\n    @media (prefers-color-scheme: light) {\n      .yak-widget-trigger[data-has-light-custom]:not(.yak-widget-dark) {\n        background-color: var(--yak-btn-light-bg, #fff); color: var(--yak-btn-light-color, #000);\n        border: 1px solid var(--yak-btn-light-border, transparent);\n        box-shadow: 0 4px 12px rgba(0, 0, 0, 0.1);\n      }\n    }\n    @media (prefers-color-scheme: dark) {\n      .yak-widget-trigger[data-has-dark-custom]:not(.yak-widget-light) {\n        background-color: var(--yak-btn-dark-bg, #000); color: var(--yak-btn-dark-color, #fff);\n        border: 1px solid var(--yak-btn-dark-border, transparent);\n        box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15);\n      }\n    }\n\n    @media (prefers-color-scheme: light) {\n      .yak-widget-trigger:not(.yak-widget-dark):not([data-has-light-custom]) {\n        background-color: #fff; color: #000;\n        box-shadow: 0 4px 12px rgba(0, 0, 0, 0.1); border: 1px solid #e5e5e5;\n      }\n      .yak-widget-trigger:not(.yak-widget-dark):not([data-has-light-custom]) .yak-widget-icon-bg {\n        background-color: rgba(0, 0, 0, 0.05);\n      }\n    }\n\n    @media (prefers-color-scheme: dark) {\n      .yak-widget-trigger:not(.yak-widget-light):not([data-has-dark-custom]) {\n        background-color: #000; color: #fff;\n        box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15); border: none;\n      }\n      .yak-widget-trigger:not(.yak-widget-light):not([data-has-dark-custom]) .yak-widget-icon-bg {\n        background-color: rgba(255, 255, 255, 0.1);\n      }\n    }\n\n    .yak-widget-trigger.yak-widget-light:not(.yak-widget-custom-light) {\n      background-color: #fff; color: #000;\n      box-shadow: 0 4px 12px rgba(0, 0, 0, 0.1); border: 1px solid #e5e5e5;\n    }\n    .yak-widget-trigger.yak-widget-light:not(.yak-widget-custom-light) .yak-widget-icon-bg {\n      background-color: rgba(0, 0, 0, 0.05);\n    }\n\n    .yak-widget-trigger.yak-widget-dark:not(.yak-widget-custom-dark) {\n      background-color: #000; color: #fff;\n      box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15); border: none;\n    }\n    .yak-widget-trigger.yak-widget-dark:not(.yak-widget-custom-dark) .yak-widget-icon-bg {\n      background-color: rgba(255, 255, 255, 0.1);\n    }\n  `;\n}\n\n// \u2500\u2500 YakEmbed class \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\n/**\n * Drop-in widget that renders the yak trigger pill plus, depending on mode,\n * the chat iframe panel and/or a WebRTC voice session. Composes both\n * `YakClient` (chat) and `YakVoiceSession` (voice) under one trigger.\n *\n * @example\n * ```ts\n * const embed = new YakEmbed({\n *   appId: \"my-app\",\n *   mode: \"both\",\n *   theme: { position: \"bottom-left\" },\n *   onToolCall: async (name, args) => { ... },\n * });\n * embed.mount();\n * ```\n */\nexport class YakEmbed {\n  private readonly client: YakClient;\n  private readonly voice: YakVoiceSession | null;\n  private readonly config: YakEmbedConfig;\n  private readonly mode: WidgetMode;\n\n  // DOM elements\n  private styleEl: HTMLStyleElement | null = null;\n  private panelRoot: HTMLDivElement | null = null;\n  private container: HTMLDivElement | null = null;\n  private iframe: HTMLIFrameElement | null = null;\n  private triggerEl: HTMLDivElement | null = null;\n  private chatButton: HTMLButtonElement | null = null;\n  private voiceButton: HTMLButtonElement | null = null;\n\n  // State\n  private isOpen = false;\n  private isReady = false;\n  private isExpanded = false;\n  private hasBeenOpened = false;\n  private pendingPrompt: string | null = null;\n  private mounted = false;\n  private voiceMachine: VoiceMachine = INITIAL_VOICE_MACHINE;\n\n  // Listeners\n  private stateListeners = new Set<YakEmbedStateListener>();\n  private voiceListeners = new Set<VoiceStateListener>();\n  private unsubscribeVoice: (() => void) | null = null;\n  private mobileQuery: MediaQueryList | null = null;\n  private mobileHandler: ((e: MediaQueryListEvent) => void) | null = null;\n  private expandHandler: ((e: MessageEvent) => void) | null = null;\n\n  constructor(config: YakEmbedConfig) {\n    this.config = config;\n    this.mode = config.mode ?? \"chat\";\n\n    // Wrap callbacks to integrate with our state\n    this.client = new YakClient({\n      ...config,\n      onReady: () => {\n        this.isReady = true;\n        this.updatePanelState();\n        this.updateChatButtonState();\n        this.sendPendingPrompt();\n        this.sendFocusIfOpen();\n        this.notifyMobileState();\n        // Surface the ready transition to framework subscribers (React/Vue/\n        // Svelte/Angular all derive their loading state from this). Without\n        // it `isReady` stays false forever and consumers spin indefinitely.\n        this.notifyListeners();\n        config.onReady?.();\n      },\n      onClose: () => {\n        this.close();\n        config.onClose?.();\n      },\n    });\n\n    if (this.mode !== \"chat\") {\n      const voiceConfig: YakVoiceSessionConfig = {\n        appId: config.appId,\n        // A single `origin` on the embed drives both surfaces: chat (iframe)\n        // and voice (mint endpoint).\n        apiOrigin: config.origin,\n        getConfig: config.getConfig,\n        chatConfig: config.chatConfig,\n        onToolCall: config.onToolCall,\n        onRedirect: config.onRedirect,\n        disablePageContent: config.options?.disablePageContent,\n        // Same signed identity as chat \u2014 drives voice persistence + memory recall.\n        user: config.user,\n      };\n      this.voice = new YakVoiceSession(voiceConfig);\n    } else {\n      this.voice = null;\n    }\n  }\n\n  /** The underlying headless YakClient for advanced usage */\n  public getClient(): YakClient {\n    return this.client;\n  }\n\n  /** The underlying voice session \u2014 null when mode === \"chat\". */\n  public getVoiceSession(): YakVoiceSession | null {\n    return this.voice;\n  }\n\n  /** Current widget mode (immutable for the lifetime of the embed). */\n  public getMode(): WidgetMode {\n    return this.mode;\n  }\n\n  // \u2500\u2500 Lifecycle \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\n  /**\n   * Mount the widget into the DOM. Call once after construction.\n   * Inserts styles and trigger button (if enabled). The chat iframe is\n   * lazily created on the first call to open().\n   */\n  public mount(target?: HTMLElement): void {\n    if (this.mounted) return;\n    this.mounted = true;\n\n    const parent = target ?? this.config.target ?? document.body;\n\n    // Inject styles\n    this.styleEl = document.createElement(\"style\");\n    this.styleEl.textContent = getPanelStyles() + getTriggerStyles();\n    parent.appendChild(this.styleEl);\n\n    // Create trigger\n    if (this.config.trigger !== false) {\n      this.createTrigger(parent);\n    }\n\n    // Listen for expansion messages from iframe\n    this.expandHandler = (event: MessageEvent) => {\n      if (event.data?.type === \"YAK_SET_EXPANDED\") {\n        this.isExpanded = Boolean(event.data.expanded);\n        this.updatePanelState();\n        this.notifyListeners();\n      }\n    };\n    window.addEventListener(\"message\", this.expandHandler);\n\n    // Start the client's message listeners\n    this.client.mount();\n\n    // Subscribe to voice state for trigger updates + fan-out to listeners\n    if (this.voice) {\n      this.voiceMachine = this.voice.getState();\n      this.unsubscribeVoice = this.voice.onStateChange((machine) => {\n        this.voiceMachine = machine;\n        this.updateVoiceButtonState();\n        for (const listener of this.voiceListeners) {\n          try {\n            listener(machine);\n          } catch (err) {\n            logger.warn(\"Error in voice listener:\", err);\n          }\n        }\n      });\n      this.updateVoiceButtonState();\n    }\n  }\n\n  /** Remove all DOM elements and event listeners. */\n  public destroy(): void {\n    if (!this.mounted) return;\n    this.mounted = false;\n\n    this.client.unmount();\n    // Drop references to the now-detached iframe window so a remount (e.g.\n    // React StrictMode's mount\u2192unmount\u2192mount) doesn't leave the client\n    // pointing at a dead window. Clears both `iframeWindow` and `readyTarget`.\n    this.client.setIframeWindow(null);\n\n    if (this.unsubscribeVoice) {\n      this.unsubscribeVoice();\n      this.unsubscribeVoice = null;\n    }\n    this.voice?.destroy();\n\n    if (this.expandHandler) {\n      window.removeEventListener(\"message\", this.expandHandler);\n      this.expandHandler = null;\n    }\n\n    if (this.mobileQuery && this.mobileHandler) {\n      this.mobileQuery.removeEventListener(\"change\", this.mobileHandler);\n      this.mobileQuery = null;\n      this.mobileHandler = null;\n    }\n\n    this.panelRoot?.remove();\n    this.triggerEl?.remove();\n    this.styleEl?.remove();\n\n    this.panelRoot = null;\n    this.container = null;\n    this.iframe = null;\n    this.triggerEl = null;\n    this.chatButton = null;\n    this.voiceButton = null;\n    this.styleEl = null;\n    this.isOpen = false;\n    this.isReady = false;\n    this.isExpanded = false;\n    this.hasBeenOpened = false;\n\n    this.stateListeners.clear();\n    this.voiceListeners.clear();\n  }\n\n  // \u2500\u2500 Public chat API \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\n  /** Open the chat widget. Creates the iframe on first call (lazy mount). */\n  public open(): void {\n    if (!this.mounted) return;\n\n    if (!this.hasBeenOpened) {\n      this.hasBeenOpened = true;\n\n      const parent = this.config.target ?? document.body;\n      this.createPanel(parent);\n    }\n\n    this.isOpen = true;\n    this.client.setWidgetOpen(true);\n    this.updatePanelState();\n    this.updateChatButtonState();\n    // Delegate focus into the (cross-origin) iframe within this user gesture.\n    // Browsers \u2014 Safari especially \u2014 won't let an embedded frame focus its own\n    // input unless the embedder first moves focus into the frame; doing it here,\n    // while the open click's user activation is live, is what makes the widget's\n    // subsequent composer focus stick on first open (Chrome tolerates its\n    // absence, Safari does not). Harmless when the iframe is still loading.\n    this.iframe?.contentWindow?.focus();\n    this.sendFocusIfOpen();\n    this.notifyListeners();\n  }\n\n  /** Close the chat widget. The iframe remains in the DOM for instant re-open. */\n  public close(): void {\n    this.isOpen = false;\n    this.client.setWidgetOpen(false);\n    this.updatePanelState();\n    this.updateChatButtonState();\n    this.notifyListeners();\n  }\n\n  /** Toggle the chat widget open/closed. */\n  public toggle(): void {\n    if (this.isOpen) {\n      this.close();\n    } else {\n      this.open();\n    }\n  }\n\n  /** Open the chat and immediately send a prompt. */\n  public openWithPrompt(prompt: string): void {\n    this.pendingPrompt = prompt;\n    this.open();\n    this.sendPendingPrompt();\n  }\n\n  /** Get the current widget state. */\n  public getState(): YakEmbedState {\n    return {\n      isOpen: this.isOpen,\n      isReady: this.isReady,\n      isLoading: this.isOpen && !this.isReady,\n      isExpanded: this.isExpanded,\n    };\n  }\n\n  /** Subscribe to state changes. Returns an unsubscribe function. */\n  public onStateChange(listener: YakEmbedStateListener): () => void {\n    this.stateListeners.add(listener);\n    return () => {\n      this.stateListeners.delete(listener);\n    };\n  }\n\n  // \u2500\u2500 Public voice API \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\n  /** Start a voice session. Must be invoked from a user gesture. */\n  public voiceStart(): Promise<void> {\n    return this.voice ? this.voice.start() : Promise.resolve();\n  }\n\n  /** Stop the current voice session. */\n  public voiceStop(): Promise<void> {\n    return this.voice ? this.voice.stop() : Promise.resolve();\n  }\n\n  /** Toggle: start if idle/error, stop if active. */\n  public async voiceToggle(): Promise<void> {\n    if (!this.voice) return;\n    const state = this.voice.getState().state;\n    if (state === \"idle\" || state === \"error\") {\n      await this.voice.start();\n    } else if (state === \"listening\" || state === \"speaking\" || state === \"thinking\") {\n      await this.voice.stop();\n    }\n  }\n\n  /** Current voice machine snapshot. */\n  public getVoiceState(): VoiceMachine {\n    return this.voice ? this.voice.getState() : INITIAL_VOICE_MACHINE;\n  }\n\n  /** Subscribe to voice state changes. */\n  public onVoiceStateChange(listener: VoiceStateListener): () => void {\n    this.voiceListeners.add(listener);\n    return () => {\n      this.voiceListeners.delete(listener);\n    };\n  }\n\n  // \u2500\u2500 DOM creation \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\n  private createPanel(parent: HTMLElement): void {\n    const theme = this.config.theme;\n    const position = theme?.position ?? DEFAULT_POSITION;\n    const colorMode = theme?.colorMode;\n    const displayMode = theme?.displayMode ?? \"chatbox\";\n    const isDrawer = displayMode === \"drawer\";\n\n    // Root overlay (pointer-events: none)\n    this.panelRoot = document.createElement(\"div\");\n    this.panelRoot.className = \"yak-panel-root\";\n\n    // Container\n    this.container = document.createElement(\"div\");\n    const classes = [\"yak-panel-container\"];\n    if (isDrawer) classes.push(\"yak-panel-drawer\");\n    if (colorMode === \"light\") classes.push(\"yak-panel-light\");\n    else if (colorMode === \"dark\") classes.push(\"yak-panel-dark\");\n    this.container.className = classes.join(\" \");\n    this.container.dataset.position = position;\n\n    // Iframe \u2014 set `allow` and `title` BEFORE `src` (some browsers\n    // persist the pre-load Permissions-Policy otherwise).\n    this.iframe = document.createElement(\"iframe\");\n    this.iframe.allow = \"clipboard-write\";\n    this.iframe.title = \"yak-chat-host\";\n    this.iframe.className = \"yak-panel-iframe\";\n    this.iframe.src = this.client.getEmbedUrl();\n\n    this.iframe.addEventListener(\"load\", () => {\n      this.client.setIframeWindow(this.iframe?.contentWindow ?? null);\n    });\n\n    this.container.appendChild(this.iframe);\n    this.panelRoot.appendChild(this.container);\n    parent.appendChild(this.panelRoot);\n\n    // Set up mobile detection\n    this.mobileQuery = window.matchMedia(\"(max-width: 640px)\");\n    this.mobileHandler = (e: MediaQueryListEvent) => {\n      this.notifyIframeFullscreen(e.matches);\n    };\n    this.mobileQuery.addEventListener(\"change\", this.mobileHandler);\n  }\n\n  private createTrigger(parent: HTMLElement): void {\n    const theme = this.config.theme;\n    const position: WidgetPosition = theme?.position ?? DEFAULT_POSITION;\n    const colorMode = theme?.colorMode;\n    const triggerConfig = typeof this.config.trigger === \"object\" ? this.config.trigger : {};\n\n    this.triggerEl = document.createElement(\"div\");\n    this.triggerEl.dataset.position = position;\n    this.triggerEl.dataset.mode = this.mode;\n    this.triggerEl.className = this.buildTriggerClasses(colorMode, triggerConfig);\n    this.applyTriggerCustomColors(triggerConfig);\n\n    // Logo circle on the left\n    const iconBg = document.createElement(\"div\");\n    iconBg.className = \"yak-widget-icon-bg\";\n    const logoImg = document.createElement(\"img\");\n    logoImg.src = `${this.client.getIframeOrigin()}/logo.svg`;\n    logoImg.alt = \"\";\n    logoImg.width = 24;\n    logoImg.height = 24;\n    logoImg.className = \"yak-widget-icon\";\n    iconBg.appendChild(logoImg);\n    this.triggerEl.appendChild(iconBg);\n\n    // Chat icon button\n    if (this.mode === \"chat\" || this.mode === \"both\") {\n      this.chatButton = document.createElement(\"button\");\n      this.chatButton.type = \"button\";\n      this.chatButton.className = \"yak-widget-trigger-icon-btn\";\n      this.chatButton.dataset.action = \"chat\";\n      this.chatButton.setAttribute(\"aria-label\", \"Open chat\");\n      this.chatButton.innerHTML = MESSAGE_CIRCLE_SVG;\n      this.chatButton.addEventListener(\"click\", () => this.open());\n      this.triggerEl.appendChild(this.chatButton);\n    }\n\n    // Voice icon button\n    if (this.mode === \"voice\" || this.mode === \"both\") {\n      this.voiceButton = document.createElement(\"button\");\n      this.voiceButton.type = \"button\";\n      this.voiceButton.className = \"yak-widget-trigger-icon-btn\";\n      this.voiceButton.dataset.action = \"voice\";\n      this.voiceButton.dataset.state = \"idle\";\n      this.voiceButton.setAttribute(\"aria-label\", VOICE_STATE_ARIA.idle);\n      this.voiceButton.innerHTML = AUDIO_LINES_SVG;\n      this.voiceButton.addEventListener(\"click\", () => {\n        void this.voiceToggle();\n      });\n      this.triggerEl.appendChild(this.voiceButton);\n    }\n\n    parent.appendChild(this.triggerEl);\n  }\n\n  private buildTriggerClasses(\n    colorMode: string | undefined,\n    triggerConfig: TriggerButtonConfig\n  ): string {\n    const classes = [\"yak-widget-trigger\"];\n    if (colorMode === \"light\") classes.push(\"yak-widget-light\");\n    else if (colorMode === \"dark\") classes.push(\"yak-widget-dark\");\n\n    const hasLightCustom =\n      triggerConfig.lightButton?.background ||\n      triggerConfig.lightButton?.color ||\n      triggerConfig.lightButton?.border;\n    const hasDarkCustom =\n      triggerConfig.darkButton?.background ||\n      triggerConfig.darkButton?.color ||\n      triggerConfig.darkButton?.border;\n\n    if (colorMode === \"light\" && hasLightCustom) classes.push(\"yak-widget-custom-light\");\n    else if (colorMode === \"dark\" && hasDarkCustom) classes.push(\"yak-widget-custom-dark\");\n\n    return classes.join(\" \");\n  }\n\n  private applyTriggerCustomColors(triggerConfig: TriggerButtonConfig): void {\n    if (!this.triggerEl) return;\n    const { lightButton, darkButton } = triggerConfig;\n\n    const hasLightCustom = lightButton?.background || lightButton?.color || lightButton?.border;\n    const hasDarkCustom = darkButton?.background || darkButton?.color || darkButton?.border;\n\n    if (hasLightCustom || hasDarkCustom) {\n      const vars: [string, string | undefined][] = [\n        [\"--yak-btn-light-bg\", lightButton?.background],\n        [\"--yak-btn-light-color\", lightButton?.color],\n        [\"--yak-btn-light-border\", lightButton?.border],\n        [\"--yak-btn-dark-bg\", darkButton?.background],\n        [\"--yak-btn-dark-color\", darkButton?.color],\n        [\"--yak-btn-dark-border\", darkButton?.border],\n      ];\n      for (const [prop, value] of vars) {\n        if (value) this.triggerEl.style.setProperty(prop, value);\n      }\n    }\n\n    if (hasLightCustom) this.triggerEl.dataset.hasLightCustom = \"true\";\n    if (hasDarkCustom) this.triggerEl.dataset.hasDarkCustom = \"true\";\n  }\n\n  // \u2500\u2500 Internal state management \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\n  private updatePanelState(): void {\n    if (!this.container) return;\n    this.container.dataset.open = String(this.isOpen && this.isReady);\n    this.container.dataset.expanded = String(this.isExpanded);\n    if (this.panelRoot) {\n      this.panelRoot.dataset.expanded = String(this.isExpanded);\n    }\n  }\n\n  private updateChatButtonState(): void {\n    if (!this.chatButton) return;\n    const isLoading = this.isOpen && !this.isReady;\n    this.chatButton.disabled = isLoading;\n    this.chatButton.setAttribute(\"aria-label\", isLoading ? \"Loading chat\" : \"Open chat\");\n\n    if (isLoading) {\n      this.chatButton.innerHTML = `<span class=\"yak-widget-spinner\" aria-hidden=\"true\"></span>`;\n    } else {\n      this.chatButton.innerHTML = MESSAGE_CIRCLE_SVG;\n    }\n  }\n\n  private updateVoiceButtonState(): void {\n    if (!this.voiceButton) return;\n    const state = this.voiceMachine.state;\n    this.voiceButton.dataset.state = state;\n    this.voiceButton.setAttribute(\"aria-label\", VOICE_STATE_ARIA[state]);\n    this.voiceButton.disabled = state === \"connecting\";\n    this.voiceButton.innerHTML = this.iconForVoiceState(state);\n  }\n\n  private iconForVoiceState(state: VoiceState): string {\n    if (state === \"connecting\") {\n      return `<span class=\"yak-widget-spinner\" aria-hidden=\"true\"></span>`;\n    }\n    if (state === \"listening\" || state === \"speaking\" || state === \"thinking\") {\n      return STOP_SVG;\n    }\n    return AUDIO_LINES_SVG;\n  }\n\n  private sendPendingPrompt(): void {\n    if (!this.pendingPrompt || !this.isReady) return;\n    logger.debug(\"Sending pending prompt:\", this.pendingPrompt);\n    this.client.sendPrompt(this.pendingPrompt);\n    this.pendingPrompt = null;\n  }\n\n  private sendFocusIfOpen(): void {\n    if (this.isOpen && this.isReady) {\n      // Re-assert frame focus before asking the widget to focus its composer.\n      // On first open this runs from the ready handshake (see `onReady`), after\n      // the iframe has loaded \u2014 so the gesture-time focus in `open()` is\n      // reinforced now that there's a real document to receive it.\n      this.iframe?.contentWindow?.focus();\n      this.client.sendFocus();\n    }\n  }\n\n  private notifyMobileState(): void {\n    if (this.mobileQuery) {\n      this.notifyIframeFullscreen(this.mobileQuery.matches);\n    }\n  }\n\n  private notifyIframeFullscreen(isFullscreen: boolean): void {\n    if (!this.iframe?.contentWindow) return;\n    const msg: IframeMessageFromHost = {\n      type: \"yak:viewport\",\n      payload: { fullscreen: isFullscreen },\n    };\n    this.iframe.contentWindow.postMessage(msg, this.client.getIframeOrigin());\n  }\n\n  private notifyListeners(): void {\n    const state = this.getState();\n    for (const listener of this.stateListeners) {\n      try {\n        listener(state);\n      } catch (err) {\n        logger.warn(\"Error in state listener:\", err);\n      }\n    }\n  }\n}\n", "import { logger } from \"./logger.js\";\nimport type { ChatConfig } from \"./types/config.js\";\nimport type {\n  ToolAdapter,\n  ToolCallHandler,\n  ToolDefinition,\n  ToolManifest,\n  YakToolset,\n} from \"./types/tools.js\";\n\ntype ResolvedAdapter = {\n  adapter: ToolAdapter;\n  tools: ToolDefinition[];\n};\n\n/**\n * Compose client-side {@link ToolAdapter}s into ONE merged tool manifest and ONE\n * routed `onToolCall`. This is the single injection point for \"available tools\"\n * into the iframe: every adapter \u2014 client-side (GraphQL/REST/custom, run by your own\n * `execute` callback) or server-relayed ({@link createYakServerAdapter}) \u2014 contributes\n * its tools to the merged manifest and its executor to the routed handler. Because every\n * tool now flows through `onToolCall`, they all surface through `onToolCallComplete` /\n * `useYakToolEvent`.\n *\n * @example\n * ```tsx\n * const toolset = createYakToolset([\n *   createYakServerAdapter({ endpoint: \"/api/yak\" }),               // tRPC + custom server tools\n *   createGraphQLToolAdapter({ name: \"shop\", schema, execute: runShopQuery }),\n *   createRESTToolAdapter({ name: \"billing\", spec, execute: callBillingApi }),\n * ]);\n *\n * <YakProvider\n *   getConfig={async () => ({ routes, ...(await toolset.getConfig()) })}\n *   onToolCall={toolset.onToolCall}\n * />;\n * ```\n */\nexport function createYakToolset(adapters: ToolAdapter[]): YakToolset {\n  let cache: ResolvedAdapter[] | null = null;\n\n  async function resolve(force = false): Promise<ResolvedAdapter[]> {\n    if (cache && !force) return cache;\n    cache = await Promise.all(\n      adapters.map(async (adapter) => ({ adapter, tools: await adapter.getTools() }))\n    );\n    return cache;\n  }\n\n  async function getConfig(): Promise<{ tools: ToolManifest }> {\n    // Refresh on every config load \u2014 tools may depend on page/user.\n    const resolved = await resolve(true);\n    const tools: ToolDefinition[] = [];\n    const seen = new Set<string>();\n\n    for (const { adapter, tools: adapterTools } of resolved) {\n      for (const tool of adapterTools) {\n        if (seen.has(tool.name)) {\n          logger.warn(\n            `Duplicate tool name \"${tool.name}\"; keeping the first and ignoring the one from adapter \"${adapter.id ?? \"unknown\"}\".`\n          );\n          continue;\n        }\n        seen.add(tool.name);\n        tools.push(tool);\n      }\n    }\n\n    return {\n      tools: {\n        tools,\n        sources: resolved.map(({ adapter, tools: adapterTools }, index) => ({\n          id: adapter.id ?? `adapter-${index}`,\n          count: adapterTools.length,\n        })),\n      },\n    };\n  }\n\n  const onToolCall: ToolCallHandler = async (name, args) => {\n    // Fast path: explicit ownership predicates need no resolution.\n    for (const adapter of adapters) {\n      if (adapter.ownsTool?.(name)) {\n        return adapter.execute(name, args);\n      }\n    }\n    // Otherwise route by the adapter's resolved tool names.\n    const resolved = await resolve();\n    for (const { adapter, tools } of resolved) {\n      if (adapter.ownsTool) continue; // already consulted above\n      if (tools.some((tool) => tool.name === name)) {\n        return adapter.execute(name, args);\n      }\n    }\n    throw new Error(`Unknown tool: ${name}`);\n  };\n\n  return { getConfig, onToolCall };\n}\n\n/** Config for {@link createYakServerAdapter}. */\nexport type YakServerAdapterConfig = {\n  /**\n   * Endpoint mounting `createYakHandler` / `createNextYakHandler`. GET returns the\n   * tool manifest; POST `{ name, args }` executes a tool. Default: `\"/api/yak\"`.\n   */\n  endpoint?: string;\n  /** Stable id for diagnostics. Default: `\"server\"`. */\n  id?: string;\n  /** Extra headers (e.g. auth) applied to both the GET manifest load and POST execution. */\n  headers?: HeadersInit | (() => HeadersInit | Promise<HeadersInit>);\n};\n\nasync function buildHeaders(\n  source: YakServerAdapterConfig[\"headers\"],\n  base?: Record<string, string>\n): Promise<Headers> {\n  const headers = new Headers(base);\n  const resolved = typeof source === \"function\" ? await source() : source;\n  if (resolved) {\n    for (const [key, value] of new Headers(resolved)) {\n      headers.set(key, value);\n    }\n  }\n  return headers;\n}\n\n/**\n * Bridge a server-side yak handler ({@link createYakHandler} /\n * `createNextYakHandler`, e.g. fronting the `@yak-io/trpc` adapter) into a\n * client-side {@link ToolAdapter}, so server-relayed tools merge into the same\n * manifest + `onToolCall` as browser-executed adapters.\n */\nexport function createYakServerAdapter(config: YakServerAdapterConfig = {}): ToolAdapter {\n  const endpoint = config.endpoint ?? \"/api/yak\";\n\n  return {\n    id: config.id ?? \"server\",\n    getTools: async () => {\n      const res = await fetch(endpoint, { headers: await buildHeaders(config.headers) });\n      if (!res.ok) {\n        throw new Error(`Failed to load tools from ${endpoint} (${res.status})`);\n      }\n      const chatConfig = (await res.json()) as ChatConfig;\n      return chatConfig.tools?.tools ?? [];\n    },\n    execute: async (name, args) => {\n      const res = await fetch(endpoint, {\n        method: \"POST\",\n        headers: await buildHeaders(config.headers, { \"Content-Type\": \"application/json\" }),\n        body: JSON.stringify({ name, args }),\n      });\n      const data = (await res.json().catch(() => ({}))) as {\n        ok?: boolean;\n        result?: unknown;\n        error?: string;\n      };\n      if (!res.ok || !data.ok) {\n        throw new Error(data.error ?? `Tool \"${name}\" failed (${res.status})`);\n      }\n      return data.result;\n    },\n  };\n}\n"],
  "mappings": "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AA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAsFT;AAEA,SAAS,mBAA2B;AAClC,SAAO;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAA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",
  "names": ["window", "body"]
}
