{"version":3,"sources":["../src/markdown-parsers-entry.ts","../src/voice/audio-playback-manager.ts","../src/voice/runtype-speech-engine.ts","../src/voice/fallback-speech-engine.ts","../src/voice/runtype-tts-entry.ts","../src/index.ts","../src/markdown-parsers-eager.ts","../src/markdown-parsers-loader.ts","../src/postprocessors.ts","../src/utils/sanitize.ts","../src/utils/streaming-table.ts","../src/webmcp-bridge.ts","../src/utils/target.ts","../src/components/ask-user-question-bubble.ts","../src/utils/dom.ts","../src/suggest-replies-tool.ts","../src/ask-user-question-tool.ts","../src/utils/formatting.ts","../package.json","../src/version.ts","../src/client.ts","../src/utils/message-id.ts","../src/utils/content.ts","../src/voice/runtype-voice-provider.ts","../src/voice/browser-voice-provider.ts","../src/voice/voice-factory.ts","../src/voice/browser-speech-engine.ts","../src/voice/read-aloud-controller.ts","../src/utils/speech-text.ts","../src/voice/runtype-tts-loader.ts","../src/session.ts","../src/utils/icons.ts","../src/utils/attachment-manager.ts","../src/utils/deep-merge.ts","../src/defaults.ts","../src/utils/tokens.ts","../src/utils/theme.ts","../src/utils/morph.ts","../src/utils/copy-selection.ts","../src/utils/composer-history.ts","../src/utils/message-fingerprint.ts","../src/utils/auto-follow.ts","../src/utils/constants.ts","../src/utils/stream-animation.ts","../src/utils/overlay-host-stacking.ts","../src/utils/scroll-lock.ts","../src/utils/dock.ts","../src/utils/positioning.ts","../src/components/header-parts.ts","../src/components/header-builder.ts","../src/utils/dropdown.ts","../src/utils/buttons.ts","../src/components/header-layouts.ts","../src/components/composer-parts.ts","../src/components/composer-builder.ts","../src/components/pill-composer-builder.ts","../src/components/panel.ts","../src/components/launcher.ts","../src/components/widget-view.ts","../src/components/message-bubble.ts","../src/components/reasoning-bubble.ts","../src/components/tool-bubble.ts","../src/components/approval-bubble.ts","../src/plugin-kit.ts","../src/components/approval-actions.ts","../src/components/suggestions.ts","../src/utils/event-stream-buffer.ts","../src/utils/event-stream-store.ts","../src/utils/throughput-tracker.ts","../src/components/event-stream-view.ts","../src/components/artifact-card.ts","../src/components/registry.ts","../src/components/artifact-pane.ts","../src/utils/artifact-gate.ts","../src/utils/artifact-resize.ts","../src/components/forms.ts","../src/plugins/registry.ts","../src/utils/events.ts","../src/utils/actions.ts","../src/utils/storage.ts","../src/utils/component-parser.ts","../src/utils/component-middleware.ts","../src/components/feedback.ts","../src/ui.ts","../src/runtime/host-layout.ts","../src/runtime/init.ts","../src/utils/dom-context.ts","../src/utils/plugins.ts","../src/presets.ts","../src/index-core.ts","../src/utils/code-generators.ts","../src/components/demo-carousel.ts"],"sourcesContent":["import { Marked } from \"marked\";\nimport DOMPurify from \"dompurify\";\n\nexport { Marked, DOMPurify };\n","import type { PcmStreamPlayer } from \"../types\";\n\n/**\n * AudioPlaybackManager\n *\n * Manages streaming playback of PCM audio chunks via the Web Audio API.\n * Receives raw PCM data (24 kHz, 16-bit signed little-endian, mono),\n * converts to Float32 AudioBuffers, and schedules gap-free sequential\n * playback using AudioBufferSourceNode.\n *\n * Works on all browsers including iOS Safari (no MediaSource dependency).\n *\n * This is the default {@link PcmStreamPlayer} for both the realtime voice\n * provider and the `provider: 'runtype'` \"Read aloud\" path. It runs entirely on\n * the main thread, so it stays in the main bundle (no AudioWorklet module). The\n * jitter-buffered AudioWorklet player (`createPcmStreamPlayer` from\n * `@runtypelabs/persona/voice-worklet-player`) is the higher-quality, opt-in\n * alternative — inject it via `voiceRecognition.provider.runtype.createPlaybackEngine`\n * or `textToSpeech.createPlaybackEngine` and it lands in the consumer's bundle.\n *\n * With the default `prebufferMs: 0` this behaves exactly like a bare scheduler\n * (the realtime path), so that path is unchanged. A non-zero `prebufferMs`\n * (the read-aloud path passes ~200) holds incoming audio until a waterline of\n * samples is buffered before starting, and re-enters buffering on underrun —\n * softening the schedule-clock snap that a hand-scheduled BufferSource otherwise\n * turns into a click on bursty HTTP-streamed audio. This is a pragmatic\n * approximation of the worklet's audio-thread silence, not parity.\n */\nexport class AudioPlaybackManager implements PcmStreamPlayer {\n  private ctx: AudioContext | null = null;\n  private nextStartTime = 0;\n  private activeSources: AudioBufferSourceNode[] = [];\n  private finishedCallbacks: (() => void)[] = [];\n  private startedCallbacks: (() => void)[] = [];\n  private playing = false;\n  private streamEnded = false;\n  private pendingCount = 0;\n  // Fires once per playback session when the first sample is actually scheduled\n  // (loading→playing). Cleared by flush(); a mid-reply underrun does not re-fire.\n  private started = false;\n  // Explicit user pause via pause(); kept separate from the AudioContext's\n  // autoplay-policy suspension so ensureContext() doesn't auto-resume over it.\n  private userPaused = false;\n\n  // Prebuffer gate: while `buffering`, incoming samples accumulate in\n  // `pendingBuffers` until they reach `waterlineSamples`, then release into the\n  // scheduler. With `waterlineSamples === 0` the gate is off (realtime default).\n  private buffering: boolean;\n  private pendingBuffers: Float32Array[] = [];\n  private pendingSamples = 0;\n\n  // PCM format constants\n  private readonly sampleRate: number;\n  private readonly waterlineSamples: number;\n\n  // Remainder byte from a previous chunk when the chunk had an odd byte count.\n  // Network chunks don't respect 2-byte sample boundaries, so we carry over\n  // the orphaned byte and prepend it to the next chunk.\n  private remainder: Uint8Array | null = null;\n\n  constructor(sampleRate = 24000, options: { prebufferMs?: number } = {}) {\n    this.sampleRate = sampleRate;\n    const prebufferMs = Math.max(0, options.prebufferMs ?? 0);\n    this.waterlineSamples = Math.round((sampleRate * prebufferMs) / 1000);\n    this.buffering = this.waterlineSamples > 0;\n  }\n\n  /**\n   * Ensure AudioContext is created and running.\n   * Must be called after a user gesture on iOS Safari.\n   */\n  private ensureContext(): AudioContext {\n    if (!this.ctx) {\n      const w = typeof window !== \"undefined\" ? (window as any) : undefined;\n      if (!w) throw new Error(\"AudioPlaybackManager requires a browser environment\");\n      const AudioCtx = w.AudioContext || w.webkitAudioContext;\n      this.ctx = new AudioCtx({ sampleRate: this.sampleRate }) as AudioContext;\n    }\n    const ctx = this.ctx!;\n    // Resume if suspended (autoplay policy) — but never override an explicit\n    // user pause(): more audio may still stream in while paused.\n    if (ctx.state === \"suspended\" && !this.userPaused) {\n      ctx.resume();\n    }\n    return ctx;\n  }\n\n  /**\n   * Enqueue a PCM chunk for playback.\n   * @param pcmData Raw PCM bytes (16-bit signed LE mono)\n   */\n  enqueue(pcmData: Uint8Array): void {\n    if (pcmData.length === 0) return;\n\n    // Prepend any remainder byte from the previous chunk\n    let data = pcmData;\n    if (this.remainder) {\n      const merged = new Uint8Array(this.remainder.length + pcmData.length);\n      merged.set(this.remainder);\n      merged.set(pcmData, this.remainder.length);\n      data = merged;\n      this.remainder = null;\n    }\n\n    // If odd byte count, save the trailing byte for next chunk\n    if (data.length % 2 !== 0) {\n      this.remainder = new Uint8Array([data[data.length - 1]]);\n      data = data.subarray(0, data.length - 1);\n    }\n\n    if (data.length === 0) return;\n\n    const float32 = this.pcmToFloat32(data);\n    if (float32.length === 0) return;\n\n    if (this.buffering) {\n      // Hold until the prebuffer waterline fills, then release as a batch.\n      this.pendingBuffers.push(float32);\n      this.pendingSamples += float32.length;\n      if (this.pendingSamples >= this.waterlineSamples) this.releaseBuffer();\n    } else {\n      this.scheduleSamples(float32);\n    }\n  }\n\n  /**\n   * Signal that no more chunks will arrive.\n   * The onFinished callback fires after all queued audio has played.\n   */\n  markStreamEnd(): void {\n    // A reply shorter than the prebuffer never reaches the waterline; release\n    // whatever we held so it still plays.\n    if (this.pendingBuffers.length > 0) this.releaseBuffer();\n    this.streamEnded = true;\n    this.checkFinished();\n  }\n\n  /**\n   * Immediately stop all playback and discard queued audio.\n   */\n  flush(): void {\n    for (const source of this.activeSources) {\n      try {\n        source.stop();\n        source.disconnect();\n      } catch {\n        // Ignore errors from already-stopped sources\n      }\n    }\n    this.activeSources = [];\n    this.pendingCount = 0;\n    this.nextStartTime = 0;\n    this.playing = false;\n    this.streamEnded = false;\n    this.finishedCallbacks = [];\n    this.startedCallbacks = [];\n    this.remainder = null;\n    // Reset the prebuffer gate and the started latch for the next reply.\n    this.pendingBuffers = [];\n    this.pendingSamples = 0;\n    this.buffering = this.waterlineSamples > 0;\n    this.started = false;\n  }\n\n  /**\n   * Whether audio is currently playing or queued.\n   */\n  isPlaying(): boolean {\n    return this.playing;\n  }\n\n  /**\n   * Register a callback for when all queued audio finishes playing.\n   */\n  onFinished(callback: () => void): void {\n    this.finishedCallbacks.push(callback);\n  }\n\n  /**\n   * Register a callback fired once when audible playback first begins (the first\n   * sample is scheduled, after any prebuffer). Cleared by {@link flush}; a\n   * mid-reply underrun re-buffer does not re-fire it.\n   */\n  onStarted(callback: () => void): void {\n    this.startedCallbacks.push(callback);\n  }\n\n  /**\n   * Pause playback. Suspends the AudioContext clock; queued/scheduled audio\n   * freezes in place and {@link resume} continues exactly where it left off.\n   */\n  pause(): void {\n    this.userPaused = true;\n    if (this.ctx && this.ctx.state === \"running\") void this.ctx.suspend();\n  }\n\n  /** Resume playback after {@link pause}. */\n  resume(): void {\n    this.userPaused = false;\n    if (this.ctx && this.ctx.state === \"suspended\") void this.ctx.resume();\n  }\n\n  /**\n   * Clean up AudioContext resources.\n   */\n  async destroy(): Promise<void> {\n    this.flush();\n    if (this.ctx) {\n      await this.ctx.close();\n      this.ctx = null;\n    }\n  }\n\n  /** Release held prebuffer samples into the scheduler in arrival order. */\n  private releaseBuffer(): void {\n    this.buffering = false;\n    const held = this.pendingBuffers;\n    this.pendingBuffers = [];\n    this.pendingSamples = 0;\n    for (const samples of held) this.scheduleSamples(samples);\n  }\n\n  /** Schedule one Float32 sample block for gap-free playback. */\n  private scheduleSamples(float32: Float32Array): void {\n    if (float32.length === 0) return;\n    const ctx = this.ensureContext();\n\n    const buffer = ctx.createBuffer(1, float32.length, this.sampleRate);\n    buffer.getChannelData(0).set(float32);\n\n    const source = ctx.createBufferSource();\n    source.buffer = buffer;\n    source.connect(ctx.destination);\n\n    const now = ctx.currentTime;\n    if (this.nextStartTime === 0) {\n      // Fresh start (first audio of this session, or after flush).\n      this.nextStartTime = now;\n    } else if (this.nextStartTime < now) {\n      // Underrun: the playhead caught up to the queue. Snap to now (a small,\n      // unavoidable gap) and, when a prebuffer is configured, re-enter buffering\n      // so subsequent chunks re-accumulate before scheduling — collapsing a\n      // train of clicks into a single rebuffer.\n      this.nextStartTime = now;\n      if (this.waterlineSamples > 0) this.buffering = true;\n    }\n    source.start(this.nextStartTime);\n    this.nextStartTime += buffer.duration;\n\n    this.activeSources.push(source);\n    this.pendingCount++;\n    this.playing = true;\n\n    if (!this.started) {\n      this.started = true;\n      const cbs = this.startedCallbacks.slice();\n      this.startedCallbacks = [];\n      for (const cb of cbs) cb();\n    }\n\n    source.onended = () => {\n      const idx = this.activeSources.indexOf(source);\n      if (idx !== -1) this.activeSources.splice(idx, 1);\n      this.pendingCount--;\n      this.checkFinished();\n    };\n  }\n\n  private checkFinished(): void {\n    // Fire once the stream has ended and nothing is scheduled or held. No\n    // `playing` precondition: an empty reply (markStreamEnd with no audio) must\n    // still resolve to idle, matching the worklet's immediate 'drained'.\n    if (\n      this.streamEnded &&\n      this.pendingCount <= 0 &&\n      this.pendingBuffers.length === 0\n    ) {\n      this.playing = false;\n      this.streamEnded = false;\n      const cbs = this.finishedCallbacks.slice();\n      this.finishedCallbacks = [];\n      for (const cb of cbs) cb();\n    }\n  }\n\n  /**\n   * Convert 16-bit signed LE PCM to Float32 samples in [-1, 1].\n   */\n  private pcmToFloat32(pcmData: Uint8Array): Float32Array {\n    // 2 bytes per sample (16-bit)\n    const numSamples = Math.floor(pcmData.length / 2);\n    const float32 = new Float32Array(numSamples);\n    const view = new DataView(pcmData.buffer, pcmData.byteOffset, pcmData.byteLength);\n\n    for (let i = 0; i < numSamples; i++) {\n      const int16 = view.getInt16(i * 2, true); // little-endian\n      float32[i] = int16 / 32768;\n    }\n\n    return float32;\n  }\n}\n","// Runtype Speech Engine (streaming TTS)\n//\n// Built-in `SpeechEngine` that backs the per-message \"Read aloud\" action (and\n// the auto-speak path) with Runtype-hosted text-to-speech. It is the client\n// half of the \"Option A\" HTTP synthesize design: a stateless\n//\n//   POST {host}/v1/agents/:agentId/speak  ->  streamed PCM16 / 24 kHz / mono\n//\n// The `clientToken` is browser-safe (same one the chat widget uses, scoped by\n// `allowedOrigins`), so this calls Runtype directly from the page — no proxy.\n// The streamed PCM is fed chunk-by-chunk into a {@link PcmStreamPlayer}, so this\n// engine stays tiny: fetch -> enqueue chunks -> markStreamEnd. The player owns\n// prebuffering, gapless scheduling, graceful underrun, and pause/resume.\n//\n// By default the player is the in-bundle, main-thread `AudioPlaybackManager`\n// (no AudioWorklet module — keeps the main bundle lean). Consumers who want the\n// jitter-buffered AudioWorklet player inject `createPcmStreamPlayer` from\n// `@runtypelabs/persona/voice-worklet-player` via `createPlaybackEngine` (config:\n// `textToSpeech.createPlaybackEngine`); the worklet then lands in their bundle.\n//\n// Wired automatically by `textToSpeech: { provider: 'runtype' }` (see\n// `session.ts`), which derives `host`/`agentId`/`clientToken` from the widget\n// config and — unless `browserFallback: false` — wraps this in a\n// `FallbackSpeechEngine` so a missing endpoint or transient failure falls back\n// to the browser voice instead of erroring.\n\nimport type {\n  PcmStreamPlayer,\n  SpeechCallbacks,\n  SpeechEngine,\n  SpeechRequest,\n} from \"../types\";\nimport { AudioPlaybackManager } from \"./audio-playback-manager\";\n\nexport interface RuntypeSpeechEngineOptions {\n  /**\n   * Runtype API host, e.g. `https://api.runtype.com` (typically the widget's\n   * `apiUrl`). A trailing slash is tolerated.\n   */\n  host: string;\n  /** Agent whose configured voice synthesizes the text. */\n  agentId: string;\n  /** Browser-safe client token — the same one the chat widget uses. */\n  clientToken: string;\n  /**\n   * Default voice id, used when a `SpeechRequest` doesn't carry its own. When\n   * omitted the agent's configured voice is used.\n   */\n  voice?: string;\n  /**\n   * Audio (ms) the player buffers before the first sample and after an\n   * underrun. Runtype streams steadily, so the default (200) keeps first sound\n   * close to time-to-first-byte while still riding out small hiccups. Applies to\n   * the default {@link AudioPlaybackManager}; a custom `createPlaybackEngine` is\n   * responsible for its own prebuffer.\n   */\n  prebufferMs?: number;\n  /**\n   * Factory for the streaming PCM player. Defaults to the in-bundle, main-thread\n   * {@link AudioPlaybackManager} (with `prebufferMs`). Pass `createPcmStreamPlayer`\n   * from `@runtypelabs/persona/voice-worklet-player` for the jitter-buffered\n   * AudioWorklet player (it then ships in your bundle, not Persona's). May be\n   * async — it is resolved on first playback, inside the user gesture.\n   */\n  createPlaybackEngine?: () => PcmStreamPlayer | Promise<PcmStreamPlayer>;\n  /**\n   * Optional hook for surfacing fetch/stream failures (a missing endpoint, an\n   * expired token, an upstream 4xx) to a log or telemetry. The widget itself\n   * only returns the read-aloud button to idle (or, with a fallback engine,\n   * silently switches to the browser voice), so without this the reason is\n   * invisible.\n   */\n  onError?: (error: Error) => void;\n}\n\n/** Strip a trailing slash so `${host}/v1/...` never doubles up. */\nfunction normalizeHost(host: string): string {\n  return host.replace(/\\/+$/, \"\");\n}\n\n/** Streaming `SpeechEngine` backed by Runtype's `/v1/agents/:id/speak`. */\nexport class RuntypeSpeechEngine implements SpeechEngine {\n  readonly id = \"runtype-tts\";\n  // The PCM player pauses/resumes via AudioContext.suspend() — solid, unlike\n  // speechSynthesis.pause().\n  readonly supportsPause = true;\n\n  private player: PcmStreamPlayer | null = null;\n  private playerPromise: Promise<PcmStreamPlayer> | null = null;\n  // Bumped on every speak()/stop() so a superseded request's async callbacks and\n  // its in-flight stream read loop become no-ops.\n  private generation = 0;\n\n  constructor(private readonly opts: RuntypeSpeechEngineOptions) {}\n\n  // Create one player lazily (a worklet engine's addModule is async), then reuse\n  // it across speaks; flush() between replies clears the queue without tearing\n  // down the AudioContext. Defaults to the in-bundle AudioPlaybackManager.\n  private ensurePlayer(): Promise<PcmStreamPlayer> {\n    return (this.playerPromise ??= Promise.resolve(\n      this.opts.createPlaybackEngine\n        ? this.opts.createPlaybackEngine()\n        : new AudioPlaybackManager(24000, {\n            prebufferMs: this.opts.prebufferMs ?? 200,\n          }),\n    ).then((player) => (this.player = player)));\n  }\n\n  speak(request: SpeechRequest, callbacks: SpeechCallbacks): void {\n    const gen = ++this.generation;\n    // Run the async fetch/stream without making speak() itself async — the\n    // widget surfaces the time until audio starts as the \"loading\" state.\n    void this.run(gen, request, callbacks);\n  }\n\n  private async run(\n    gen: number,\n    request: SpeechRequest,\n    callbacks: SpeechCallbacks,\n  ): Promise<void> {\n    try {\n      const player = await this.ensurePlayer();\n      if (gen !== this.generation) return; // superseded while the worklet booted\n      player.flush(); // drop any prior playback (and its callbacks)\n      player.resume(); // clear a prior pause so this reply isn't stuck suspended\n\n      // Drive read-aloud state from the player, not from chunk arrival: onStarted\n      // fires when the prebuffer fills and audio is actually audible (loading ->\n      // playing); onFinished when it drains (-> idle). An empty stream produces\n      // no onStarted and an immediate onFinished, so the UI skips straight back\n      // to idle without a phantom \"playing\".\n      player.onStarted(() => {\n        if (gen === this.generation) callbacks.onStart?.();\n      });\n      player.onFinished(() => {\n        if (gen === this.generation) callbacks.onEnd?.();\n      });\n\n      const url = `${normalizeHost(this.opts.host)}/v1/agents/${encodeURIComponent(\n        this.opts.agentId,\n      )}/speak`;\n      const res = await fetch(url, {\n        method: \"POST\",\n        headers: {\n          \"Content-Type\": \"application/json\",\n          // Match Runtype's client-token auth convention. Never placed in the\n          // URL/query string.\n          Authorization: `Bearer ${this.opts.clientToken}`,\n        },\n        body: JSON.stringify({\n          text: request.text,\n          voice: request.voice ?? this.opts.voice,\n          format: \"pcm\",\n        }),\n      });\n      if (gen !== this.generation) return; // superseded while awaiting headers\n      if (!res.ok || !res.body) throw new Error(await describeError(res));\n\n      const reader = res.body.getReader();\n      for (;;) {\n        const { done, value } = await reader.read();\n        if (gen !== this.generation) {\n          // A newer speak()/stop() won — stop pulling and release the stream.\n          await reader.cancel().catch(() => {});\n          return;\n        }\n        if (done) break;\n        if (value && value.byteLength > 0) player.enqueue(value);\n      }\n\n      player.markStreamEnd();\n    } catch (err) {\n      if (gen !== this.generation) return; // error from a superseded request\n      const error = err instanceof Error ? err : new Error(String(err));\n      this.opts.onError?.(error); // surface the reason (log, telemetry, …)\n      callbacks.onError?.(error); // and let the widget (or fallback) react\n    }\n  }\n\n  pause(): void {\n    this.player?.pause();\n  }\n\n  resume(): void {\n    this.player?.resume();\n  }\n\n  stop(): void {\n    this.generation++; // invalidate any in-flight stream + pending onFinished\n    this.player?.flush();\n  }\n\n  destroy(): void {\n    this.generation++;\n    void this.player?.destroy();\n    this.player = null;\n    this.playerPromise = null;\n  }\n}\n\n/** Best-effort human-readable message from a non-OK speak response. */\nasync function describeError(res: Response): Promise<string> {\n  try {\n    const data = (await res.json()) as { error?: string; detail?: string };\n    return data.detail\n      ? `${data.error ?? `Runtype TTS ${res.status}`}: ${data.detail}`\n      : data.error ?? `Runtype TTS request failed (${res.status})`;\n  } catch {\n    return `Runtype TTS request failed (${res.status})`;\n  }\n}\n","// Fallback Speech Engine\n//\n// Composes a primary `SpeechEngine` with a fallback one (typically a hosted\n// engine + the browser Web Speech API). If the primary fails *before* any audio\n// is audible — a missing/404 endpoint, a network error, an auth failure — the\n// fallback transparently takes over for that utterance, so the \"Read aloud\"\n// button is never left broken. A failure that happens *after* playback has\n// started (a mid-stream drop) is surfaced as a real error instead of restarting\n// from the top.\n//\n// This is what makes `textToSpeech: { provider: 'runtype' }` safe to ship before\n// (or independently of) the Runtype TTS endpoint: it speaks with the browser\n// voice today and auto-upgrades to Runtype voices the moment the endpoint\n// answers. Set `browserFallback: false` to opt out and surface Runtype errors.\n\nimport type { SpeechCallbacks, SpeechEngine, SpeechRequest } from \"../types\";\n\nexport interface FallbackSpeechEngineOptions {\n  /**\n   * Called once when the primary engine fails before audio starts and the\n   * fallback takes over — so a silent downgrade is still observable in dev/\n   * telemetry even though the user keeps hearing speech.\n   */\n  onFallback?: (error: Error) => void;\n}\n\n/** A `SpeechEngine` that falls back from `primary` to `fallback` per utterance. */\nexport class FallbackSpeechEngine implements SpeechEngine {\n  readonly id = \"fallback\";\n\n  // Whichever engine is currently driving playback, so pause/resume/stop route\n  // to the right one after a fallback has (or hasn't) kicked in.\n  private active: SpeechEngine;\n\n  constructor(\n    private readonly primary: SpeechEngine,\n    private readonly fallback: SpeechEngine,\n    private readonly options: FallbackSpeechEngineOptions = {},\n  ) {\n    this.active = primary;\n  }\n\n  // Pause/resume only matters once something is playing, and both built-in\n  // engines support it; report the active engine's capability.\n  get supportsPause(): boolean {\n    return this.active.supportsPause;\n  }\n\n  speak(request: SpeechRequest, callbacks: SpeechCallbacks): void {\n    this.active = this.primary;\n    let started = false;\n\n    this.primary.speak(request, {\n      onStart: () => {\n        started = true;\n        callbacks.onStart?.();\n      },\n      onEnd: () => callbacks.onEnd?.(),\n      onError: (error) => {\n        // A failure once audio is playing is a genuine error — don't restart.\n        if (started) {\n          callbacks.onError?.(error);\n          return;\n        }\n        // Pre-start failure: silently hand the utterance to the fallback.\n        this.options.onFallback?.(error);\n        this.active = this.fallback;\n        this.fallback.speak(request, callbacks);\n      },\n    });\n  }\n\n  pause(): void {\n    this.active.pause();\n  }\n\n  resume(): void {\n    this.active.resume();\n  }\n\n  stop(): void {\n    this.active.stop();\n  }\n\n  destroy(): void {\n    this.primary.destroy?.();\n    this.fallback.destroy?.();\n  }\n}\n","// Standalone entry for the deferred Runtype TTS chunk (`dist/runtype-tts.js`).\n//\n// Bundles the hosted read-aloud engine, its browser-fallback wrapper, and the\n// `AudioPlaybackManager` they depend on, so the IIFE/CDN build can load the\n// whole `provider: 'runtype'` read-aloud path on demand (kept out of\n// `index.global.js`). `session.ts` resolves this module via\n// `runtype-tts-loader.ts`; the loader is overridden in `index-global.ts` to\n// fetch this chunk from a sibling URL. See `runtype-tts-loader.ts`.\nexport { RuntypeSpeechEngine } from \"./runtype-speech-engine\";\nexport { FallbackSpeechEngine } from \"./fallback-speech-engine\";\n","/**\n * npm package entry (`@runtypelabs/persona`).\n *\n * This is a thin barrel over `index-core.ts` (the shared public API) that adds\n * back the **dev/config-tool-only** helpers: `generateCodeSnippet` and\n * `createDemoCarousel`. Those are kept out of `index-core.ts` so the IIFE/CDN\n * build (`index-global.ts`, which re-exports from `index-core.ts`) doesn't ship\n * them: a running widget never needs them, only build-time/demo tooling does.\n *\n * Net effect: npm consumers get the full API (unchanged), while the script-tag\n * `window.AgentWidget` global no longer exposes `generateCodeSnippet` /\n * `createDemoCarousel`.\n */\n\n// Register `marked` + `dompurify` synchronously for the bundled npm build so\n// the synchronous markdown/sanitize API renders on first paint. The IIFE/CDN\n// entry (`index-global.ts`) does NOT import this; it lazy-loads the parsers\n// from the `markdown-parsers.js` chunk instead. Must run before any render.\nimport \"./markdown-parsers-eager\";\n\n// Full public API (everything except the two dev-only helpers below).\nexport * from \"./index-core\";\nexport { default } from \"./index-core\";\n\n// Dev / config-tool helper: generate install snippets from a widget config.\nexport { generateCodeSnippet } from \"./utils/code-generators\";\nexport type {\n  CodeFormat,\n  CodeGeneratorHooks,\n  CodeGeneratorOptions\n} from \"./utils/code-generators\";\n\n// Demo-only component: the examples' showcase carousel.\nexport { createDemoCarousel } from \"./components/demo-carousel\";\nexport type {\n  DemoCarouselItem,\n  DemoCarouselOptions,\n  DemoCarouselHandle\n} from \"./components/demo-carousel\";\n","/**\n * Eager markdown-parser registration for the bundled (ESM / CJS) builds.\n *\n * npm consumers bundle `marked` and `dompurify` directly anyway, so there is no\n * benefit to deferring them — and the public `markdownPostprocessor()` /\n * `createDefaultSanitizer()` API is synchronous. Importing this module for its\n * side effect registers both parsers up front so `getMarkdownParsersSync()`\n * resolves them on the first render.\n *\n * This module is intentionally NOT reachable from `index-global.ts` (the IIFE\n * entry): it statically imports `marked` + `dompurify`, so pulling it into the\n * CDN bundle would defeat the lazy `markdown-parsers.js` chunk. It is imported\n * only from `index.ts` (the npm barrel) and from unit tests that exercise the\n * synchronous parsers directly.\n */\nimport { Marked } from \"marked\";\nimport DOMPurify from \"dompurify\";\nimport { provideMarkdownParsers } from \"./markdown-parsers-loader\";\n\nprovideMarkdownParsers({ Marked, DOMPurify });\n","import type { Marked } from \"marked\";\nimport type DOMPurify from \"dompurify\";\n\nexport type MarkdownParsersModule = {\n  Marked: typeof Marked;\n  DOMPurify: typeof DOMPurify;\n};\n\nlet loader: (() => Promise<MarkdownParsersModule>) | null = null;\nlet moduleCache: MarkdownParsersModule | null = null;\nlet loadPromise: Promise<MarkdownParsersModule> | null = null;\n\nexport const setMarkdownParsersLoader = (l: () => Promise<MarkdownParsersModule>) => {\n  loader = l;\n};\n\n/**\n * Register the parsers synchronously. Used by the ESM/CJS build (where `marked`\n * and `dompurify` are bundled directly via `markdown-parsers-eager.ts`), so\n * `getMarkdownParsersSync()` returns them on the very first render and the\n * synchronous `markdownPostprocessor` / `createDefaultSanitizer` API keeps\n * working without an async round-trip. The IIFE/CDN build never calls this;\n * it lazy-loads the `markdown-parsers.js` chunk instead.\n */\nexport const provideMarkdownParsers = (mod: MarkdownParsersModule): void => {\n  moduleCache = mod;\n};\n\nexport const loadMarkdownParsers = (): Promise<MarkdownParsersModule> => {\n  if (moduleCache) return Promise.resolve(moduleCache);\n  if (loadPromise) return loadPromise;\n  if (!loader) {\n    // Fallback for regular ESM/CJS consumers (they import directly)\n    loadPromise = import(\"./markdown-parsers-entry\").then((mod) => {\n      moduleCache = mod;\n      return mod;\n    });\n    return loadPromise;\n  }\n  loadPromise = loader().then((mod) => {\n    moduleCache = mod;\n    return mod;\n  });\n  return loadPromise;\n};\n\nexport const getMarkdownParsersSync = (): MarkdownParsersModule | null => {\n  return moduleCache;\n};\n","import type { RendererObject } from \"marked\";\nimport type { AgentWidgetMarkdownConfig, AgentWidgetMarkdownRendererOverrides, AgentWidgetMarkdownOptions } from \"./types\";\nimport { getMarkdownParsersSync } from \"./markdown-parsers-loader\";\n\n/**\n * Options for creating a markdown processor\n */\nexport type MarkdownProcessorOptions = {\n  /** Marked parsing options */\n  markedOptions?: AgentWidgetMarkdownOptions;\n  /** Custom renderer overrides */\n  renderer?: AgentWidgetMarkdownRendererOverrides;\n};\n\n/**\n * Converts AgentWidgetMarkdownRendererOverrides to marked's RendererObject format\n */\nconst convertRendererOverrides = (\n  overrides?: AgentWidgetMarkdownRendererOverrides\n): Partial<RendererObject> | undefined => {\n  if (!overrides) return undefined;\n  \n  // The token-based API in marked v12+ matches our type definitions\n  // We can pass through the overrides directly\n  return overrides as Partial<RendererObject>;\n};\n\n/**\n * Creates a configured markdown processor with custom options and renderers.\n * \n * @param options - Configuration options for the markdown processor\n * @returns A function that converts markdown text to HTML\n * \n * @example\n * ```typescript\n * // Basic usage with defaults\n * const processor = createMarkdownProcessor();\n * const html = processor(\"# Hello World\");\n * \n * // With custom options\n * const processor = createMarkdownProcessor({\n *   markedOptions: { gfm: true, breaks: true },\n *   renderer: {\n *     link(token) {\n *       return `<a href=\"${token.href}\" target=\"_blank\">${token.text}</a>`;\n *     }\n *   }\n * });\n * ```\n */\nexport const createMarkdownProcessor = (options?: MarkdownProcessorOptions) => {\n  let markedInstance: any = null;\n\n  return (text: string): string => {\n    const parsers = getMarkdownParsersSync();\n    if (!parsers) {\n      // If the markdown parser hasn't loaded yet, fall back to plain text with HTML escaped.\n      // The widget will re-render automatically once the parser finishes loading.\n      return escapeHtml(text);\n    }\n    \n    if (!markedInstance) {\n      const { Marked } = parsers;\n      const opts = options?.markedOptions;\n      markedInstance = new Marked({\n        gfm: opts?.gfm ?? true,\n        breaks: opts?.breaks ?? true,\n        pedantic: opts?.pedantic,\n        silent: opts?.silent,\n      });\n      \n      const rendererOverrides = convertRendererOverrides(options?.renderer);\n      if (rendererOverrides) {\n        markedInstance.use({ renderer: rendererOverrides });\n      }\n    }\n    \n    return markedInstance.parse(text) as string;\n  };\n};\n\n/**\n * Creates a markdown processor from AgentWidgetMarkdownConfig.\n * This is a convenience function that maps the widget config to processor options.\n * \n * @param config - The markdown configuration from widget config\n * @returns A function that converts markdown text to HTML\n */\nexport const createMarkdownProcessorFromConfig = (config?: AgentWidgetMarkdownConfig) => {\n  if (!config) {\n    return createMarkdownProcessor();\n  }\n  \n  return createMarkdownProcessor({\n    markedOptions: config.options,\n    renderer: config.renderer,\n  });\n};\n\n// Create default markdown processor instance\nconst defaultMarkdownProcessor = createMarkdownProcessor();\n\n/**\n * Basic markdown renderer using default settings.\n * Remember to sanitize the returned HTML if you render untrusted content in your host page.\n * \n * For custom configuration, use `createMarkdownProcessor()` or `createMarkdownProcessorFromConfig()`.\n */\nexport const markdownPostprocessor = (text: string): string => {\n  return defaultMarkdownProcessor(text);\n};\n\n/**\n * Escapes HTML entities. Used as the default safe renderer.\n */\nexport const escapeHtml = (text: string): string =>\n  text\n    .replace(/&/g, \"&amp;\")\n    .replace(/</g, \"&lt;\")\n    .replace(/>/g, \"&gt;\")\n    .replace(/\"/g, \"&quot;\")\n    .replace(/'/g, \"&#39;\");\n\nconst escapeAttribute = (value: string) =>\n  value.replace(/\"/g, \"&quot;\").replace(/</g, \"&lt;\").replace(/>/g, \"&gt;\");\n\nconst makeToken = (idx: number) => `%%FORM_PLACEHOLDER_${idx}%%`;\n\nconst directiveReplacer = (source: string, placeholders: Array<{ token: string; type: string }>) => {\n  let working = source;\n\n  // JSON directive pattern e.g. <Directive>{\"component\":\"form\",\"type\":\"init\"}</Directive>\n  working = working.replace(/<Directive>([\\s\\S]*?)<\\/Directive>/gi, (match, jsonText) => {\n    try {\n      const parsed = JSON.parse(jsonText.trim());\n      if (parsed && typeof parsed === \"object\" && parsed.component === \"form\" && parsed.type) {\n        const token = makeToken(placeholders.length);\n        placeholders.push({ token, type: String(parsed.type) });\n        return token;\n      }\n    } catch (error) {\n      return match;\n    }\n    return match;\n  });\n\n  // XML-style directive e.g. <Form type=\"init\" />\n  working = working.replace(/<Form\\s+type=\"([^\"]+)\"\\s*\\/>/gi, (_, type) => {\n    const token = makeToken(placeholders.length);\n    placeholders.push({ token, type });\n    return token;\n  });\n\n  return working;\n};\n\n/**\n * Creates a directive postprocessor with custom markdown configuration.\n * Converts special directives (either `<Form type=\"init\" />` or\n * `<Directive>{\"component\":\"form\",\"type\":\"init\"}</Directive>`) into placeholder\n * elements that the widget upgrades after render. Remaining text is rendered as\n * Markdown with the provided configuration.\n * \n * @param markdownConfig - Optional markdown configuration\n * @returns A function that processes text with directives and markdown\n */\nexport const createDirectivePostprocessor = (markdownConfig?: AgentWidgetMarkdownConfig) => {\n  const processor = createMarkdownProcessorFromConfig(markdownConfig);\n  \n  return (text: string): string => {\n    const placeholders: Array<{ token: string; type: string }> = [];\n    const withTokens = directiveReplacer(text, placeholders);\n    let html = processor(withTokens);\n\n    placeholders.forEach(({ token, type }) => {\n      const tokenRegex = new RegExp(token.replace(/[.*+?^${}()|[\\]\\\\]/g, \"\\\\$&\"), \"g\");\n      const safeType = escapeAttribute(type);\n      const replacement = `<div class=\"persona-form-directive\" data-tv-form=\"${safeType}\"></div>`;\n      html = html.replace(tokenRegex, replacement);\n    });\n\n    return html;\n  };\n};\n\n/**\n * Converts special directives (either `<Form type=\"init\" />` or\n * `<Directive>{\"component\":\"form\",\"type\":\"init\"}</Directive>`) into placeholder\n * elements that the widget upgrades after render. Remaining text is rendered as\n * Markdown using default settings.\n * \n * For custom markdown configuration, use `createDirectivePostprocessor()`.\n */\nexport const directivePostprocessor = (text: string): string => {\n  const placeholders: Array<{ token: string; type: string }> = [];\n  const withTokens = directiveReplacer(text, placeholders);\n  let html = markdownPostprocessor(withTokens);\n\n  placeholders.forEach(({ token, type }) => {\n    const tokenRegex = new RegExp(token.replace(/[.*+?^${}()|[\\]\\\\]/g, \"\\\\$&\"), \"g\");\n    const safeType = escapeAttribute(type);\n    const replacement = `<div class=\"persona-form-directive\" data-tv-form=\"${safeType}\"></div>`;\n    html = html.replace(tokenRegex, replacement);\n  });\n\n  return html;\n};\n","import type * as DOMPurifyType from \"dompurify\";\nimport { escapeHtml } from \"../postprocessors\";\nimport { getMarkdownParsersSync } from \"../markdown-parsers-loader\";\n\n/**\n * A function that sanitizes an HTML string, returning safe HTML.\n */\nexport type SanitizeFunction = (html: string) => string;\n\nconst DEFAULT_PURIFY_CONFIG: DOMPurifyType.Config = {\n  // Tags safe for markdown-rendered content\n  ALLOWED_TAGS: [\n    // Headings & structure\n    \"h1\", \"h2\", \"h3\", \"h4\", \"h5\", \"h6\", \"p\", \"br\", \"hr\", \"div\", \"span\",\n    // Lists\n    \"ul\", \"ol\", \"li\", \"dl\", \"dt\", \"dd\",\n    // Inline formatting\n    \"strong\", \"em\", \"b\", \"i\", \"u\", \"s\", \"del\", \"ins\", \"mark\", \"small\", \"sub\", \"sup\",\n    \"abbr\", \"kbd\", \"var\", \"samp\", \"code\",\n    // Links & media\n    \"a\", \"img\",\n    // Block elements\n    \"blockquote\", \"pre\", \"details\", \"summary\",\n    // Tables\n    \"table\", \"thead\", \"tbody\", \"tfoot\", \"tr\", \"th\", \"td\", \"caption\", \"colgroup\", \"col\",\n    // Forms (used by widget directive system)\n    \"input\", \"label\", \"select\", \"option\", \"textarea\", \"button\",\n  ],\n  ALLOWED_ATTR: [\n    // Link/media attributes\n    \"href\", \"src\", \"alt\", \"title\", \"target\", \"rel\", \"loading\", \"width\", \"height\",\n    // Table attributes\n    \"colspan\", \"rowspan\", \"scope\",\n    // Styling & identity\n    \"class\", \"id\",\n    // Form attributes\n    \"type\", \"name\", \"value\", \"placeholder\", \"disabled\", \"checked\", \"for\",\n    // Accessibility\n    \"aria-label\", \"aria-hidden\", \"aria-expanded\", \"role\", \"tabindex\",\n    // Widget-internal data attributes\n    \"data-tv-form\", \"data-message-id\", \"data-persona-component-directive\",\n    \"data-preserve-animation\", \"data-persona-instance\",\n  ],\n};\n\n/** Raster image data URI pattern: blocks SVG and other non-image types. */\nconst SAFE_DATA_URI = /^data:image\\/(?:png|jpe?g|gif|webp|bmp|x-icon|avif)/i;\n\n/**\n * Creates the default DOMPurify-based sanitizer.\n * Uses the global window when available (browser).\n */\nexport const createDefaultSanitizer = (): SanitizeFunction => {\n  let purifyInstance: ReturnType<typeof DOMPurifyType.default> | null = null;\n\n  return (html: string): string => {\n    const parsers = getMarkdownParsersSync();\n    if (!parsers) {\n      // If DOMPurify hasn't loaded yet, fall back to escaping HTML completely\n      // to remain safe until the module is available.\n      return escapeHtml(html);\n    }\n    \n    if (!purifyInstance) {\n      const { DOMPurify } = parsers;\n      // DOMPurify needs a DOM context. In the browser, pass `window`.\n      // The widget only runs in browsers, so `window` is always available at runtime.\n      purifyInstance = DOMPurify(typeof window !== \"undefined\" ? window : (undefined as never));\n\n      // Hook: strip data:image/svg+xml and other unsafe data: URIs from src/href\n      purifyInstance.addHook(\"uponSanitizeAttribute\", (_node, data) => {\n        if (data.attrName === \"src\" || data.attrName === \"href\") {\n          const val = data.attrValue;\n          if (val.toLowerCase().startsWith(\"data:\") && !SAFE_DATA_URI.test(val)) {\n            data.attrValue = \"\";\n            data.keepAttr = false;\n          }\n        }\n      });\n    }\n\n    return purifyInstance.sanitize(html, DEFAULT_PURIFY_CONFIG) as string;\n  };\n};\n\n/**\n * Resolves a `sanitize` config value into a concrete function or null.\n *\n * - `undefined` / `true` → built-in DOMPurify sanitizer\n * - `false` → `null` (no sanitization)\n * - custom function → returned as-is\n */\nexport const resolveSanitizer = (\n  option: boolean | SanitizeFunction | undefined,\n): SanitizeFunction | null => {\n  if (option === false) return null;\n  if (typeof option === \"function\") return option;\n  return createDefaultSanitizer();\n};\n","/**\n * Streaming markdown table stabilizer (Telegram-style space reservation).\n *\n * During SSE streaming the full accumulated markdown is re-parsed by `marked`\n * on every chunk. For GFM tables that produces two jarring jolts:\n *\n *  1. The paragraph→table flip. GFM only recognizes a table once the *delimiter*\n *     row (`| --- | --- |`) has arrived, so a freshly-streamed header line first\n *     renders as a plain paragraph and then snaps into a `<table>`.\n *  2. Partial-row flicker. The in-flight last row grows cell-by-cell.\n *\n * This module rewrites table-in-progress regions so a real `<table>` renders\n * from the first row onward with a stable column count: it completes the\n * delimiter row as soon as it starts streaming and pads the trailing partial\n * row to the header's column count. Combined with `table-layout: fixed` while\n * streaming (see `.persona-content-streaming table` in widget.css), columns lock\n * to even widths so rows append vertically without horizontal reflow.\n *\n * It runs ONLY while a message is streaming; the final render uses the real,\n * untouched `marked` output, so correctness is never affected.\n */\n\n/**\n * A GFM delimiter row, full or still streaming in: only delimiter characters\n * (`-`, `:`, `|`, whitespace) and at least one dash. Matches `|`-led partials\n * like `| -`, `|--`, `| :--`, and complete rows like `| --- | :--: |`.\n */\nconst DELIMITER_RE = /^\\s*\\|?[\\s:|-]*-[\\s:|-]*$/;\n\n/** A candidate table row contains at least one pipe. */\nconst hasPipe = (line: string): boolean => line.includes(\"|\");\n\n/** Split a markdown table row into trimmed cell strings, ignoring outer pipes. */\nconst splitCells = (line: string): string[] => {\n  let s = line.trim();\n  if (s.startsWith(\"|\")) s = s.slice(1);\n  if (s.endsWith(\"|\")) s = s.slice(0, -1);\n  return s.split(\"|\").map((cell) => cell.trim());\n};\n\n/** Render cells back into a normalized, pipe-delimited row. */\nconst buildRow = (cells: string[]): string => `| ${cells.join(\" | \")} |`;\n\n/** Build a complete delimiter row with the given column count. */\nconst buildDelimiter = (cols: number): string =>\n  `| ${Array.from({ length: cols }, () => \"---\").join(\" | \")} |`;\n\n/** Pad (or trim) a row's cells to exactly `cols` columns. */\nconst fitCells = (cells: string[], cols: number): string[] => {\n  if (cells.length >= cols) return cells.slice(0, cols);\n  return cells.concat(Array.from({ length: cols - cells.length }, () => \"\"));\n};\n\n/**\n * Normalize any streaming-in-progress GFM tables in `markdown` so they render as\n * complete tables with a stable column count. Returns the input unchanged when\n * there is nothing to stabilize (cheap fast-path for the common no-table case).\n */\nexport const stabilizeStreamingTables = (markdown: string): string => {\n  if (!markdown || !markdown.includes(\"|\")) return markdown;\n\n  const lines = markdown.split(\"\\n\");\n  let changed = false;\n\n  for (let i = 0; i < lines.length - 1; i++) {\n    const header = lines[i];\n    const delimiter = lines[i + 1];\n\n    // A table starts at a header line (has a pipe, is not itself a delimiter)\n    // immediately followed by a delimiter row that is full or still streaming.\n    if (!hasPipe(header) || DELIMITER_RE.test(header)) continue;\n    if (!DELIMITER_RE.test(delimiter)) continue;\n\n    const cols = splitCells(header).length;\n    if (cols < 1) continue;\n\n    // Complete the delimiter to match the header's column count so `marked`\n    // recognizes the table immediately instead of waiting for it to finish.\n    const fullDelimiter = buildDelimiter(cols);\n    if (lines[i + 1] !== fullDelimiter) {\n      lines[i + 1] = fullDelimiter;\n      changed = true;\n    }\n\n    // Normalize body rows (including a partial trailing one) to `cols` columns\n    // so each row occupies its slot instead of growing cell-by-cell. The region\n    // ends at the first blank or pipe-less line.\n    let j = i + 2;\n    for (; j < lines.length; j++) {\n      const row = lines[j];\n      if (row.trim() === \"\" || !hasPipe(row)) break;\n      const normalized = buildRow(fitCells(splitCells(row), cols));\n      if (lines[j] !== normalized) {\n        lines[j] = normalized;\n        changed = true;\n      }\n    }\n\n    i = j - 1; // resume scanning after this table region\n  }\n\n  return changed ? lines.join(\"\\n\") : markdown;\n};\n","/**\n * WebMCP consumption bridge.\n *\n * Owns the per-widget lifecycle of `@mcp-b/webmcp-polyfill`:\n *   - installs the polyfill (lazily, only when enabled) so `document.modelContext`\n *     is present;\n *   - snapshots the host page's tool registry per dispatch turn for\n *     `dispatch.clientTools[]`;\n *   - executes `webmcp:*` tool calls returned by the agent, mediating a single\n *     confirm-bubble gate before invoking the page's `execute()`.\n *\n * Spec reference: WebMCP (https://webmachinelearning.github.io/webmcp/).\n * Wire-level merging, namespace prefixing, and server-side allowlist\n * enforcement live on the Runtype API; this bridge mirrors those checks\n * client-side as a usability convenience, not a security boundary.\n *\n * About `@mcp-b/webmcp-polyfill`: it polyfills the *strict standard surface*\n * only (`registerTool` / `getTools` / `executeTool` on `document.modelContext`),\n * with no MCP-B-only extensions. The spec standardizes the *producer* side;\n * Persona is an in-page *consumer*, so it reads the registry via the\n * producer-facing preview API:\n *   - `getTools()`: async; returns `{ name, description, inputSchema }` where\n *     `inputSchema` is a JSON *string*. Annotations are not exposed here.\n *   - `executeTool(toolInfo, inputArgsJson, { signal })`: async; validates args\n *     against the tool's schema, runs `execute()`, and returns the raw result as\n *     a JSON *string* (or `null` for `undefined`). Honors `signal` for abort.\n *\n * The polyfill auto-installs `document.modelContext` at module-evaluation time,\n * so it is imported *dynamically* and only when `config.webmcp.enabled === true`\n *: a static import would install the global for every widget consumer,\n * including those that never opted into WebMCP.\n *\n * Confirm model: every `webmcp:*` call goes through one confirm gate before\n * `execute()` runs, regardless of `annotations.readOnlyHint`. (The polyfill owns\n * the spec's `client.requestUserInteraction` callback internally; Persona cannot\n * inject a nested confirm there, so the single outer gate is the whole story.)\n */\n\nimport type {\n  AgentWidgetWebMcpConfig,\n  ClientToolDefinition,\n  WebMcpConfirmHandler,\n  WebMcpConfirmInfo,\n  WebMcpToolResult,\n} from \"./types\";\n\n/**\n * Default per-call timeout for a WebMCP tool's `execute()`. Bounds how long\n * Persona waits before telling the agent the tool failed, keeping a misbehaving\n * tool from pinning the agent indefinitely. The timeout aborts the polyfill's\n * `executeTool` via an `AbortSignal`, so the page's work is asked to stop too\n * (cooperatively: a tool that ignores the signal may still complete).\n */\nconst DEFAULT_TOOL_TIMEOUT_MS = 30_000;\n\n/** Server-applied wire prefix; strip when looking up registry entries. */\nexport const WEBMCP_TOOL_PREFIX = \"webmcp:\";\n\n/**\n * Minimal structural view of the `@mcp-b/webmcp-polyfill` strict-core surface\n * that Persona consumes. We declare only what we use rather than depending on\n * `@mcp-b/webmcp-types` so the widget's type surface stays self-contained.\n */\ninterface ModelContextToolInfo {\n  name: string;\n  description: string;\n  /** JSON-encoded JSON Schema for the tool's input. */\n  inputSchema?: string;\n  /**\n   * Display title declared on the tool (`ToolDescriptor.title` in the WebMCP\n   * spec). The polyfill returns `\"\"` when the tool didn't declare one. Note:\n   * `annotations` (incl. the legacy `annotations.title`) are NOT exposed on\n   * this strict consumer surface: top-level `title` is the only display-name\n   * channel available to us.\n   */\n  title?: string;\n}\n\ninterface ModelContextCoreLike {\n  getTools(): Promise<ModelContextToolInfo[]>;\n  executeTool(\n    tool: ModelContextToolInfo,\n    inputArgsJson: string,\n    options?: { signal?: AbortSignal },\n  ): Promise<string | null>;\n}\n\n/**\n * Page-global map of bare tool name → declared display title\n * (`ToolDescriptor.title`). `document.modelContext` is page-global, so a\n * single map shared across widget/bridge instances is semantically correct.\n * Refreshed on every registry read (`snapshotForDispatch` / `executeToolCall`)\n * and consumed by the approval bubble's summary line via\n * `getWebMcpToolDisplayTitle`.\n */\nconst webMcpToolDisplayTitles = new Map<string, string>();\n\n/**\n * Record declared display titles from a fresh `getTools()` read. The map is\n * rebuilt from scratch, callers always pass the FULL registry snapshot, so\n * a tool that unregistered or dropped its title can't leave a stale label\n * behind. Exported for tests; production callers are the bridge's registry\n * reads.\n */\nexport const recordWebMcpToolDisplayTitles = (\n  infos: ModelContextToolInfo[],\n): void => {\n  webMcpToolDisplayTitles.clear();\n  for (const info of infos) {\n    const title = info.title?.trim();\n    if (title) webMcpToolDisplayTitles.set(info.name, title);\n  }\n};\n\n/**\n * Look up the display title a page tool declared via the WebMCP spec's\n * `ToolDescriptor.title`. Accepts wire (`webmcp:add_to_cart`) or bare\n * (`add_to_cart`) names. Returns `undefined` when the tool didn't declare\n * one (callers fall back to humanizing the tool name).\n */\nexport const getWebMcpToolDisplayTitle = (\n  toolName: string,\n): string | undefined => webMcpToolDisplayTitles.get(stripWebMcpPrefix(toolName));\n\nconst log = {\n  warn(message: string, ...rest: unknown[]): void {\n    if (typeof console !== \"undefined\" && typeof console.warn === \"function\") {\n      // eslint-disable-next-line no-console\n      console.warn(`[Persona/WebMCP] ${message}`, ...rest);\n    }\n  },\n};\n\n/** The slice of `@mcp-b/webmcp-polyfill` the bridge consumes on install. */\nexport type WebMcpPolyfillModule = {\n  initializeWebMCPPolyfill: () => void;\n};\n\n/**\n * Override how the polyfill module is obtained. By default the bridge does\n * `import(\"@mcp-b/webmcp-polyfill\")`, which bundlers resolve for npm\n * consumers. The IIFE/CDN build can't resolve a bare specifier at runtime, so\n * its entry (`index-global.ts`) registers a loader that imports the\n * self-contained `webmcp-polyfill.js` chunk from a URL derived from the\n * widget script's own `src`. Page-global, like `document.modelContext`\n * itself. Pass `null` to restore the default (used by tests).\n */\nlet polyfillLoader: (() => Promise<WebMcpPolyfillModule>) | null = null;\n\nexport const setWebMcpPolyfillLoader = (\n  loader: (() => Promise<WebMcpPolyfillModule>) | null,\n): void => {\n  polyfillLoader = loader;\n};\n\n/**\n * Compute a stable, order-independent fingerprint of a `ClientToolDefinition[]`\n * snapshot, for the diff-only / send-once dispatch path (client-token mode).\n *\n * The widget caches \"the fingerprint of the tool set last sent in full\" for the\n * current session; an unchanged set on a follow-up turn lets it ship only the\n * fingerprint instead of the whole array. Per-tool strings are sorted so tool\n * ordering does not affect the result. `pageOrigin` is deliberately excluded: * it is audit metadata, not part of the tool contract.\n *\n * This is a fast, non-cryptographic content key. The canonical per-tool content\n * is hashed down to a short, fixed-length digest so the result fits the server's\n * `clientToolsFingerprint` wire field (`z.string().max(128)`) regardless of how\n * many tools the page registers: sending the raw concatenated content would\n * overflow that bound and be rejected with a 400. The server stores and compares\n * the widget's fingerprint verbatim, so cross-implementation byte-equality is NOT\n * required: only self-consistency across this widget's turns.\n */\nexport function computeClientToolsFingerprint(\n  tools: ClientToolDefinition[],\n): string {\n  if (tools.length === 0) return \"0:empty\";\n  const parts = tools\n    .map((t) =>\n      [\n        t.name,\n        t.description ?? \"\",\n        t.parametersSchema ? JSON.stringify(t.parametersSchema) : \"\",\n        t.origin ?? \"\",\n        t.annotations ? JSON.stringify(t.annotations) : \"\",\n      ].join(\"\\x1f\"),\n    )\n    .sort();\n  return `${tools.length}:${hashFingerprintContent(parts.join(\"\\x1e\"))}`;\n}\n\n/**\n * cyrb53: a fast, well-distributed non-cryptographic string hash. Returns a\n * 53-bit value (safe-integer range). Two independent seeds are combined by the\n * caller for a ~106-bit digest, which makes accidental collisions across a\n * single conversation's handful of tool-set variants infeasible.\n */\nfunction cyrb53(str: string, seed: number): number {\n  let h1 = 0xdeadbeef ^ seed;\n  let h2 = 0x41c6ce57 ^ seed;\n  for (let i = 0; i < str.length; i++) {\n    const ch = str.charCodeAt(i);\n    h1 = Math.imul(h1 ^ ch, 2654435761);\n    h2 = Math.imul(h2 ^ ch, 1597334677);\n  }\n  h1 = Math.imul(h1 ^ (h1 >>> 16), 2246822507);\n  h1 ^= Math.imul(h2 ^ (h2 >>> 13), 3266489909);\n  h2 = Math.imul(h2 ^ (h2 >>> 16), 2246822507);\n  h2 ^= Math.imul(h1 ^ (h1 >>> 13), 3266489909);\n  return 4294967296 * (2097151 & h2) + (h1 >>> 0);\n}\n\n/**\n * Compress the canonical tool-set content string into a short, fixed-length\n * fingerprint (≤ ~24 chars) that fits the server's 128-char wire bound. Uses two\n * seeded cyrb53 passes, base-36 encoded.\n */\nfunction hashFingerprintContent(content: string): string {\n  const a = cyrb53(content, 0).toString(36);\n  const b = cyrb53(content, 0x9e3779b1).toString(36);\n  return `${a}.${b}`;\n}\n\nexport class WebMcpBridge {\n  private confirmHandler: WebMcpConfirmHandler | null;\n  private readonly timeoutMs: number;\n\n  /** `true` once the polyfill has been (idempotently) installed. */\n  private installed = false;\n  /** Memoizes the one-shot async install so concurrent callers share it. */\n  private readyPromise: Promise<void> | null = null;\n  /**\n   * Warn-once latch for a present-but-incompatible `document.modelContext`\n   * (some other / older WebMCP polyfill squatting the global). `getModelContext`\n   * is hit on every snapshot + execute, so we log the diagnostic only once.\n   */\n  private incompatibleContextWarned = false;\n\n  constructor(private readonly config: AgentWidgetWebMcpConfig) {\n    this.confirmHandler = config.onConfirm ?? null;\n    this.timeoutMs = DEFAULT_TOOL_TIMEOUT_MS;\n  }\n\n  /**\n   * Override the confirm handler post-construction. Used by `ui.ts` to wire\n   * the in-panel approval bubble after the client has been built (the widget\n   * lifecycle constructs the client before the panel renders).\n   */\n  public setConfirmHandler(handler: WebMcpConfirmHandler | null): void {\n    this.confirmHandler = handler;\n  }\n\n  /**\n   * `true` when the bridge can both snapshot the registry AND execute returned\n   * tool calls: i.e. the polyfill is installed and `document.modelContext`\n   * exposes the consumer surface (`getTools` / `executeTool`). Native browsers\n   * that ship `document.modelContext` satisfy this too.\n   *\n   * Synchronous and best-effort: returns `false` until the lazy install has\n   * resolved (see `ensureReady`). The snapshot/execute paths await readiness\n   * themselves, so this is purely an advisory check for callers.\n   */\n  public isOperational(): boolean {\n    if (this.config.enabled !== true) return false;\n    if (!this.installed) return false;\n    return this.getModelContext() !== null;\n  }\n\n  /**\n   * Per-turn snapshot for `dispatch.clientTools[]`. Returns the JSON-only\n   * surface: `execute` stays client-side, reached later via `executeToolCall`.\n   *\n   * Async because the strict polyfill's `getTools()` is async. Both payload\n   * builders in `client.ts` already `await`, so this adds no new ceremony.\n   */\n  public async snapshotForDispatch(): Promise<ClientToolDefinition[]> {\n    await this.ensureReady();\n    if (this.config.enabled !== true) return [];\n\n    const mc = this.getModelContext();\n    if (!mc) return [];\n\n    let infos: ModelContextToolInfo[];\n    try {\n      infos = await mc.getTools();\n    } catch (err) {\n      log.warn(\"getTools() threw: shipping an empty WebMCP snapshot.\", err);\n      return [];\n    }\n    recordWebMcpToolDisplayTitles(infos);\n\n    const pageOrigin = typeof location !== \"undefined\" ? location.origin : \"\";\n\n    return infos\n      .filter((info) => this.passesClientAllowlist(info.name))\n      .map<ClientToolDefinition>((info) => {\n        const def: ClientToolDefinition = {\n          name: info.name,\n          description: info.description,\n          origin: \"webmcp\",\n          ...(pageOrigin ? { pageOrigin } : {}),\n        };\n        const schema = parseSchema(info.inputSchema);\n        if (schema) def.parametersSchema = schema;\n        return def;\n      });\n  }\n\n  /**\n   * Execute a `webmcp:<name>` tool call returned by the agent and return the\n   * normalized MCP-shaped result for `/resume`.\n   *\n   * Failure modes: all return `{ isError: true, content: [...] }` rather than\n   * throwing, so the dispatch can resume cleanly:\n   *   - bridge not operational\n   *   - tool not in registry (e.g. unmounted between snapshot and call)\n   *   - tool excluded by the client allowlist\n   *   - user declined the confirm gate\n   *   - `execute()` threw or failed schema validation\n   *   - `execute()` exceeded the 30s timeout\n   *   - `signal` fired (session-level `cancel()`)\n   *\n   * When `signal` is provided, abort is honored at three points: before the\n   * confirm bubble renders, after the user approves but before `execute()`\n   * runs, and (via a combined `AbortController`) during `execute()` itself.\n   * Honoring abort BEFORE the confirm prevents a late approval after `cancel()`\n   * from firing a host-page side effect with no matching `/resume`.\n   */\n  public async executeToolCall(\n    wireToolName: string,\n    args: unknown,\n    signal?: AbortSignal,\n  ): Promise<WebMcpToolResult> {\n    await this.ensureReady();\n    if (this.config.enabled !== true) {\n      return errorResult(\n        \"WebMCP is not enabled on this widget.\",\n      );\n    }\n\n    const mc = this.getModelContext();\n    if (!mc) {\n      // Distinguish \"no modelContext at all\" from \"present but incompatible\"\n      // (a foreign/older polyfill squatting document.modelContext) so the\n      // resumed error is actionable. getModelContext has already warned once\n      // for the incompatible case.\n      const present =\n        typeof document !== \"undefined\" &&\n        Boolean((document as Document & { modelContext?: unknown }).modelContext);\n      return errorResult(\n        present\n          ? \"WebMCP is not operational: document.modelContext is present but does not expose the strict getTools()/executeTool() surface (likely a different or older WebMCP polyfill).\"\n          : \"WebMCP bridge is not operational on this page (document.modelContext not available).\",\n      );\n    }\n\n    const bareName = stripWebMcpPrefix(wireToolName);\n\n    let infos: ModelContextToolInfo[];\n    try {\n      infos = await mc.getTools();\n    } catch (err) {\n      const message = err instanceof Error ? err.message : String(err);\n      return errorResult(`Failed to read WebMCP registry: ${message}`);\n    }\n    recordWebMcpToolDisplayTitles(infos);\n    const info = infos.find((candidate) => candidate.name === bareName);\n\n    if (!info) {\n      return errorResult(\n        `WebMCP tool not registered on this page: ${bareName}`,\n      );\n    }\n\n    // Re-apply the client-side allowlist at execute time. `snapshotForDispatch`\n    // already filters it for `clientTools[]`, but the agent could request a\n    // tool that the integrator excluded: e.g. a `webmcp:` call replayed from\n    // history, a server bug, or a page that re-registered a previously-hidden\n    // tool. The server is the trust boundary; this is a defense-in-depth\n    // convenience check to keep us symmetric with the snapshot.\n    if (!this.passesClientAllowlist(bareName)) {\n      return errorResult(\n        `WebMCP tool not allowed by client allowlist: ${bareName}`,\n      );\n    }\n\n    // Bail before the confirm renders: a late approval after cancel() would\n    // otherwise fire a host-page side effect with no matching /resume.\n    if (signal?.aborted) {\n      return errorResult(\"Aborted by cancel()\");\n    }\n\n    // Confirm-by-default gate. Every `webmcp:*` call routes through here,\n    // regardless of `annotations.readOnlyHint`.\n    const displayTitle = getWebMcpToolDisplayTitle(bareName);\n    const gateInfo: WebMcpConfirmInfo = {\n      toolName: bareName,\n      args,\n      description: info.description,\n      ...(displayTitle ? { title: displayTitle } : {}),\n      reason: \"gate\",\n    };\n    if (!(await this.requestConfirm(gateInfo))) {\n      return errorResult(\"User declined the tool call.\");\n    }\n\n    // The await above may have parked us long enough for cancel() to fire.\n    // Bail before invoking `execute()` so we don't fire a side effect that\n    // the server can no longer accept a `/resume` for.\n    if (signal?.aborted) {\n      return errorResult(\"Aborted by cancel()\");\n    }\n\n    // Drive both the 30s timeout and the caller's `signal` through a single\n    // AbortController passed to `executeTool`. The polyfill races the page's\n    // `execute()` against this signal, so abort is cooperative: a tool that\n    // ignores the signal may still complete on the page after the agent gets\n    // an `isError` result. Side-effectful tools should bound their own work.\n    const controller = new AbortController();\n    let timedOut = false;\n    const timer = setTimeout(() => {\n      timedOut = true;\n      controller.abort();\n    }, this.timeoutMs);\n    const onAbort = () => controller.abort();\n    if (signal) {\n      if (signal.aborted) controller.abort();\n      else signal.addEventListener(\"abort\", onAbort, { once: true });\n    }\n\n    try {\n      const raw = await mc.executeTool(info, safeStringifyArgs(args), {\n        signal: controller.signal,\n      });\n      return normalizeSerializedResult(raw);\n    } catch (err) {\n      if (timedOut) {\n        return errorResult(\n          `WebMCP tool '${bareName}' timed out after ${this.timeoutMs}ms`,\n        );\n      }\n      if (signal?.aborted) {\n        return errorResult(\"Aborted by cancel()\");\n      }\n      const message = err instanceof Error ? err.message : String(err);\n      return errorResult(message);\n    } finally {\n      clearTimeout(timer);\n      if (signal) signal.removeEventListener(\"abort\", onAbort);\n    }\n  }\n\n  /**\n   * Lazily install `@mcp-b/webmcp-polyfill` the first time the bridge needs the\n   * registry. Idempotent and memoized. Dynamic import keeps the polyfill out of\n   * the main bundle and prevents it from installing `document.modelContext` for\n   * widget consumers that never enable WebMCP.\n   *\n   * Producer pages should still install the polyfill themselves (or import it)\n   * before registering tools: Persona's install is a fallback, and a page that\n   * registers tools at load before Persona's first dispatch needs the global to\n   * already exist.\n   */\n  private ensureReady(): Promise<void> {\n    if (this.config.enabled !== true) return Promise.resolve();\n    if (!this.readyPromise) {\n      this.readyPromise = this.install();\n    }\n    return this.readyPromise;\n  }\n\n  private async install(): Promise<void> {\n    try {\n      // A compatible registry is already on the page (the host installed the\n      // polyfill, or a native impl): initialize would no-op against it, so\n      // skip loading the module entirely. Pages that register tools before\n      // Persona's first dispatch always land here, because registering\n      // requires `document.modelContext` to exist.\n      if (this.getModelContext()) {\n        this.installed = true;\n        return;\n      }\n      const mod = polyfillLoader\n        ? await polyfillLoader()\n        : await import(\"@mcp-b/webmcp-polyfill\");\n      // Idempotent: no-ops if `document.modelContext` already exists (native or\n      // a prior install by the host page).\n      mod.initializeWebMCPPolyfill();\n      this.installed = true;\n    } catch (err) {\n      log.warn(\n        \"Failed to load @mcp-b/webmcp-polyfill: WebMCP consumption disabled.\",\n        err,\n      );\n      this.installed = false;\n    }\n  }\n\n  /**\n   * Read the consumer surface off `document.modelContext`, returning `null`\n   * when it is absent or doesn't expose the producer-preview API we rely on.\n   */\n  private getModelContext(): ModelContextCoreLike | null {\n    if (typeof document === \"undefined\") return null;\n    const mc = (document as Document & { modelContext?: unknown }).modelContext;\n    if (!mc || typeof mc !== \"object\") {\n      // Absent (not yet installed, or no WebMCP on this page): not an error,\n      // and not worth warning about; the snapshot/execute paths fall back to a\n      // clean \"not operational\" result.\n      return null;\n    }\n    const core = mc as Partial<ModelContextCoreLike>;\n    if (\n      typeof core.getTools !== \"function\" ||\n      typeof core.executeTool !== \"function\"\n    ) {\n      // A `document.modelContext` IS present but doesn't expose the strict-core\n      // surface we consume (`getTools` / `executeTool`). This usually means a\n      // different or older WebMCP polyfill (or a native impl on a divergent\n      // draft) installed the global first: which `@mcp-b/webmcp-polyfill`\n      // correctly declines to overwrite. Warn once so integrators understand\n      // why WebMCP is inert instead of seeing a silent no-op.\n      if (!this.incompatibleContextWarned) {\n        this.incompatibleContextWarned = true;\n        log.warn(\n          \"document.modelContext is present but does not expose getTools()/executeTool(): \" +\n            \"WebMCP consumption is disabled. Another (incompatible or older) WebMCP polyfill \" +\n            \"likely installed document.modelContext before Persona. Remove it, or use a polyfill \" +\n            \"implementing the strict standard surface (e.g. @mcp-b/webmcp-polyfill).\",\n        );\n      }\n      return null;\n    }\n    return mc as ModelContextCoreLike;\n  }\n\n  private async requestConfirm(info: WebMcpConfirmInfo): Promise<boolean> {\n    const handler = this.confirmHandler ?? defaultBrowserConfirmHandler;\n    try {\n      return await handler(info);\n    } catch (err) {\n      log.warn(\n        `Confirm handler threw for WebMCP tool '${info.toolName}'; declining.`,\n        err,\n      );\n      return false;\n    }\n  }\n\n  private passesClientAllowlist(toolName: string): boolean {\n    const list = this.config.allowlist;\n    if (!list || list.length === 0) return true;\n    return list.some((pattern) => matchesGlob(toolName, pattern));\n  }\n}\n\n/**\n * Strip the server-applied `webmcp:` prefix from a wire-format tool name.\n * Exported for tests; widget code should always go through the bridge.\n */\nexport const stripWebMcpPrefix = (name: string): string =>\n  name.startsWith(WEBMCP_TOOL_PREFIX)\n    ? name.slice(WEBMCP_TOOL_PREFIX.length)\n    : name;\n\n/**\n * `true` when `wireToolName` carries the `webmcp:` prefix. Used by `client.ts`\n * to route `step_await` events.\n */\nexport const isWebMcpToolName = (name: string): boolean =>\n  name.startsWith(WEBMCP_TOOL_PREFIX);\n\n/**\n * Glob match with `*` as the only wildcard. Matches any sequence of any\n * characters. Sufficient for the spec's prefix-style allowlists like\n * `search_*` or `list_*`. Tool names themselves cannot contain `:`\n * (see polyfill validation), so we don't need to special-case it.\n */\nconst matchesGlob = (name: string, pattern: string): boolean => {\n  if (pattern === \"*\") return true;\n  // Escape regex metachars except `*`, then convert `*` to `.*`.\n  const escaped = pattern.replace(/[.+?^${}()|[\\]\\\\]/g, \"\\\\$&\");\n  const regex = new RegExp(\"^\" + escaped.replace(/\\*/g, \".*\") + \"$\");\n  return regex.test(name);\n};\n\n/**\n * Parse the JSON-string `inputSchema` from `getTools()` back into an object for\n * `parametersSchema`. Returns `undefined` for a missing or unparseable schema\n * (the server can still accept a tool with no declared parameters).\n */\nconst parseSchema = (raw: string | undefined): object | undefined => {\n  if (raw === undefined || raw === \"\") return undefined;\n  try {\n    const parsed = JSON.parse(raw);\n    return parsed !== null && typeof parsed === \"object\"\n      ? (parsed as object)\n      : undefined;\n  } catch {\n    return undefined;\n  }\n};\n\n/**\n * Normalize the JSON-string result from `executeTool` into MCP `CallToolResult`\n * shape. The polyfill returns `JSON.stringify(rawResult)` (the tool's raw\n * `execute()` return, NOT pre-normalized) or `null` for an `undefined` return.\n * Already-shaped returns (with `content: [...]`) pass through; everything else\n * becomes a single text block. Tools that intentionally return MCP errors\n * should set `isError: true` themselves.\n */\nconst normalizeSerializedResult = (raw: string | null): WebMcpToolResult => {\n  if (raw === null || raw === undefined) {\n    return { content: [{ type: \"text\", text: \"\" }] };\n  }\n\n  let parsed: unknown;\n  try {\n    parsed = JSON.parse(raw);\n  } catch {\n    // Not valid JSON (shouldn't happen, the polyfill stringifies), surface\n    // the raw string as text rather than dropping it.\n    return { content: [{ type: \"text\", text: raw }] };\n  }\n\n  if (\n    parsed !== null &&\n    typeof parsed === \"object\" &&\n    Array.isArray((parsed as { content?: unknown }).content)\n  ) {\n    return parsed as WebMcpToolResult;\n  }\n\n  const text = typeof parsed === \"string\" ? parsed : safeStringify(parsed);\n  return { content: [{ type: \"text\", text }] };\n};\n\nconst errorResult = (message: string): WebMcpToolResult => ({\n  isError: true,\n  content: [{ type: \"text\", text: message }],\n});\n\n/**\n * Fallback confirm UI: `window.confirm()`. Production deployments should wire\n * `config.webmcp.onConfirm` to a handler matched to their UX (e.g. an inline\n * approval bubble). Declines silently in non-browser environments (SSR, tests\n * without a DOM).\n */\nconst defaultBrowserConfirmHandler: WebMcpConfirmHandler = async (info) => {\n  if (typeof window === \"undefined\" || typeof window.confirm !== \"function\") {\n    return false;\n  }\n  const argsPreview = previewArgs(info.args);\n  const prompt =\n    `Allow the AI to call ${info.toolName}` +\n    (argsPreview ? `\\n\\nArguments:\\n${argsPreview}` : \"\") +\n    (info.description ? `\\n\\n${info.description}` : \"\");\n  return window.confirm(prompt);\n};\n\nconst previewArgs = (args: unknown): string => {\n  if (args === undefined || args === null) return \"\";\n  try {\n    const json = JSON.stringify(args, null, 2);\n    return json.length > 500 ? json.slice(0, 500) + \"…\" : json;\n  } catch {\n    return String(args);\n  }\n};\n\n/**\n * Stringify tool args for `executeTool(toolInfo, inputArgsJson)`. Falls back to\n * `{}` for `undefined`/non-serializable args so the polyfill always receives a\n * valid JSON object string to validate against the tool schema.\n */\nconst safeStringifyArgs = (args: unknown): string => {\n  if (args === undefined) return \"{}\";\n  try {\n    const json = JSON.stringify(args);\n    return json === undefined ? \"{}\" : json;\n  } catch {\n    return \"{}\";\n  }\n};\n\n/**\n * `JSON.stringify` that tolerates circular references and non-serializable\n * values. A misbehaving tool result shouldn't break the resume path.\n */\nconst safeStringify = (value: unknown): string => {\n  if (value === undefined) return \"\";\n  try {\n    return JSON.stringify(value);\n  } catch {\n    return String(value);\n  }\n};\n","/**\n * Target resolution for the normalized, backend-neutral `target` field.\n *\n * `target` is a single string that selects which backend resource a widget\n * talks to, optimized for a browser widget (always serializable, no live\n * objects). Three shapes are supported:\n *\n *   - Runtype TypeID (no prefix): `\"agent_…\"` / `\"flow_…\"` route to the\n *     Runtype agent/flow paths. The TypeID prefix is self-describing, so no\n *     wrapper is needed for the common case.\n *   - Provider-prefixed: `\"<provider>:<id>\"` is handed to the matching\n *     `targetProviders[provider]` resolver, which returns the dispatch payload\n *     fragment for that backend (e.g. `eve`, `langgraph`). `\"runtype:…\"` is a\n *     built-in that re-detects a TypeID.\n *   - Bare name: `\"support\"` requires a `targetProviders.default` resolver,\n *     otherwise it throws (a bare name is ambiguous without one).\n *\n * Resolvers are registered, not passed as the value, which keeps `target`\n * itself a plain string that survives script-tag installs, `data-config`,\n * persisted state, and codegen.\n */\n\n/** Resolver for a provider-prefixed (or default) target id. */\nexport type TargetResolver = (id: string) => { payload: Record<string, unknown> };\n\n/** Normalized routing produced from a `target` string. */\nexport type ResolvedTarget =\n  | { kind: \"agentId\"; agentId: string }\n  | { kind: \"flowId\"; flowId: string }\n  | { kind: \"payload\"; payload: Record<string, unknown> };\n\nconst RUNTYPE_AGENT_PREFIX = \"agent_\";\nconst RUNTYPE_FLOW_PREFIX = \"flow_\";\n\nfunction detectRuntypeTypeId(id: string): ResolvedTarget | null {\n  if (id.startsWith(RUNTYPE_AGENT_PREFIX)) return { kind: \"agentId\", agentId: id };\n  if (id.startsWith(RUNTYPE_FLOW_PREFIX)) return { kind: \"flowId\", flowId: id };\n  return null;\n}\n\n/**\n * Resolve a `target` string into normalized routing. Pure and synchronous so\n * it can run on the dispatch hot path and be unit-tested in isolation.\n */\nexport function resolveTarget(\n  target: string,\n  targetProviders?: Record<string, TargetResolver>,\n): ResolvedTarget {\n  const trimmed = target.trim();\n  if (!trimmed) {\n    throw new Error(\"[Persona] `target` is empty.\");\n  }\n\n  const colon = trimmed.indexOf(\":\");\n  if (colon > 0) {\n    const prefix = trimmed.slice(0, colon);\n    const rest = trimmed.slice(colon + 1);\n\n    // Built-in: an explicit `runtype:` prefix wrapping a TypeID.\n    if (prefix === \"runtype\") {\n      const detected = detectRuntypeTypeId(rest);\n      if (detected) return detected;\n      throw new Error(\n        `[Persona] target \"runtype:${rest}\" is not a valid Runtype agent_/flow_ id.`,\n      );\n    }\n\n    const resolver = targetProviders?.[prefix];\n    if (!resolver) {\n      throw new Error(\n        `[Persona] No target provider registered for \"${prefix}\". ` +\n          `Add a \\`targetProviders.${prefix}\\` resolver, or use a Runtype agent_/flow_ id.`,\n      );\n    }\n    return { kind: \"payload\", payload: resolver(rest).payload };\n  }\n\n  // No prefix: a bare Runtype TypeID is self-describing.\n  const detected = detectRuntypeTypeId(trimmed);\n  if (detected) return detected;\n\n  // Bare, non-TypeID name: only resolvable via an explicit default resolver.\n  const fallback = targetProviders?.default;\n  if (fallback) return { kind: \"payload\", payload: fallback(trimmed).payload };\n\n  throw new Error(\n    `[Persona] target \"${trimmed}\" has no provider prefix and is not a Runtype agent_/flow_ id. ` +\n      `Use \"<provider>:${trimmed}\", a Runtype TypeID, or register a \\`targetProviders.default\\` resolver.`,\n  );\n}\n","import { parse as parsePartialJson, ARR, OBJ, STR } from \"partial-json\";\nimport { createElement } from \"../utils/dom\";\nimport {\n  AgentWidgetAskUserQuestionFeature,\n  AgentWidgetConfig,\n  AgentWidgetMessage,\n  AskUserQuestionOption,\n  AskUserQuestionPayload,\n  AskUserQuestionPrompt,\n} from \"../types\";\n\nexport const ASK_USER_QUESTION_TOOL_NAME = \"ask_user_question\";\nexport const ASK_USER_QUESTION_MAX = 8;\n\nconst SHEET_SENTINEL = \"data-persona-ask-sheet-for\";\nconst DEFAULT_FREE_TEXT_LABEL_ROWS = \"Other\";\nconst DEFAULT_FREE_TEXT_LABEL_PILLS = \"Other…\";\nconst DEFAULT_FREE_TEXT_PLACEHOLDER = \"Type your own answer here\";\nconst DEFAULT_SUBMIT_LABEL = \"Send\";\nconst DEFAULT_NEXT_LABEL = \"Next\";\nconst DEFAULT_BACK_LABEL = \"Back\";\nconst DEFAULT_SUBMIT_ALL_LABEL = \"Submit all\";\nconst DEFAULT_SKIP_LABEL = \"Skip\";\nconst DEFAULT_SKELETON_PILLS = 3;\n\nexport const ATTR_CURRENT_INDEX = \"data-ask-current-index\";\nexport const ATTR_QUESTION_COUNT = \"data-ask-question-count\";\nexport const ATTR_ANSWERS = \"data-ask-answers\";\nexport const ATTR_GROUPED = \"data-ask-grouped\";\nexport const ATTR_LAYOUT = \"data-ask-layout\";\n\nexport type AskUserQuestionLayout = \"rows\" | \"pills\";\n\nexport const resolveLayout = (\n  feature: AgentWidgetAskUserQuestionFeature\n): AskUserQuestionLayout => (feature.layout === \"pills\" ? \"pills\" : \"rows\");\n\nexport const getLayout = (sheet: HTMLElement): AskUserQuestionLayout =>\n  sheet.getAttribute(ATTR_LAYOUT) === \"pills\" ? \"pills\" : \"rows\";\n\nlet truncateWarned = false;\n\n/**\n * Escape a tool-call id for safe use inside a CSS attribute selector.\n * `CSS.escape` would work but isn't available in all test environments (jsdom).\n */\nconst escapeAttrValue = (value: string): string => value.replace(/[\"\\\\]/g, \"\\\\$&\");\n\nexport const isAskUserQuestionMessage = (message: AgentWidgetMessage): boolean => {\n  return (\n    message.variant === \"tool\" &&\n    !!message.toolCall &&\n    message.toolCall.name === ASK_USER_QUESTION_TOOL_NAME\n  );\n};\n\nconst resolveFeature = (config?: AgentWidgetConfig): AgentWidgetAskUserQuestionFeature => {\n  return config?.features?.askUserQuestion ?? {};\n};\n\n/**\n * Parse an `ask_user_question` tool-variant message into a partial payload.\n * Safe to call mid-stream: will walk the tool call's `chunks` via\n * `partial-json` and return `{ payload: null, complete: false }` when there\n * isn't enough data yet. `complete` flips to `true` once the tool call\n * reports status `\"complete\"`.\n *\n * Exported for plugin authors implementing `renderAskUserQuestion`.\n */\nexport const parseAskUserQuestionPayload = (\n  message: AgentWidgetMessage\n): { payload: Partial<AskUserQuestionPayload> | null; complete: boolean } => {\n  const toolCall = message.toolCall;\n  if (!toolCall) return { payload: null, complete: false };\n\n  const complete = toolCall.status === \"complete\";\n\n  if (toolCall.args && typeof toolCall.args === \"object\") {\n    return { payload: toolCall.args as Partial<AskUserQuestionPayload>, complete };\n  }\n\n  const chunks = toolCall.chunks;\n  if (!chunks || chunks.length === 0) return { payload: null, complete };\n\n  try {\n    const text = chunks.join(\"\");\n    const parsed = parsePartialJson(text, STR | OBJ | ARR);\n    if (parsed && typeof parsed === \"object\") {\n      return { payload: parsed as Partial<AskUserQuestionPayload>, complete };\n    }\n  } catch {\n    // malformed; fall through\n  }\n  return { payload: null, complete };\n};\n\n/**\n * Return the questions array (capped to {@link ASK_USER_QUESTION_MAX}). Logs a\n * single one-shot warning if a payload exceeds the cap.\n */\nexport const promptsFromPayload = (\n  payload: Partial<AskUserQuestionPayload> | null\n): Partial<AskUserQuestionPrompt>[] => {\n  const all = Array.isArray(payload?.questions) ? (payload!.questions as Partial<AskUserQuestionPrompt>[]) : [];\n  if (all.length > ASK_USER_QUESTION_MAX && !truncateWarned) {\n    truncateWarned = true;\n    if (typeof console !== \"undefined\") {\n      // eslint-disable-next-line no-console\n      console.warn(\n        `[AgentWidget] ask_user_question received ${all.length} questions; truncating to ${ASK_USER_QUESTION_MAX}.`\n      );\n    }\n  }\n  return all.slice(0, ASK_USER_QUESTION_MAX);\n};\n\n/**\n * Kept for plugin authors who only want to render the first question.\n * @deprecated Plugins should iterate `payload.questions` themselves; the\n * built-in renderer now paginates multi-question payloads.\n */\nconst firstPrompt = (\n  payload: Partial<AskUserQuestionPayload> | null\n): Partial<AskUserQuestionPrompt> | null => {\n  return promptsFromPayload(payload)[0] ?? null;\n};\n\nconst promptAt = (\n  payload: Partial<AskUserQuestionPayload> | null,\n  index: number\n): Partial<AskUserQuestionPrompt> | null => {\n  return promptsFromPayload(payload)[index] ?? null;\n};\n\nconst applyStyleVars = (\n  root: HTMLElement,\n  feature: AgentWidgetAskUserQuestionFeature\n): void => {\n  const s = feature.styles;\n  if (!s) return;\n  if (s.sheetBackground) root.style.setProperty(\"--persona-ask-sheet-bg\", s.sheetBackground);\n  if (s.sheetBorder) root.style.setProperty(\"--persona-ask-sheet-border\", s.sheetBorder);\n  if (s.sheetShadow) root.style.setProperty(\"--persona-ask-sheet-shadow\", s.sheetShadow);\n  if (s.pillBackground) root.style.setProperty(\"--persona-ask-pill-bg\", s.pillBackground);\n  if (s.pillBackgroundSelected)\n    root.style.setProperty(\"--persona-ask-pill-bg-selected\", s.pillBackgroundSelected);\n  if (s.pillTextColor) root.style.setProperty(\"--persona-ask-pill-fg\", s.pillTextColor);\n  if (s.pillTextColorSelected)\n    root.style.setProperty(\"--persona-ask-pill-fg-selected\", s.pillTextColorSelected);\n  if (s.pillBorderRadius) root.style.setProperty(\"--persona-ask-pill-radius\", s.pillBorderRadius);\n  if (s.customInputBackground)\n    root.style.setProperty(\"--persona-ask-input-bg\", s.customInputBackground);\n};\n\nconst buildAffordance = (\n  layout: AskUserQuestionLayout,\n  multiSelect: boolean,\n  index: number\n): HTMLElement | null => {\n  if (layout !== \"rows\") return null;\n  const wrap = createElement(\"span\", \"persona-ask-row-affordance\");\n  wrap.setAttribute(\"aria-hidden\", \"true\");\n  if (multiSelect) {\n    const check = createElement(\"span\", \"persona-ask-row-check\");\n    wrap.appendChild(check);\n  } else {\n    const badge = createElement(\"span\", \"persona-ask-row-badge\");\n    badge.textContent = String(index + 1);\n    wrap.appendChild(badge);\n  }\n  return wrap;\n};\n\nconst buildPill = (\n  option: AskUserQuestionOption,\n  index: number,\n  layout: AskUserQuestionLayout,\n  multiSelect: boolean\n): HTMLButtonElement => {\n  const cls =\n    layout === \"rows\"\n      ? \"persona-ask-pill persona-ask-row persona-pointer-events-auto\"\n      : \"persona-ask-pill persona-pointer-events-auto\";\n  const btn = createElement(\"button\", cls) as HTMLButtonElement;\n  btn.type = \"button\";\n  btn.setAttribute(\"role\", multiSelect ? \"checkbox\" : \"button\");\n  btn.setAttribute(\"aria-pressed\", \"false\");\n  btn.setAttribute(\"data-ask-user-action\", \"pick\");\n  btn.setAttribute(\"data-option-index\", String(index));\n  btn.setAttribute(\"data-option-label\", option.label);\n\n  if (layout === \"rows\") {\n    const content = createElement(\"span\", \"persona-ask-row-content\");\n    const label = createElement(\"span\", \"persona-ask-row-label\");\n    label.textContent = option.label;\n    content.appendChild(label);\n    if (option.description) {\n      const desc = createElement(\"span\", \"persona-ask-row-description\");\n      desc.textContent = option.description;\n      content.appendChild(desc);\n    }\n    btn.appendChild(content);\n    const aff = buildAffordance(layout, multiSelect, index);\n    if (aff) btn.appendChild(aff);\n  } else {\n    btn.textContent = option.label;\n    if (option.description) btn.title = option.description;\n  }\n  return btn;\n};\n\nconst buildSkeletonPill = (layout: AskUserQuestionLayout): HTMLElement => {\n  const cls =\n    layout === \"rows\"\n      ? \"persona-ask-pill persona-ask-row persona-ask-pill-skeleton persona-pointer-events-none\"\n      : \"persona-ask-pill persona-ask-pill-skeleton persona-pointer-events-none\";\n  const el = createElement(\"span\", cls);\n  el.setAttribute(\"aria-hidden\", \"true\");\n  return el;\n};\n\n/**\n * Build the interactive pill list + optional free-text pill for a given prompt.\n */\nconst buildPillList = (\n  prompt: Partial<AskUserQuestionPrompt> | null,\n  feature: AgentWidgetAskUserQuestionFeature,\n  complete: boolean,\n  layout: AskUserQuestionLayout\n): HTMLElement => {\n  const baseClass =\n    layout === \"rows\"\n      ? \"persona-ask-pills persona-ask-pills--rows persona-flex persona-flex-col persona-gap-2\"\n      : \"persona-ask-pills persona-flex persona-flex-wrap persona-gap-2\";\n  const list = createElement(\"div\", baseClass);\n  list.setAttribute(\"role\", \"group\");\n  list.setAttribute(\"data-ask-pill-list\", \"true\");\n\n  const multiSelect = !!prompt?.multiSelect;\n  const realOptions = Array.isArray(prompt?.options) ? (prompt!.options as AskUserQuestionOption[]) : [];\n  const cleanOptions = realOptions.filter((o) => o && typeof o.label === \"string\" && o.label.length > 0);\n\n  if (cleanOptions.length === 0 && !complete) {\n    for (let i = 0; i < DEFAULT_SKELETON_PILLS; i++) {\n      list.appendChild(buildSkeletonPill(layout));\n    }\n    return list;\n  }\n\n  cleanOptions.forEach((option, index) => {\n    list.appendChild(buildPill(option, index, layout, multiSelect));\n  });\n\n  // Free-text affordance:\n  //   - Rows layout: a composite row that visually matches the option rows\n  //     and HAS the input inside it (no separate row below). Number badge\n  //     `N+1` on the right; pressing it focuses the input via the\n  //     `focus-free-text` action.\n  //   - Pills layout (legacy): a dashed pill button that expands a separate\n  //     input row on click (handled by `buildFreeTextRow`).\n  const allowFreeText = prompt?.allowFreeText !== false;\n  if (allowFreeText) {\n    const defaultLabel =\n      layout === \"rows\" ? DEFAULT_FREE_TEXT_LABEL_ROWS : DEFAULT_FREE_TEXT_LABEL_PILLS;\n    if (layout === \"rows\") {\n      const otherRow = createElement(\n        \"div\",\n        \"persona-ask-pill persona-ask-row persona-ask-row--other persona-ask-pill-custom persona-pointer-events-auto\"\n      );\n      otherRow.setAttribute(\"data-ask-user-action\", \"focus-free-text\");\n      otherRow.setAttribute(\"data-option-index\", String(cleanOptions.length));\n      otherRow.setAttribute(\"data-ask-other-row\", \"true\");\n\n      const content = createElement(\"span\", \"persona-ask-row-content\");\n      const input = document.createElement(\"input\");\n      input.type = \"text\";\n      input.className = \"persona-ask-row-input persona-flex-1 persona-pointer-events-auto\";\n      input.placeholder = feature.freeTextPlaceholder ?? DEFAULT_FREE_TEXT_PLACEHOLDER;\n      input.setAttribute(\"data-ask-free-text-input\", \"true\");\n      input.setAttribute(\n        \"aria-label\",\n        feature.freeTextLabel ?? defaultLabel\n      );\n      content.appendChild(input);\n      otherRow.appendChild(content);\n\n      const aff = buildAffordance(layout, multiSelect, cleanOptions.length);\n      if (aff) otherRow.appendChild(aff);\n      list.appendChild(otherRow);\n    } else {\n      const freeBtn = createElement(\n        \"button\",\n        \"persona-ask-pill persona-ask-pill-custom persona-pointer-events-auto\"\n      ) as HTMLButtonElement;\n      freeBtn.type = \"button\";\n      freeBtn.setAttribute(\"data-ask-user-action\", \"open-free-text\");\n      freeBtn.textContent = feature.freeTextLabel ?? defaultLabel;\n      list.appendChild(freeBtn);\n    }\n  }\n\n  return list;\n};\n\nconst buildFreeTextRow = (\n  feature: AgentWidgetAskUserQuestionFeature,\n  layout: AskUserQuestionLayout\n): HTMLElement => {\n  const cls =\n    layout === \"rows\"\n      ? \"persona-ask-free-text persona-ask-free-text--rows persona-flex persona-gap-2 persona-mt-2\"\n      : \"persona-ask-free-text persona-hidden persona-flex persona-gap-2 persona-mt-2\";\n  const row = createElement(\"div\", cls);\n  row.setAttribute(\"data-ask-free-text-row\", \"true\");\n\n  const input = document.createElement(\"input\");\n  input.type = \"text\";\n  input.className =\n    \"persona-ask-free-text-input persona-flex-1 persona-pointer-events-auto\";\n  input.placeholder = feature.freeTextPlaceholder ?? DEFAULT_FREE_TEXT_PLACEHOLDER;\n  input.setAttribute(\"data-ask-free-text-input\", \"true\");\n\n  row.appendChild(input);\n\n  // Pills (legacy) layout keeps the explicit Send button so the expand-on-click\n  // affordance has a commit target. Rows layout drops it: the input commits via\n  // Enter, or via the grouped Next/Submit-all flush path.\n  if (layout !== \"rows\") {\n    const submit = createElement(\n      \"button\",\n      \"persona-ask-free-text-submit persona-pointer-events-auto\"\n    ) as HTMLButtonElement;\n    submit.type = \"button\";\n    submit.textContent = feature.submitLabel ?? DEFAULT_SUBMIT_LABEL;\n    submit.setAttribute(\"data-ask-user-action\", \"submit-free-text\");\n    row.appendChild(submit);\n  }\n\n  return row;\n};\n\nconst buildMultiSelectActions = (\n  feature: AgentWidgetAskUserQuestionFeature\n): HTMLElement => {\n  const row = createElement(\n    \"div\",\n    \"persona-ask-multi-actions persona-flex persona-justify-end persona-mt-2\"\n  );\n  row.setAttribute(\"data-ask-multi-actions\", \"true\");\n\n  const submit = createElement(\n    \"button\",\n    \"persona-ask-multi-submit persona-pointer-events-auto\"\n  ) as HTMLButtonElement;\n  submit.type = \"button\";\n  submit.textContent = feature.submitLabel ?? DEFAULT_SUBMIT_LABEL;\n  submit.setAttribute(\"data-ask-user-action\", \"submit-multi\");\n  submit.disabled = true;\n\n  row.appendChild(submit);\n  return row;\n};\n\nconst buildNavRow = (\n  index: number,\n  count: number,\n  feature: AgentWidgetAskUserQuestionFeature\n): HTMLElement => {\n  const row = createElement(\n    \"div\",\n    \"persona-ask-nav persona-flex persona-justify-between persona-items-center persona-gap-2 persona-mt-2\"\n  );\n  row.setAttribute(\"data-ask-nav-row\", \"true\");\n\n  const back = createElement(\n    \"button\",\n    \"persona-ask-nav-back persona-pointer-events-auto\"\n  ) as HTMLButtonElement;\n  back.type = \"button\";\n  back.textContent = feature.backLabel ?? DEFAULT_BACK_LABEL;\n  back.setAttribute(\"data-ask-user-action\", \"back\");\n  back.disabled = index === 0;\n  row.appendChild(back);\n\n  const rightGroup = createElement(\n    \"div\",\n    \"persona-ask-nav-right persona-flex persona-items-center persona-gap-2\"\n  );\n\n  const skip = createElement(\n    \"button\",\n    \"persona-ask-nav-skip persona-pointer-events-auto\"\n  ) as HTMLButtonElement;\n  skip.type = \"button\";\n  skip.textContent = feature.skipLabel ?? DEFAULT_SKIP_LABEL;\n  skip.setAttribute(\"data-ask-user-action\", \"skip\");\n  rightGroup.appendChild(skip);\n\n  const next = createElement(\n    \"button\",\n    \"persona-ask-nav-next persona-pointer-events-auto\"\n  ) as HTMLButtonElement;\n  next.type = \"button\";\n  const isFinal = index === count - 1;\n  next.textContent = isFinal\n    ? feature.submitAllLabel ?? DEFAULT_SUBMIT_ALL_LABEL\n    : feature.nextLabel ?? DEFAULT_NEXT_LABEL;\n  next.setAttribute(\"data-ask-user-action\", isFinal ? \"submit-all\" : \"next\");\n  next.disabled = true; // updated by syncNavState\n  rightGroup.appendChild(next);\n\n  row.appendChild(rightGroup);\n\n  return row;\n};\n\n/**\n * Read the answers map stored on the sheet element.\n */\nexport const readAnswersFromSheet = (\n  sheet: HTMLElement\n): Record<number, string | string[]> => {\n  const raw = sheet.getAttribute(ATTR_ANSWERS);\n  if (!raw) return {};\n  try {\n    const parsed = JSON.parse(raw);\n    return parsed && typeof parsed === \"object\" ? (parsed as Record<number, string | string[]>) : {};\n  } catch {\n    return {};\n  }\n};\n\n/**\n * Write the answers map back to the sheet element.\n */\nexport const writeAnswersToSheet = (\n  sheet: HTMLElement,\n  answers: Record<number, string | string[]>\n): void => {\n  sheet.setAttribute(ATTR_ANSWERS, JSON.stringify(answers));\n};\n\nexport const getCurrentIndex = (sheet: HTMLElement): number => {\n  const raw = Number(sheet.getAttribute(ATTR_CURRENT_INDEX) ?? \"0\");\n  return Number.isFinite(raw) ? Math.max(0, Math.floor(raw)) : 0;\n};\n\nexport const setCurrentIndex = (sheet: HTMLElement, index: number): void => {\n  sheet.setAttribute(ATTR_CURRENT_INDEX, String(Math.max(0, Math.floor(index))));\n};\n\nexport const getQuestionCount = (sheet: HTMLElement): number => {\n  const raw = Number(sheet.getAttribute(ATTR_QUESTION_COUNT) ?? \"1\");\n  return Number.isFinite(raw) ? Math.max(1, Math.floor(raw)) : 1;\n};\n\nexport const isGroupedSheet = (sheet: HTMLElement): boolean => {\n  return sheet.getAttribute(ATTR_GROUPED) === \"true\";\n};\n\nconst restoreAnswersFromMessage = (\n  message: AgentWidgetMessage,\n  prompts: Partial<AskUserQuestionPrompt>[]\n): Record<number, string | string[]> => {\n  const stored = message.agentMetadata?.askUserQuestionAnswers;\n  if (!stored || typeof stored !== \"object\") return {};\n  const result: Record<number, string | string[]> = {};\n  prompts.forEach((p, i) => {\n    const q = typeof p?.question === \"string\" ? p.question : \"\";\n    if (q && Object.prototype.hasOwnProperty.call(stored, q)) {\n      const v = stored[q];\n      if (typeof v === \"string\" || Array.isArray(v)) {\n        result[i] = v;\n      }\n    }\n  });\n  return result;\n};\n\nconst restoreIndexFromMessage = (\n  message: AgentWidgetMessage,\n  count: number\n): number => {\n  const stored = message.agentMetadata?.askUserQuestionIndex;\n  if (typeof stored !== \"number\" || !Number.isFinite(stored)) return 0;\n  return Math.max(0, Math.min(count - 1, Math.floor(stored)));\n};\n\n/**\n * Keyed-by-question-text view of the current answers on a sheet. Used both for\n * persistence to message metadata and for the final tool-result payload sent\n * back to the agent.\n */\nexport const buildStructuredAnswers = (\n  sheet: HTMLElement,\n  message: AgentWidgetMessage\n): Record<string, string | string[]> => {\n  const { payload } = parseAskUserQuestionPayload(message);\n  const prompts = promptsFromPayload(payload);\n  const indexed = readAnswersFromSheet(sheet);\n  const result: Record<string, string | string[]> = {};\n  const seen = new Set<string>();\n  prompts.forEach((p, i) => {\n    const q = typeof p?.question === \"string\" ? p.question : \"\";\n    if (!q) return;\n    if (seen.has(q) && typeof console !== \"undefined\") {\n      // eslint-disable-next-line no-console\n      console.warn(`[AgentWidget] ask_user_question has duplicate question text \"${q}\"; later answer wins.`);\n    }\n    seen.add(q);\n    if (Object.prototype.hasOwnProperty.call(indexed, i)) {\n      result[q] = indexed[i];\n    }\n  });\n  return result;\n};\n\n/**\n * Apply the selected/unselected visual state to pills on the current page,\n * based on the answer stored for `currentIndex`.\n */\nconst applySelectionState = (sheet: HTMLElement): void => {\n  const answers = readAnswersFromSheet(sheet);\n  const currentIndex = getCurrentIndex(sheet);\n  const stored = answers[currentIndex];\n  const selected = new Set<string>();\n  if (typeof stored === \"string\") selected.add(stored);\n  else if (Array.isArray(stored)) stored.forEach((s) => selected.add(s));\n\n  const pills = sheet.querySelectorAll<HTMLButtonElement>('[data-ask-user-action=\"pick\"][data-option-label]');\n  pills.forEach((pill) => {\n    const label = pill.getAttribute(\"data-option-label\") ?? \"\";\n    const on = selected.has(label);\n    pill.setAttribute(\"aria-pressed\", on ? \"true\" : \"false\");\n    pill.classList.toggle(\"persona-ask-pill-selected\", on);\n  });\n\n  // Also pre-fill the free-text input if the saved answer doesn't match any pill.\n  const realPillLabels = new Set(\n    Array.from(pills).map((p) => p.getAttribute(\"data-option-label\") ?? \"\")\n  );\n  // In rows mode the input lives inside the Other row of the pill list; in\n  // pills mode it lives in a separate (potentially hidden) free-text row.\n  // Querying the input directly covers both layouts.\n  const freeInput = sheet.querySelector<HTMLInputElement>('[data-ask-free-text-input=\"true\"]');\n  if (freeInput) {\n    if (typeof stored === \"string\" && stored.length > 0 && !realPillLabels.has(stored)) {\n      freeInput.value = stored;\n      const freeRow = freeInput.closest<HTMLElement>('[data-ask-free-text-row=\"true\"]');\n      freeRow?.classList.remove(\"persona-hidden\");\n    } else {\n      freeInput.value = \"\";\n    }\n  }\n};\n\n/**\n * Update the Next/Submit-all enabled state based on whether the current\n * question has a non-empty answer stored.\n */\nconst syncNavState = (sheet: HTMLElement): void => {\n  if (!isGroupedSheet(sheet)) return;\n  const answers = readAnswersFromSheet(sheet);\n  const currentIndex = getCurrentIndex(sheet);\n  const v = answers[currentIndex];\n  const hasAnswer =\n    (typeof v === \"string\" && v.length > 0) || (Array.isArray(v) && v.length > 0);\n  const next = sheet.querySelector<HTMLButtonElement>(\n    '[data-ask-user-action=\"next\"], [data-ask-user-action=\"submit-all\"]'\n  );\n  if (next) next.disabled = !hasAnswer;\n\n  // Multi-select submit (1-question mode): keep existing behavior.\n  const multi = sheet.querySelector<HTMLButtonElement>('[data-ask-user-action=\"submit-multi\"]');\n  if (multi) {\n    const labels = Array.from(\n      sheet.querySelectorAll<HTMLElement>('[aria-pressed=\"true\"][data-option-label]')\n    );\n    multi.disabled = labels.length === 0;\n  }\n};\n\n/**\n * Replace the page-scoped body of the sheet (question text, pills, free-text\n * row, multi-select actions) with content for `currentIndex`. Called both on\n * initial mount and after every Back/Next navigation. Preserves the stepper\n * row, the dismiss button, and the nav row at the bottom.\n */\nconst renderCurrentPage = (\n  sheet: HTMLElement,\n  message: AgentWidgetMessage,\n  config: AgentWidgetConfig | undefined\n): void => {\n  const feature = resolveFeature(config);\n  const layout = getLayout(sheet);\n  const { payload, complete } = parseAskUserQuestionPayload(message);\n  const grouped = isGroupedSheet(sheet);\n  const index = getCurrentIndex(sheet);\n  const count = getQuestionCount(sheet);\n  const prompt = grouped ? promptAt(payload, index) : firstPrompt(payload);\n  const multiSelect = !!prompt?.multiSelect;\n\n  // Inline stepper \"{index+1}/{count}\" lives in the header next to the\n  // question text. Empty in single-Q mode.\n  const stepInline = sheet.querySelector<HTMLElement>('[data-ask-step-inline=\"true\"]');\n  if (stepInline) {\n    stepInline.textContent = grouped ? `${index + 1}/${count}` : \"\";\n  }\n  // Sweep any legacy stepper row from earlier renders.\n  const oldStepper = sheet.querySelector<HTMLElement>('[data-ask-stepper=\"true\"]');\n  if (oldStepper) oldStepper.remove();\n\n  // Question text\n  const qText = sheet.querySelector<HTMLElement>('[data-ask-question=\"true\"]');\n  if (qText) {\n    const text = typeof prompt?.question === \"string\" ? prompt.question : \"\";\n    qText.textContent = text;\n    qText.classList.toggle(\"persona-ask-question-skeleton\", !text && !complete);\n  }\n\n  // Pills list\n  const pillList = sheet.querySelector<HTMLElement>('[data-ask-pill-list=\"true\"]');\n  if (pillList) {\n    const fresh = buildPillList(prompt, feature, complete, layout);\n    pillList.replaceWith(fresh);\n  }\n\n  // Free-text row: re-build to clear stale input value across pages.\n  // Only present in pills (legacy) mode; in rows mode the input lives inside\n  // the Other row of the pill list, which is rebuilt above.\n  if (layout !== \"rows\") {\n    const oldFree = sheet.querySelector<HTMLElement>('[data-ask-free-text-row=\"true\"]');\n    if (oldFree) oldFree.replaceWith(buildFreeTextRow(feature, layout));\n  }\n\n  // Multi-select action row: only relevant in 1-question mode.\n  const oldMulti = sheet.querySelector<HTMLElement>('[data-ask-multi-actions=\"true\"]');\n  if (!grouped && multiSelect && !oldMulti) {\n    sheet.appendChild(buildMultiSelectActions(feature));\n  } else if ((!multiSelect || grouped) && oldMulti) {\n    oldMulti.remove();\n  }\n  sheet.setAttribute(\"data-multi-select\", multiSelect ? \"true\" : \"false\");\n\n  // Nav row stays last; only present in grouped mode.\n  const oldNav = sheet.querySelector<HTMLElement>('[data-ask-nav-row=\"true\"]');\n  if (grouped) {\n    const fresh = buildNavRow(index, count, feature);\n    if (oldNav) oldNav.replaceWith(fresh);\n    else sheet.appendChild(fresh);\n  } else if (oldNav) {\n    oldNav.remove();\n  }\n\n  applySelectionState(sheet);\n  syncNavState(sheet);\n};\n\nconst buildSheet = (\n  message: AgentWidgetMessage,\n  config: AgentWidgetConfig | undefined,\n  payload: Partial<AskUserQuestionPayload> | null\n): HTMLElement => {\n  const feature = resolveFeature(config);\n  const layout = resolveLayout(feature);\n  const toolCallId = message.toolCall!.id;\n  const prompts = promptsFromPayload(payload);\n  const count = Math.max(1, prompts.length);\n  const grouped = count > 1;\n\n  const initialAnswers = restoreAnswersFromMessage(message, prompts);\n  const initialIndex = grouped ? restoreIndexFromMessage(message, count) : 0;\n\n  const sheet = createElement(\n    \"div\",\n    [\n      \"persona-ask-sheet\",\n      `persona-ask-sheet--${layout}`,\n      \"persona-pointer-events-auto\",\n      \"persona-ask-sheet-enter\",\n    ].join(\" \")\n  );\n  sheet.setAttribute(SHEET_SENTINEL, toolCallId);\n  sheet.setAttribute(\"data-tool-call-id\", toolCallId);\n  sheet.setAttribute(\"data-message-id\", message.id);\n  sheet.setAttribute(ATTR_QUESTION_COUNT, String(count));\n  sheet.setAttribute(ATTR_CURRENT_INDEX, String(initialIndex));\n  sheet.setAttribute(ATTR_GROUPED, grouped ? \"true\" : \"false\");\n  sheet.setAttribute(ATTR_LAYOUT, layout);\n  writeAnswersToSheet(sheet, initialAnswers);\n  sheet.setAttribute(\"role\", \"group\");\n  sheet.setAttribute(\"aria-label\", \"Suggested answers\");\n\n  if (feature.slideInMs !== undefined) {\n    sheet.style.setProperty(\"--persona-ask-sheet-duration\", `${feature.slideInMs}ms`);\n  }\n  applyStyleVars(sheet, feature);\n\n  // Header: question text (flex-1) + compact \"N/M\" stepper indicator on the\n  // right (grouped only). Skip in the nav row is the canonical escape hatch\n  //: plugins that want a different escape model render their own UX.\n  const header = createElement(\n    \"div\",\n    \"persona-ask-sheet-header persona-flex persona-items-center persona-gap-3\"\n  );\n\n  const qText = createElement(\"div\", \"persona-ask-sheet-question persona-flex-1\");\n  qText.setAttribute(\"data-ask-question\", \"true\");\n  qText.textContent = \"\";\n  header.appendChild(qText);\n\n  // Inline stepper indicator. Empty for single-Q; populated by\n  // renderCurrentPage to \"{index+1}/{count}\" in grouped mode.\n  const stepInline = createElement(\n    \"span\",\n    \"persona-ask-sheet-step-inline\"\n  );\n  stepInline.setAttribute(\"data-ask-step-inline\", \"true\");\n  stepInline.textContent = \"\";\n  header.appendChild(stepInline);\n\n  sheet.appendChild(header);\n\n  // Skeleton placeholders: these get replaced wholesale by renderCurrentPage.\n  const skeletonClass =\n    layout === \"rows\"\n      ? \"persona-ask-pills persona-ask-pills--rows persona-flex persona-flex-col persona-gap-2\"\n      : \"persona-ask-pills persona-flex persona-flex-wrap persona-gap-2\";\n  const list = createElement(\"div\", skeletonClass);\n  list.setAttribute(\"data-ask-pill-list\", \"true\");\n  list.setAttribute(\"role\", \"group\");\n  sheet.appendChild(list);\n\n  // Pills (legacy) layout uses a separate, hidden free-text row that expands\n  // on click. Rows layout embeds the input inside the Other row of the pill\n  // list, so the standalone row is unnecessary.\n  if (layout !== \"rows\") {\n    sheet.appendChild(buildFreeTextRow(feature, layout));\n  }\n\n  // Render the actual current page (stepper, pills, multi-actions, nav).\n  renderCurrentPage(sheet, message, config);\n\n  // Remove the enter class next frame so the slide-in transition runs.\n  requestAnimationFrame(() => {\n    requestAnimationFrame(() => sheet.classList.remove(\"persona-ask-sheet-enter\"));\n  });\n\n  return sheet;\n};\n\nconst syncSheetFromMessage = (\n  sheet: HTMLElement,\n  message: AgentWidgetMessage,\n  config: AgentWidgetConfig | undefined\n): void => {\n  // If the payload's question count grew (rare mid-stream), update the cached count.\n  const { payload } = parseAskUserQuestionPayload(message);\n  const newCount = Math.max(1, promptsFromPayload(payload).length);\n  if (newCount > getQuestionCount(sheet)) {\n    sheet.setAttribute(ATTR_QUESTION_COUNT, String(newCount));\n    if (newCount > 1 && !isGroupedSheet(sheet)) {\n      sheet.setAttribute(ATTR_GROUPED, \"true\");\n    }\n  }\n  renderCurrentPage(sheet, message, config);\n};\n\n/**\n * Create the small in-transcript stub for an `ask_user_question` tool call.\n * The stub is passive: the interactive sheet is mounted separately into\n * the composer overlay via `ensureAskUserQuestionSheet`.\n */\nexport const createAskUserQuestionBubble = (\n  message: AgentWidgetMessage,\n  config?: AgentWidgetConfig\n): HTMLElement => {\n  const bubble = createElement(\n    \"div\",\n    \"persona-ask-stub persona-inline-flex persona-items-center persona-gap-2\"\n  );\n  bubble.id = `bubble-${message.id}`;\n  bubble.setAttribute(\"data-message-id\", message.id);\n  bubble.setAttribute(\"data-bubble-type\", \"ask-user-question\");\n\n  const feature = resolveFeature(config);\n  applyStyleVars(bubble, feature);\n\n  const text = createElement(\"span\", \"persona-ask-stub-label\");\n  const { complete } = parseAskUserQuestionPayload(message);\n  text.textContent = complete ? \"Awaiting your response…\" : \"Preparing options…\";\n  bubble.appendChild(text);\n\n  return bubble;\n};\n\n/**\n * Mount or update the interactive answer-pill sheet for a given message.\n * Idempotent: if a sheet already exists for the tool-call id, it is hydrated\n * in-place instead of remounted, so streaming updates don't flicker.\n */\nexport const ensureAskUserQuestionSheet = (\n  message: AgentWidgetMessage,\n  config: AgentWidgetConfig | undefined,\n  overlay: HTMLElement | null | undefined\n): void => {\n  if (!overlay) return;\n  if (!isAskUserQuestionMessage(message)) return;\n\n  const feature = resolveFeature(config);\n  if (feature.enabled === false) return;\n\n  const toolCallId = message.toolCall!.id;\n\n  // Only keep the latest sheet in the overlay: clear any stale siblings.\n  const siblings = overlay.querySelectorAll<HTMLElement>(`[${SHEET_SENTINEL}]`);\n  siblings.forEach((el) => {\n    if (el.getAttribute(SHEET_SENTINEL) !== toolCallId) {\n      el.remove();\n    }\n  });\n\n  const existing = overlay.querySelector<HTMLElement>(\n    `[${SHEET_SENTINEL}=\"${escapeAttrValue(toolCallId)}\"]`\n  );\n  if (existing) {\n    syncSheetFromMessage(existing, message, config);\n    return;\n  }\n\n  const { payload } = parseAskUserQuestionPayload(message);\n  const sheet = buildSheet(message, config, payload);\n  overlay.appendChild(sheet);\n};\n\n/**\n * Remove the sheet for a specific tool-call id, or all sheets if omitted.\n * Runs a slide-out transition before removing.\n */\nexport const removeAskUserQuestionSheet = (\n  overlay: HTMLElement | null | undefined,\n  toolCallId?: string\n): void => {\n  if (!overlay) return;\n\n  const selector = toolCallId\n    ? `[${SHEET_SENTINEL}=\"${escapeAttrValue(toolCallId)}\"]`\n    : `[${SHEET_SENTINEL}]`;\n  const sheets = overlay.querySelectorAll<HTMLElement>(selector);\n\n  sheets.forEach((sheet) => {\n    sheet.classList.add(\"persona-ask-sheet-leave\");\n    const duration = Number.parseInt(\n      getComputedStyle(sheet).getPropertyValue(\"--persona-ask-sheet-duration\") || \"180\",\n      10\n    );\n    const remove = () => sheet.remove();\n    setTimeout(remove, Number.isFinite(duration) ? duration : 180);\n  });\n};\n\n/**\n * Read the currently-selected option labels from a multi-select sheet.\n */\nexport const getSelectedLabels = (sheet: HTMLElement): string[] => {\n  return Array.from(\n    sheet.querySelectorAll<HTMLElement>('[aria-pressed=\"true\"][data-option-label]')\n  )\n    .map((el) => el.getAttribute(\"data-option-label\"))\n    .filter((label): label is string => typeof label === \"string\" && label.length > 0);\n};\n\n/**\n * Update the answer for the current page and refresh visual state. Used by\n * the ui.ts event handlers in grouped mode.\n */\nexport const setCurrentAnswer = (\n  sheet: HTMLElement,\n  answer: string | string[]\n): void => {\n  const answers = readAnswersFromSheet(sheet);\n  const idx = getCurrentIndex(sheet);\n  if (typeof answer === \"string\" && answer.length === 0) {\n    delete answers[idx];\n  } else if (Array.isArray(answer) && answer.length === 0) {\n    delete answers[idx];\n  } else {\n    answers[idx] = answer;\n  }\n  writeAnswersToSheet(sheet, answers);\n  applySelectionState(sheet);\n  syncNavState(sheet);\n};\n\n/**\n * Navigate to a page by index and re-render the current page contents.\n */\nexport const navigateToPage = (\n  sheet: HTMLElement,\n  message: AgentWidgetMessage,\n  config: AgentWidgetConfig | undefined,\n  index: number\n): void => {\n  const count = getQuestionCount(sheet);\n  const clamped = Math.max(0, Math.min(count - 1, index));\n  setCurrentIndex(sheet, clamped);\n  renderCurrentPage(sheet, message, config);\n};\n\n/**\n * Re-export of the post-render nav-state sync, for ui.ts to call after pill\n * toggles in grouped multi-select mode.\n */\nexport const refreshNavState = (sheet: HTMLElement): void => {\n  syncNavState(sheet);\n};\n\n/**\n * Test seam: reset the one-shot truncation warning so each test can assert\n * the warn fires exactly once.\n */\nexport const __resetTruncateWarn = (): void => {\n  truncateWarned = false;\n};\n","/**\n * DOM utility functions\n */\nexport const createElement = <K extends keyof HTMLElementTagNameMap>(\n  tag: K,\n  className?: string\n): HTMLElementTagNameMap[K] => {\n  const element = document.createElement(tag);\n  if (className) {\n    element.className = className;\n  }\n  return element;\n};\n\nexport const createElementInDocument = <K extends keyof HTMLElementTagNameMap>(\n  documentRef: Document,\n  tag: K,\n  className?: string\n): HTMLElementTagNameMap[K] => {\n  const element = documentRef.createElement(tag);\n  if (className) {\n    element.className = className;\n  }\n  return element;\n};\n\nexport const createFragment = (): DocumentFragment => {\n  return document.createDocumentFragment();\n};\n\nexport interface CreateNodeOptions {\n  /** Sets `element.className`. */\n  className?: string;\n  /** Sets `element.textContent` before any `children` are appended. */\n  text?: string;\n  /** Attribute name → value pairs applied via `setAttribute`. */\n  attrs?: Record<string, string>;\n  /**\n   * Inline styles. Nullish (`undefined`/`null`) values are skipped so callers\n   * can inline conditionals (e.g. `borderColor: cfg.borderColor`) without an\n   * `if` per property. Note this only *sets* values, it never clears them, so\n   * prefer it for constructing fresh elements rather than re-styling live ones.\n   */\n  style?: Partial<CSSStyleDeclaration>;\n}\n\n/**\n * Ergonomic element factory that bundles the className + attribute + style +\n * children boilerplate the widget's DOM builders otherwise repeat by hand.\n *\n * `createElement` stays the right tool for the simple `(tag, className)` case;\n * reach for `createNode` when a node also needs attributes, inline styles, or\n * up-front children. Nullish `children` are skipped so callers can inline\n * conditionals (e.g. `maybeIcon && el`).\n */\nexport const createNode = <K extends keyof HTMLElementTagNameMap>(\n  tag: K,\n  options: CreateNodeOptions = {},\n  ...children: Array<Node | string | null | undefined>\n): HTMLElementTagNameMap[K] => {\n  const element = document.createElement(tag);\n\n  if (options.className) {\n    element.className = options.className;\n  }\n  if (options.text !== undefined) {\n    element.textContent = options.text;\n  }\n  if (options.attrs) {\n    for (const [name, value] of Object.entries(options.attrs)) {\n      element.setAttribute(name, value);\n    }\n  }\n  if (options.style) {\n    const style = element.style as unknown as Record<string, string>;\n    const source = options.style as Record<string, string | null | undefined>;\n    for (const property of Object.keys(source)) {\n      const value = source[property];\n      if (value != null) {\n        style[property] = value;\n      }\n    }\n  }\n\n  const appendable = children.filter(\n    (child): child is Node | string => child != null\n  );\n  if (appendable.length > 0) {\n    element.append(...appendable);\n  }\n\n  return element;\n};\n\n/**\n * Join truthy class-name fragments into a single space-separated string\n * (the clsx / classnames pattern). Falsy fragments\n * (`false` / `null` / `undefined` / `\"\"`) are dropped, so conditional classes\n * read inline as `cond && \"persona-foo\"` instead of imperative\n * `classList.add(...)` branches.\n */\nexport const cx = (\n  ...parts: Array<string | false | null | undefined>\n): string => parts.filter(Boolean).join(\" \");\n\n\n\n\n\n\n\n","/**\n * Built-in `suggest_replies` client tool.\n *\n * The widget can advertise this tool to the agent on every dispatch via\n * `clientTools[]` (set `features.suggestReplies.expose: true`): the same\n * wire surface as `ask_user_question` and WebMCP page tools. When the model\n * calls it, the execution pauses with a `step_await`\n * (`awaitReason: \"local_tool_required\"`); unlike `ask_user_question`, the\n * widget resolves it FIRE-AND-FORGET: it renders the suggestions as tappable\n * chips above the composer and immediately resumes the execution with a\n * canned \"shown\" result, so the agent's turn completes without waiting on the\n * user. Tapping a chip sends its text verbatim as the user's next message.\n *\n * Chip visibility is DERIVED state, not imperative show/hide: the chips of\n * the last `suggest_replies` tool message with no user message after it are\n * shown (see {@link latestAgentSuggestions}). That single rule covers\n * soft-dismiss on any user message (typed, voice, or chip click), restore on\n * reload/hydration, and latest-wins when a turn carries multiple calls.\n */\n\nimport type {\n  AgentWidgetConfig,\n  AgentWidgetMessage,\n  ClientToolDefinition,\n} from \"./types\";\n\nexport const SUGGEST_REPLIES_TOOL_NAME = \"suggest_replies\";\n\n/** Renderer cap: payloads beyond this are truncated with a console warning. */\nexport const SUGGEST_REPLIES_MAX = 4;\n\n/**\n * JSON Schema for the tool's parameters. Mirrors what\n * {@link parseSuggestRepliesPayload} hydrates the chips from, so the schema\n * the model is held to and the shape the renderer expects can never drift.\n */\nexport const SUGGEST_REPLIES_PARAMETERS_SCHEMA = {\n  type: \"object\",\n  properties: {\n    suggestions: {\n      type: \"array\",\n      minItems: 1,\n      maxItems: SUGGEST_REPLIES_MAX,\n      description:\n        \"1-4 short, distinct follow-up replies, phrased in the user's voice.\",\n      items: { type: \"string\", minLength: 1, maxLength: 60 },\n    },\n  },\n  required: [\"suggestions\"],\n  additionalProperties: false,\n} as const;\n\n/**\n * The `ClientToolDefinition` shipped on `dispatch.clientTools[]` when\n * `features.suggestReplies.expose` is on. Exported so integrators who prefer\n * declaring the tool server-side (a flow's `runtimeTools`) can reuse the same\n * description and schema instead of hand-writing them.\n */\nexport const SUGGEST_REPLIES_CLIENT_TOOL: ClientToolDefinition = {\n  name: SUGGEST_REPLIES_TOOL_NAME,\n  description:\n    \"Offer the user tappable quick-reply suggestions for their next message. \" +\n    \"Call at most once per turn, as the LAST action after your reply text is \" +\n    \"complete. Each suggestion is sent verbatim as the user's next message, \" +\n    'so phrase suggestions in the user\\'s voice (e.g. \"Tell me more about ' +\n    'pricing\"). Keep them short and distinct. The result only confirms the ' +\n    \"suggestions were shown: do not add further commentary after calling \" +\n    \"this tool; end your turn.\",\n  parametersSchema: SUGGEST_REPLIES_PARAMETERS_SCHEMA,\n  origin: \"sdk\",\n  annotations: { readOnlyHint: true },\n};\n\n/**\n * The canned tool output posted to `/resume` the moment the chips render.\n * MCP content shape, matching what the WebMCP resume path posts for page\n * tools. Built fresh per call so a caller can't mutate a shared object.\n */\nexport const suggestRepliesToolResult = (): {\n  content: { type: \"text\"; text: string }[];\n} => ({\n  content: [{ type: \"text\", text: \"Suggestions shown to the user.\" }],\n});\n\n/** A tool-variant message produced by a `suggest_replies` call. */\nexport const isSuggestRepliesMessage = (\n  message: AgentWidgetMessage,\n): boolean =>\n  message.variant === \"tool\" &&\n  message.toolCall?.name === SUGGEST_REPLIES_TOOL_NAME;\n\n/**\n * Tolerant parse of a `suggest_replies` tool call's args into chip labels:\n * accepts a JSON string or object, coerces items to trimmed strings, drops\n * empties, and truncates past the renderer cap with a console warning.\n */\nexport const parseSuggestRepliesPayload = (args: unknown): string[] => {\n  let parsed: unknown = args;\n  if (typeof parsed === \"string\") {\n    try {\n      parsed = JSON.parse(parsed);\n    } catch {\n      return [];\n    }\n  }\n  const raw = (parsed as { suggestions?: unknown } | null | undefined)\n    ?.suggestions;\n  if (!Array.isArray(raw)) return [];\n  const chips = raw\n    .filter((item): item is string => typeof item === \"string\")\n    .map((item) => item.trim())\n    .filter((item) => item.length > 0);\n  if (chips.length > SUGGEST_REPLIES_MAX) {\n    console.warn(\n      `[persona] suggest_replies: ${chips.length} suggestions exceeds the cap of ${SUGGEST_REPLIES_MAX}; extra suggestions dropped.`,\n    );\n    return chips.slice(0, SUGGEST_REPLIES_MAX);\n  }\n  return chips;\n};\n\n/**\n * The chips to show right now: those of the LAST `suggest_replies` tool\n * message with NO user message after it, or `null` when none apply. All\n * calls in a turn still get resumed (the server awaits each); only the\n * latest renders.\n */\nexport const latestAgentSuggestions = (\n  messages: AgentWidgetMessage[],\n): string[] | null => {\n  for (let i = messages.length - 1; i >= 0; i--) {\n    const message = messages[i];\n    if (message.role === \"user\") return null;\n    if (!isSuggestRepliesMessage(message)) continue;\n    const chips = parseSuggestRepliesPayload(message.toolCall?.args);\n    return chips.length > 0 ? chips : null;\n  }\n  return null;\n};\n\n/**\n * Gate for advertising the tool: `expose` opts it into the agent's catalog,\n * and `enabled !== false` guarantees the widget will actually auto-resolve\n * and render chips for it: exposing the tool with the feature disabled\n * would park the execution on a generic tool bubble with no resume coming.\n */\nexport const shouldExposeSuggestReplies = (\n  config: AgentWidgetConfig | undefined,\n): boolean => {\n  const feature = config?.features?.suggestReplies;\n  return feature?.expose === true && feature.enabled !== false;\n};\n","/**\n * Built-in `ask_user_question` client tool.\n *\n * The widget can advertise this tool to the agent on every dispatch via\n * `clientTools[]` (set `features.askUserQuestion.expose: true`): the same\n * wire surface WebMCP page tools ride. The server registers it as a LOCAL\n * tool under its bare name (`origin: 'sdk'` tools are not `webmcp:`-prefixed),\n * so when the model calls it the execution pauses with a `step_await`\n * (`awaitReason: \"local_tool_required\"`), the widget's answer-pill sheet\n * renders, and `session.resolveAskUserQuestion()` resumes the execution with\n * the structured answers.\n *\n * This replaces the previous integrator burden of hand-declaring the tool in\n * a flow's `runtimeTools` and keeping that schema in sync with the renderer.\n * Flows that already declare `ask_user_question` server-side should leave\n * `expose` off: the model would otherwise see the tool twice.\n */\n\nimport {\n  ASK_USER_QUESTION_MAX,\n  ASK_USER_QUESTION_TOOL_NAME,\n} from \"./components/ask-user-question-bubble\";\nimport {\n  SUGGEST_REPLIES_CLIENT_TOOL,\n  shouldExposeSuggestReplies,\n} from \"./suggest-replies-tool\";\nimport type { AgentWidgetConfig, ClientToolDefinition } from \"./types\";\n\n/**\n * JSON Schema for the tool's parameters. Mirrors {@link AskUserQuestionPayload}\n *, the shape `parseAskUserQuestionPayload` hydrates the answer sheet from,  * so the schema the model is held to and the schema the renderer expects can\n * never drift.\n */\nexport const ASK_USER_QUESTION_PARAMETERS_SCHEMA = {\n  type: \"object\",\n  properties: {\n    questions: {\n      type: \"array\",\n      minItems: 1,\n      maxItems: ASK_USER_QUESTION_MAX,\n      description: \"Questions to ask the user. Prefer a single question.\",\n      items: {\n        type: \"object\",\n        properties: {\n          question: {\n            type: \"string\",\n            description: \"The complete question, ending with a question mark.\",\n          },\n          header: {\n            type: \"string\",\n            maxLength: 12,\n            description: 'Short topic label, e.g. \"Auth method\".',\n          },\n          options: {\n            type: \"array\",\n            minItems: 2,\n            maxItems: 4,\n            description:\n              '2-4 distinct choices. Do NOT add an \"Other\" option: free text is automatic.',\n            items: {\n              type: \"object\",\n              properties: {\n                label: {\n                  type: \"string\",\n                  description: \"Concise choice text (1-5 words).\",\n                },\n                description: {\n                  type: \"string\",\n                  description: \"What the option means or implies.\",\n                },\n              },\n              required: [\"label\"],\n              additionalProperties: false,\n            },\n          },\n          multiSelect: {\n            type: \"boolean\",\n            description: \"Allow selecting multiple options. Default false.\",\n          },\n          allowFreeText: {\n            type: \"boolean\",\n            description: \"Show a free-text input. Default true.\",\n          },\n        },\n        required: [\"question\", \"options\"],\n        additionalProperties: false,\n      },\n    },\n  },\n  required: [\"questions\"],\n  additionalProperties: false,\n} as const;\n\n/**\n * The `ClientToolDefinition` shipped on `dispatch.clientTools[]` when\n * `features.askUserQuestion.expose` is on. Exported so integrators who prefer\n * declaring the tool server-side (a flow's `runtimeTools`) can reuse the same\n * description and schema instead of hand-writing them.\n */\nexport const ASK_USER_QUESTION_CLIENT_TOOL: ClientToolDefinition = {\n  name: ASK_USER_QUESTION_TOOL_NAME,\n  description:\n    \"Ask the user multiple-choice questions and wait for their answers. Use \" +\n    \"only when blocked on a decision that is the user's to make: a \" +\n    \"preference, a choice between valid approaches, or information you \" +\n    \"cannot infer. Each question offers 2-4 options plus an automatic \" +\n    \"free-text input. The result maps each question to its answer (an array \" +\n    \"when multiSelect); a question absent from the result was skipped.\",\n  parametersSchema: ASK_USER_QUESTION_PARAMETERS_SCHEMA,\n  origin: \"sdk\",\n  annotations: { readOnlyHint: true },\n};\n\n/**\n * Built-in client tools to append to a dispatch's `clientTools[]` for the\n * given widget config: `ask_user_question` and `suggest_replies`; future\n * built-in local tools join here.\n *\n * Each tool is gated on BOTH of its feature flags: `expose` opts the tool\n * into the agent's catalog, and `enabled !== false` guarantees the widget can\n * actually render UI (and, for fire-and-forget tools, auto-resume) for it: * exposing a tool with its feature disabled would park the execution on a\n * generic tool bubble with no way to advance.\n */\nexport const builtInClientToolsForDispatch = (\n  config: AgentWidgetConfig | undefined,\n): ClientToolDefinition[] => {\n  const tools: ClientToolDefinition[] = [];\n  const ask = config?.features?.askUserQuestion;\n  if (ask?.expose === true && ask.enabled !== false) {\n    tools.push(ASK_USER_QUESTION_CLIENT_TOOL);\n  }\n  if (shouldExposeSuggestReplies(config)) {\n    tools.push(SUGGEST_REPLIES_CLIENT_TOOL);\n  }\n  return tools;\n};\n","import { AgentWidgetReasoning, AgentWidgetToolCall, AgentWidgetStreamParser, AgentWidgetStreamParserResult } from \"../types\";\nimport { parse as parsePartialJson, STR, OBJ } from \"partial-json\";\n\n/**\n * Unescapes JSON string escape sequences that LLMs often double-escape.\n * Converts literal \\n, \\r, \\t sequences to actual control characters.\n */\nconst unescapeJsonString = (str: string): string => {\n  return str\n    .replace(/\\\\n/g, '\\n')\n    .replace(/\\\\r/g, '\\r')\n    .replace(/\\\\t/g, '\\t')\n    .replace(/\\\\\"/g, '\"')\n    .replace(/\\\\\\\\/g, '\\\\');\n};\n\nexport const formatUnknownValue = (value: unknown): string => {\n  if (value === null) return \"null\";\n  if (value === undefined) return \"\";\n  if (typeof value === \"string\") return value;\n  if (typeof value === \"number\" || typeof value === \"boolean\") {\n    return String(value);\n  }\n  try {\n    return JSON.stringify(value, null, 2);\n  } catch (error) {\n    return String(value);\n  }\n};\n\nexport const formatReasoningDuration = (reasoning: AgentWidgetReasoning) => {\n  const end = reasoning.completedAt ?? Date.now();\n  const start = reasoning.startedAt ?? end;\n  const durationMs =\n    reasoning.durationMs !== undefined\n      ? reasoning.durationMs\n      : Math.max(0, end - start);\n  const seconds = durationMs / 1000;\n  if (seconds < 0.1) {\n    return \"Thought for <0.1 seconds\";\n  }\n  const formatted =\n    seconds >= 10\n      ? Math.round(seconds).toString()\n      : seconds.toFixed(1).replace(/\\.0$/, \"\");\n  return `Thought for ${formatted} seconds`;\n};\n\nexport const describeReasonStatus = (reasoning: AgentWidgetReasoning) => {\n  if (reasoning.status === \"complete\") return formatReasoningDuration(reasoning);\n  if (reasoning.status === \"pending\") return \"Waiting\";\n  return \"\";\n};\n\nexport const formatToolDuration = (tool: AgentWidgetToolCall) => {\n  const durationMs =\n    typeof tool.duration === \"number\"\n      ? tool.duration\n      : typeof tool.durationMs === \"number\"\n        ? tool.durationMs\n        : Math.max(\n            0,\n            (tool.completedAt ?? Date.now()) -\n              (tool.startedAt ?? tool.completedAt ?? Date.now())\n          );\n  const seconds = durationMs / 1000;\n  if (seconds < 0.1) {\n    return \"Used tool for <0.1 seconds\";\n  }\n  const formatted =\n    seconds >= 10\n      ? Math.round(seconds).toString()\n      : seconds.toFixed(1).replace(/\\.0$/, \"\");\n  return `Used tool for ${formatted} seconds`;\n};\n\nexport const describeToolStatus = (status: AgentWidgetToolCall[\"status\"]) => {\n  if (status === \"complete\") return \"\";\n  if (status === \"pending\") return \"Starting\";\n  return \"Running\";\n};\n\nexport const describeToolTitle = (tool: AgentWidgetToolCall) => {\n  if (tool.status === \"complete\") {\n    return formatToolDuration(tool);\n  }\n  return \"Using tool...\";\n};\n\n/**\n * Formats a millisecond duration as a short human-readable string.\n * Returns \"2.3s\", \"15s\", or \"<0.1s\".\n */\nexport const formatElapsedMs = (ms: number): string => {\n  const seconds = ms / 1000;\n  if (seconds < 0.1) return \"<0.1s\";\n  if (seconds >= 10) return `${Math.round(seconds)}s`;\n  return `${seconds.toFixed(1).replace(/\\.0$/, \"\")}s`;\n};\n\n/**\n * Computes the current elapsed time string for a tool call.\n */\nexport const computeToolElapsed = (tool: AgentWidgetToolCall): string => {\n  const durationMs =\n    typeof tool.duration === \"number\"\n      ? tool.duration\n      : typeof tool.durationMs === \"number\"\n        ? tool.durationMs\n        : Math.max(\n            0,\n            (tool.completedAt ?? Date.now()) -\n              (tool.startedAt ?? tool.completedAt ?? Date.now())\n          );\n  return formatElapsedMs(durationMs);\n};\n\n/**\n * Computes the current elapsed time string for a reasoning block.\n */\nexport const computeReasoningElapsed = (reasoning: AgentWidgetReasoning): string => {\n  const durationMs =\n    reasoning.durationMs !== undefined\n      ? reasoning.durationMs\n      : Math.max(\n          0,\n          (reasoning.completedAt ?? Date.now()) -\n            (reasoning.startedAt ?? reasoning.completedAt ?? Date.now())\n        );\n  return formatElapsedMs(durationMs);\n};\n\n/**\n * Resolves a text template with tool call placeholders.\n * Supported placeholders: {toolName}, {duration}\n * Returns the fallback if template is undefined.\n */\nexport const resolveToolHeaderText = (\n  tool: AgentWidgetToolCall,\n  template: string | undefined,\n  fallback: string\n): string => {\n  if (!template) return fallback;\n\n  const toolName = tool.name?.trim() || \"tool\";\n  const duration = computeToolElapsed(tool);\n\n  return template\n    .replace(/\\{toolName\\}/g, toolName)\n    .replace(/\\{duration\\}/g, duration);\n};\n\n/**\n * A segment of parsed template text with optional inline formatting.\n */\nexport interface TemplateSegment {\n  /** The text content (or \"{duration}\" for duration placeholders) */\n  text: string;\n  /** CSS modifier names to apply: \"dim\", \"bold\", \"italic\" */\n  styles: string[];\n  /** True when this segment represents a {duration} placeholder */\n  isDuration?: boolean;\n}\n\n/**\n * Parses a template string with inline formatting markers into segments.\n *\n * Supported markers (Markdown-like):\n * - `**text**` → bold\n * - `*text*`  → italic\n * - `~text~`  → dim / muted\n *\n * Placeholders `{toolName}` are resolved; `{duration}` is preserved as a\n * typed segment so the caller can render it as a live-updating DOM node.\n *\n * @example\n * parseFormattedTemplate(\"Finished {toolName} ~{duration}~\", \"Get Weather\")\n * // → [\n * //   { text: \"Finished Get Weather \", styles: [] },\n * //   { text: \"{duration}\", styles: [\"dim\"], isDuration: true }\n * // ]\n */\nexport const parseFormattedTemplate = (\n  template: string,\n  toolName: string\n): TemplateSegment[] => {\n  const resolved = template.replace(/\\{toolName\\}/g, toolName);\n  const segments: TemplateSegment[] = [];\n  // Order matters: ** must match before *\n  const regex = /\\*\\*(.+?)\\*\\*|\\*(.+?)\\*|~(.+?)~/g;\n\n  let lastIndex = 0;\n  let match;\n\n  while ((match = regex.exec(resolved)) !== null) {\n    if (match.index > lastIndex) {\n      pushSegments(segments, resolved.slice(lastIndex, match.index), []);\n    }\n\n    if (match[1] !== undefined) {\n      pushSegments(segments, match[1], [\"bold\"]);\n    } else if (match[2] !== undefined) {\n      pushSegments(segments, match[2], [\"italic\"]);\n    } else if (match[3] !== undefined) {\n      pushSegments(segments, match[3], [\"dim\"]);\n    }\n\n    lastIndex = match.index + match[0].length;\n  }\n\n  if (lastIndex < resolved.length) {\n    pushSegments(segments, resolved.slice(lastIndex), []);\n  }\n\n  return segments;\n};\n\n/** Splits text on {duration} and pushes typed segments. */\nconst pushSegments = (\n  segments: TemplateSegment[],\n  text: string,\n  styles: string[]\n): void => {\n  const parts = text.split(\"{duration}\");\n  for (let i = 0; i < parts.length; i++) {\n    if (parts[i]) {\n      segments.push({ text: parts[i], styles });\n    }\n    if (i < parts.length - 1) {\n      segments.push({ text: \"{duration}\", styles, isDuration: true });\n    }\n  }\n};\n\n/**\n * Creates a regex-based parser for extracting text from JSON streams.\n * This is a simpler alternative to schema-stream that uses regex to extract\n * the 'text' field incrementally as JSON streams in.\n * \n * This can be used as an alternative parser option.\n */\nconst createRegexJsonParserInternal = (): {\n  processChunk(accumulatedContent: string): Promise<AgentWidgetStreamParserResult | string | null>;\n  getExtractedText(): string | null;\n  close?(): Promise<void>;\n} => {\n  let extractedText: string | null = null;\n  let processedLength = 0;\n  \n  // Regex-based extraction for incremental JSON parsing\n  const extractTextFromIncompleteJson = (jsonString: string): string | null => {\n    // Look for \"text\": \"value\" pattern, handling incomplete strings\n    // Match: \"text\": \" followed by any characters (including incomplete)\n    const textFieldRegex = /\"text\"\\s*:\\s*\"((?:[^\"\\\\]|\\\\.|\")*?)\"/;\n    const match = jsonString.match(textFieldRegex);\n    \n    if (match && match[1]) {\n      // Unescape the string value\n      try {\n        // Replace escaped characters\n        let unescaped = match[1]\n          .replace(/\\\\n/g, '\\n')\n          .replace(/\\\\r/g, '\\r')\n          .replace(/\\\\t/g, '\\t')\n          .replace(/\\\\\"/g, '\"')\n          .replace(/\\\\\\\\/g, '\\\\');\n        return unescaped;\n      } catch {\n        return match[1];\n      }\n    }\n    \n    // Also try to match incomplete text field (text field that hasn't closed yet)\n    // Look for \"text\": \" followed by content that may not be closed\n    const incompleteTextFieldRegex = /\"text\"\\s*:\\s*\"((?:[^\"\\\\]|\\\\.)*)/;\n    const incompleteMatch = jsonString.match(incompleteTextFieldRegex);\n    \n    if (incompleteMatch && incompleteMatch[1]) {\n      // Unescape the partial string value\n      try {\n        let unescaped = incompleteMatch[1]\n          .replace(/\\\\n/g, '\\n')\n          .replace(/\\\\r/g, '\\r')\n          .replace(/\\\\t/g, '\\t')\n          .replace(/\\\\\"/g, '\"')\n          .replace(/\\\\\\\\/g, '\\\\');\n        return unescaped;\n      } catch {\n        return incompleteMatch[1];\n      }\n    }\n    \n    return null;\n  };\n  \n  return {\n    getExtractedText: () => extractedText,\n    processChunk: async (accumulatedContent: string): Promise<AgentWidgetStreamParserResult | string | null> => {\n      // Skip if no new content\n      if (accumulatedContent.length <= processedLength) {\n        return extractedText !== null\n          ? { text: extractedText, raw: accumulatedContent }\n          : null;\n      }\n      \n      // Validate that the accumulated content looks like valid JSON\n      const trimmed = accumulatedContent.trim();\n      if (!trimmed.startsWith('{') && !trimmed.startsWith('[')) {\n        return null;\n      }\n      \n      // Try to extract text field using regex\n      const extracted = extractTextFromIncompleteJson(accumulatedContent);\n      if (extracted !== null) {\n        extractedText = extracted;\n      }\n      \n      // Update processed length\n      processedLength = accumulatedContent.length;\n      \n      // Return both the extracted text and raw JSON\n      if (extractedText !== null) {\n        return {\n          text: extractedText,\n          raw: accumulatedContent\n        };\n      }\n\n      return null;\n    },\n    close: async () => {\n      // No cleanup needed for regex-based parser\n    }\n  };\n};\n\n/**\n * Extracts the text field from JSON (works with partial JSON during streaming).\n * For complete JSON, uses fast path. For incomplete JSON, returns null (use stateful parser in client.ts).\n * \n * @param jsonString - The JSON string (can be partial/incomplete during streaming)\n * @returns The extracted text value, or null if not found or invalid\n */\nexport const extractTextFromJson = (jsonString: string): string | null => {\n  try {\n    // Try to parse complete JSON first (fast path)\n    const parsed = JSON.parse(jsonString);\n    if (parsed && typeof parsed === \"object\" && typeof parsed.text === \"string\") {\n      return parsed.text;\n    }\n  } catch {\n    // For incomplete JSON, return null - use stateful parser in client.ts\n    return null;\n  }\n  return null;\n};\n\n/**\n * Plain text parser - passes through text as-is without any parsing.\n * This is the default parser.\n */\nexport const createPlainTextParser = (): AgentWidgetStreamParser => {\n  const parser: AgentWidgetStreamParser = {\n    processChunk: (_accumulatedContent: string): string | null => {\n      // Always return null to indicate this isn't a structured format\n      // Content will be displayed as plain text\n      return null;\n    },\n    getExtractedText: (): string | null => {\n      return null;\n    }\n  };\n  // Mark this as a plain text parser\n  (parser as any).__isPlainTextParser = true;\n  return parser;\n};\n\n/**\n * JSON parser using regex-based extraction.\n * Extracts the 'text' field from JSON responses using regex patterns.\n * This is a simpler regex-based alternative to createJsonStreamParser.\n * Less robust for complex/malformed JSON but has no external dependencies.\n */\nexport const createRegexJsonParser = (): AgentWidgetStreamParser => {\n  const regexParser = createRegexJsonParserInternal();\n  \n  return {\n    processChunk: async (accumulatedContent: string): Promise<AgentWidgetStreamParserResult | string | null> => {\n      // Only process if it looks like JSON\n      const trimmed = accumulatedContent.trim();\n      if (!trimmed.startsWith('{') && !trimmed.startsWith('[')) {\n        return null;\n      }\n      return regexParser.processChunk(accumulatedContent);\n    },\n    getExtractedText: regexParser.getExtractedText.bind(regexParser),\n    close: regexParser.close?.bind(regexParser)\n  };\n};\n\n/**\n * JSON stream parser using partial-json library.\n * Extracts the 'text' field from JSON responses using the partial-json library,\n * which is specifically designed for parsing incomplete JSON from LLMs.\n * This is the recommended parser as it's more robust than regex.\n * \n * Library: https://github.com/promplate/partial-json-parser-js\n */\nexport const createJsonStreamParser = (): AgentWidgetStreamParser => {\n  let extractedText: string | null = null;\n  let processedLength = 0;\n  \n  return {\n    getExtractedText: () => extractedText,\n    processChunk: (accumulatedContent: string): AgentWidgetStreamParserResult | string | null => {\n      // Validate that the accumulated content looks like JSON\n      const trimmed = accumulatedContent.trim();\n      if (!trimmed.startsWith('{') && !trimmed.startsWith('[')) {\n        return null;\n      }\n      \n      // Skip if no new content\n      if (accumulatedContent.length <= processedLength) {\n        return extractedText !== null || extractedText === \"\"\n          ? { text: extractedText || \"\", raw: accumulatedContent }\n          : null;\n      }\n      \n      try {\n        // Parse partial JSON - allow partial strings and objects\n        // STR | OBJ allows incomplete strings and objects during streaming\n        const parsed = parsePartialJson(accumulatedContent, STR | OBJ);\n        \n        if (parsed && typeof parsed === \"object\") {\n          // Check for component directives - extract text if present for combined text+component\n          if (parsed.component && typeof parsed.component === \"string\") {\n            // For component directives, extract text if present, otherwise empty\n            extractedText = typeof parsed.text === \"string\" ? unescapeJsonString(parsed.text) : \"\";\n          }\n          // Check for form directives - these also don't have text fields\n          else if (parsed.type === \"init\" && parsed.form) {\n            // For form directives, return empty - they're handled by form postprocessor\n            extractedText = \"\";\n          }\n          // Extract text field if available\n          else if (typeof parsed.text === \"string\") {\n            extractedText = unescapeJsonString(parsed.text);\n          }\n        }\n      } catch (error) {\n        // If parsing fails completely, keep the last extracted text\n        // This can happen with very malformed JSON\n      }\n      \n      // Update processed length\n      processedLength = accumulatedContent.length;\n      \n      // Always return raw JSON for component/form directive detection\n      // Return empty string for text if it's a component/form directive\n      if (extractedText !== null) {\n        return {\n          text: extractedText,\n          raw: accumulatedContent\n        };\n      }\n\n      return null;\n    },\n    close: () => {\n      // No cleanup needed\n    }\n  };\n};\n\n/**\n * Flexible JSON stream parser that can extract text from various field names.\n * This parser looks for display text in multiple possible fields, making it\n * compatible with different JSON response formats.\n * \n * @param textExtractor Optional function to extract display text from parsed JSON.\n *                      If not provided, looks for common text fields.\n */\nexport const createFlexibleJsonStreamParser = (\n  textExtractor?: (parsed: any) => string | null\n): AgentWidgetStreamParser => {\n  let extractedText: string | null = null;\n  let processedLength = 0;\n  \n  // Default text extractor that handles common patterns\n  const defaultExtractor = (parsed: any): string | null => {\n    if (!parsed || typeof parsed !== \"object\") return null;\n\n    // Helper to safely extract and unescape text\n    const getText = (value: any): string | null => {\n      return typeof value === \"string\" ? unescapeJsonString(value) : null;\n    };\n\n    // Check for component directives - extract text if present for combined text+component\n    if (parsed.component && typeof parsed.component === \"string\") {\n      // For component directives, extract text if present, otherwise empty\n      return typeof parsed.text === \"string\" ? unescapeJsonString(parsed.text) : \"\";\n    }\n    \n    // Check for form directives - these also don't have text fields\n    if (parsed.type === \"init\" && parsed.form) {\n      // For form directives, return empty - they're handled by form postprocessor\n      return \"\";\n    }\n    \n    // Check for action-based text fields\n    if (parsed.action) {\n      switch (parsed.action) {\n        case 'nav_then_click':\n          return getText(parsed.on_load_text) || getText(parsed.text) || null;\n        case 'message':\n        case 'message_and_click':\n        case 'checkout':\n          return getText(parsed.text) || null;\n        default:\n          return getText(parsed.text) || getText(parsed.display_text) || getText(parsed.message) || null;\n      }\n    }\n    \n    // Fallback to common text field names\n    return getText(parsed.text) || getText(parsed.display_text) || getText(parsed.message) || getText(parsed.content) || null;\n  };\n  \n  const extractText = textExtractor || defaultExtractor;\n  \n  return {\n    getExtractedText: () => extractedText,\n    processChunk: (accumulatedContent: string): AgentWidgetStreamParserResult | string | null => {\n      // Validate that the accumulated content looks like JSON\n      const trimmed = accumulatedContent.trim();\n      if (!trimmed.startsWith('{') && !trimmed.startsWith('[')) {\n        return null;\n      }\n      \n      // Skip if no new content\n      if (accumulatedContent.length <= processedLength) {\n        return extractedText !== null\n          ? { text: extractedText, raw: accumulatedContent }\n          : null;\n      }\n      \n      try {\n        // Parse partial JSON - allow partial strings and objects\n        // STR | OBJ allows incomplete strings and objects during streaming\n        const parsed = parsePartialJson(accumulatedContent, STR | OBJ);\n        \n        // Extract text using the provided or default extractor\n        const newText = extractText(parsed);\n        if (newText !== null) {\n          extractedText = newText;\n        }\n      } catch (error) {\n        // If parsing fails completely, keep the last extracted text\n        // This can happen with very malformed JSON\n      }\n      \n      // Update processed length\n      processedLength = accumulatedContent.length;\n      \n      // Always return the raw JSON for action parsing and component detection\n      // Text may be null or empty for component/form directives, that's ok\n      return {\n        text: extractedText || \"\",\n        raw: accumulatedContent\n      };\n    },\n    close: () => {\n      // No cleanup needed\n    }\n  };\n};\n\n/**\n * XML stream parser.\n * Extracts text from <text>...</text> tags in XML responses.\n */\nexport const createXmlParser = (): AgentWidgetStreamParser => {\n  let extractedText: string | null = null;\n  \n  return {\n    processChunk: (accumulatedContent: string): AgentWidgetStreamParserResult | string | null => {\n      // Return null if not XML format\n      const trimmed = accumulatedContent.trim();\n      if (!trimmed.startsWith('<')) {\n        return null;\n      }\n      \n      // Extract text from <text>...</text> tags\n      // Handle both <text>content</text> and <text attr=\"value\">content</text>\n      const match = accumulatedContent.match(/<text[^>]*>([\\s\\S]*?)<\\/text>/);\n      if (match && match[1]) {\n        extractedText = match[1];\n        // For XML, we typically don't need the raw content for middleware\n        // but we can include it for consistency\n        return { text: extractedText, raw: accumulatedContent };\n      }\n      \n      return null;\n    },\n    getExtractedText: (): string | null => {\n      return extractedText;\n    }\n  };\n};\n\n\n\n\n\n\n\n","{\n  \"name\": \"@runtypelabs/persona\",\n  \"version\": \"4.5.0\",\n  \"description\": \"Themeable, pluggable streaming agent widget for websites, in plain JS with support for voice input and reasoning / tool output.\",\n  \"type\": \"module\",\n  \"main\": \"dist/index.cjs\",\n  \"module\": \"dist/index.js\",\n  \"types\": \"dist/index.d.ts\",\n  \"exports\": {\n    \".\": {\n      \"types\": \"./dist/index.d.ts\",\n      \"import\": \"./dist/index.js\",\n      \"require\": \"./dist/index.cjs\"\n    },\n    \"./theme-reference\": {\n      \"types\": \"./dist/theme-reference.d.ts\",\n      \"import\": \"./dist/theme-reference.js\",\n      \"require\": \"./dist/theme-reference.cjs\"\n    },\n    \"./codegen\": {\n      \"types\": \"./dist/codegen.d.ts\",\n      \"import\": \"./dist/codegen.js\",\n      \"require\": \"./dist/codegen.cjs\"\n    },\n    \"./theme-editor\": {\n      \"types\": \"./dist/theme-editor.d.ts\",\n      \"import\": \"./dist/theme-editor.js\",\n      \"require\": \"./dist/theme-editor.cjs\"\n    },\n    \"./theme-editor/preview\": {\n      \"types\": \"./dist/theme-editor-preview.d.ts\",\n      \"import\": \"./dist/theme-editor-preview.js\",\n      \"require\": \"./dist/theme-editor-preview.cjs\"\n    },\n    \"./testing\": {\n      \"types\": \"./dist/testing.d.ts\",\n      \"import\": \"./dist/testing.js\",\n      \"require\": \"./dist/testing.cjs\"\n    },\n    \"./smart-dom-reader\": {\n      \"types\": \"./dist/smart-dom-reader.d.ts\",\n      \"import\": \"./dist/smart-dom-reader.js\",\n      \"require\": \"./dist/smart-dom-reader.cjs\"\n    },\n    \"./voice-worklet-player\": {\n      \"types\": \"./dist/voice-worklet-player.d.ts\",\n      \"import\": \"./dist/voice-worklet-player.js\",\n      \"require\": \"./dist/voice-worklet-player.cjs\"\n    },\n    \"./plugin-kit\": {\n      \"types\": \"./dist/plugin-kit.d.ts\",\n      \"import\": \"./dist/plugin-kit.js\",\n      \"require\": \"./dist/plugin-kit.cjs\"\n    },\n    \"./animations/glyph-cycle\": {\n      \"types\": \"./dist/animations/glyph-cycle.d.ts\",\n      \"import\": \"./dist/animations/glyph-cycle.js\",\n      \"require\": \"./dist/animations/glyph-cycle.cjs\"\n    },\n    \"./animations/wipe\": {\n      \"types\": \"./dist/animations/wipe.d.ts\",\n      \"import\": \"./dist/animations/wipe.js\",\n      \"require\": \"./dist/animations/wipe.cjs\"\n    },\n    \"./widget.css\": \"./dist/widget.css\"\n  },\n  \"files\": [\n    \"dist\",\n    \"src\"\n  ],\n  \"scripts\": {\n    \"build\": \"rimraf dist && pnpm run build:styles && pnpm run build:markdown-parsers && pnpm run build:client && pnpm run build:installer && pnpm run build:launcher && pnpm run build:webmcp-polyfill && pnpm run build:runtype-tts && pnpm run build:theme-ref && pnpm run build:codegen && pnpm run build:theme-editor && pnpm run build:theme-editor-preview && pnpm run build:testing && pnpm run build:smart-dom-reader && pnpm run build:voice-worklet-player && pnpm run build:plugin-kit && pnpm run build:animations\",\n    \"build:markdown-parsers\": \"tsup --config tsup.markdown-parsers.config.ts\",\n    \"build:plugin-kit\": \"tsup src/plugin-kit.ts --format esm,cjs --minify --dts --out-dir dist --no-splitting\",\n    \"build:theme-editor\": \"tsup src/theme-editor.ts --format esm,cjs --minify --dts --out-dir dist --no-splitting\",\n    \"build:theme-editor-preview\": \"tsup src/theme-editor-preview.ts --format esm,cjs --minify --dts --out-dir dist --no-splitting\",\n    \"build:testing\": \"tsup src/testing.ts --format esm,cjs --minify --dts --out-dir dist --no-splitting\",\n    \"build:smart-dom-reader\": \"tsup src/smart-dom-reader.ts --format esm,cjs --minify --dts --out-dir dist --no-splitting\",\n    \"build:voice-worklet-player\": \"tsup src/voice-worklet-player.ts --format esm,cjs --minify --dts --out-dir dist --no-splitting\",\n    \"build:animations\": \"tsup src/animations/glyph-cycle.ts src/animations/wipe.ts --format esm,cjs --minify --dts --out-dir dist/animations --no-splitting\",\n    \"build:theme-ref\": \"tsup src/theme-reference.ts --format esm,cjs --minify --dts\",\n    \"build:codegen\": \"tsup src/codegen.ts --format esm,cjs --minify --dts\",\n    \"build:styles\": \"node scripts/build-styles.mjs\",\n    \"build:client\": \"tsup src/index.ts --format esm,cjs --minify --sourcemap --splitting false --dts --loader \\\".css=text\\\" && tsup --config tsup.global.config.ts && node -e \\\"const fs=require('fs');for(const ext of ['.global.js','.global.js.map']){const from='dist/index-global'+ext;if(fs.existsSync(from))fs.renameSync(from,'dist/index'+ext);}\\\"\",\n    \"build:webmcp-polyfill\": \"tsup --config tsup.webmcp-polyfill.config.ts\",\n    \"build:runtype-tts\": \"tsup --config tsup.runtype-tts.config.ts\",\n    \"build:installer\": \"tsup src/install.ts --format iife --global-name SiteAgentInstaller --out-dir dist --minify --sourcemap --no-splitting\",\n    \"build:launcher\": \"tsup src/launcher-global.ts --format iife --global-name AgentWidgetLauncher --minify --sourcemap --splitting false --out-dir dist && node -e \\\"const fs=require('fs');for(const ext of ['.global.js','.global.js.map']){const from='dist/launcher-global'+ext;if(fs.existsSync(from))fs.renameSync(from,'dist/launcher'+ext);}\\\"\",\n    \"lint\": \"eslint . --ext .ts\",\n    \"typecheck\": \"pnpm run check:runtype-types && tsc --noEmit\",\n    \"test\": \"vitest\",\n    \"test:ui\": \"vitest --ui\",\n    \"test:run\": \"vitest run\",\n    \"size\": \"size-limit\",\n    \"fetch:runtype-openapi\": \"node scripts/fetch-runtype-openapi.mjs\",\n    \"generate:runtype-types\": \"pnpm run fetch:runtype-openapi && node scripts/generate-runtype-openapi-types.mjs\",\n    \"check:runtype-types\": \"pnpm run fetch:runtype-openapi && node scripts/generate-runtype-openapi-types.mjs --check\"\n  },\n  \"dependencies\": {\n    \"@mcp-b/webmcp-polyfill\": \"^3.0.0\",\n    \"dompurify\": \"^3.4.10\",\n    \"idiomorph\": \"^0.7.4\",\n    \"lucide\": \"^1.18.0\",\n    \"marked\": \"^12.0.2\",\n    \"partial-json\": \"^0.1.7\",\n    \"zod\": \"^3.22.4\"\n  },\n  \"devDependencies\": {\n    \"@size-limit/file\": \"^12.1.0\",\n    \"@types/node\": \"^20.12.7\",\n    \"@typescript-eslint/eslint-plugin\": \"^7.0.0\",\n    \"@typescript-eslint/parser\": \"^7.0.0\",\n    \"@vitest/ui\": \"^4.0.9\",\n    \"esbuild\": \"^0.21.5\",\n    \"eslint\": \"^8.57.0\",\n    \"eslint-config-prettier\": \"^9.1.0\",\n    \"fake-indexeddb\": \"^6.2.5\",\n    \"rimraf\": \"^5.0.5\",\n    \"size-limit\": \"^12.1.0\",\n    \"tsup\": \"^8.0.1\",\n    \"typescript\": \"^5.4.5\",\n    \"vitest\": \"^4.0.9\"\n  },\n  \"engines\": {\n    \"node\": \">=20.0.0\"\n  },\n  \"author\": \"Runtype\",\n  \"license\": \"MIT\",\n  \"keywords\": [\n    \"ai\",\n    \"chat\",\n    \"widget\",\n    \"streaming\",\n    \"typescript\",\n    \"persona\",\n    \"agent\"\n  ],\n  \"repository\": {\n    \"type\": \"git\",\n    \"url\": \"git+https://github.com/runtypelabs/persona.git\",\n    \"directory\": \"packages/widget\"\n  },\n  \"bugs\": {\n    \"url\": \"https://github.com/runtypelabs/persona/issues\"\n  },\n  \"homepage\": \"https://github.com/runtypelabs/persona/tree/main/packages/widget#readme\",\n  \"publishConfig\": {\n    \"access\": \"public\"\n  }\n}\n","// Named import (not default) so esbuild tree-shakes the JSON module down to\n// the one field we read: a default import inlines the entire package.json\n// (~4.8 kB minified) into every bundle.\nimport { version } from \"../package.json\";\n\n/**\n * The current version of the @runtypelabs/persona package.\n * This is automatically derived from package.json.\n */\nexport const VERSION = version;\n","import {\n  AgentWidgetConfig,\n  AgentWidgetMessage,\n  AgentWidgetEvent,\n  AgentWidgetStreamParser,\n  AgentWidgetStreamParserResult,\n  AgentWidgetContextProvider,\n  AgentWidgetRequestMiddleware,\n  AgentWidgetRequestPayload,\n  AgentWidgetAgentRequestPayload,\n  AgentWidgetCustomFetch,\n  AgentWidgetSSEEventParser,\n  AgentWidgetHeadersFunction,\n  AgentWidgetSSEEventResult as _AgentWidgetSSEEventResult,\n  AgentExecutionState,\n  StopReasonKind,\n  ClientSession,\n  ClientInitResponse,\n  ClientChatRequest,\n  ClientFeedbackRequest,\n  ClientFeedbackType,\n  PersonaArtifactKind,\n  ContentPart,\n  WebMcpConfirmHandler\n} from \"./types\";\nimport { WebMcpBridge, computeClientToolsFingerprint, isWebMcpToolName } from \"./webmcp-bridge\";\nimport { resolveTarget } from \"./utils/target\";\nimport { builtInClientToolsForDispatch } from \"./ask-user-question-tool\";\nimport {\n  extractTextFromJson,\n  createPlainTextParser,\n  createJsonStreamParser,\n  createRegexJsonParser,\n  createXmlParser\n} from \"./utils/formatting\";\nimport { VERSION } from \"./version\";\n// artifactsSidebarEnabled is used in ui.ts to gate the sidebar pane rendering;\n// artifact events are always processed here regardless of config.\n\ntype DispatchOptions = {\n  messages: AgentWidgetMessage[];\n  signal?: AbortSignal;\n  /** Pre-generated ID for the expected assistant response (for feedback tracking) */\n  assistantMessageId?: string;\n};\n\ntype SSEHandler = (event: AgentWidgetEvent) => void;\n\nconst DEFAULT_ENDPOINT = \"https://api.runtype.com/v1/dispatch\";\nconst DEFAULT_CLIENT_API_BASE = \"https://api.runtype.com\";\n\n/**\n * Derive a download filename for `agent_media` parts that are delivered\n * without one. Maps a few well-known MIME types to friendly extensions and\n * falls back to `attachment.<subtype>` (or just `attachment` for opaque\n * types like `application/octet-stream`).\n */\nfunction filenameFromMediaType(mediaType: string): string {\n  // MIME types are case-insensitive (RFC 7231); compare against a lowercased\n  // copy so callers that pass mixed casing still hit the friendly extensions.\n  const lower = mediaType.toLowerCase();\n  const knownExtensions: Record<string, string> = {\n    \"application/pdf\": \"pdf\",\n    \"application/json\": \"json\",\n    \"application/zip\": \"zip\",\n    \"text/plain\": \"txt\",\n    \"text/csv\": \"csv\",\n    \"text/markdown\": \"md\"\n  };\n  const ext = knownExtensions[lower];\n  if (ext) return `attachment.${ext}`;\n  const slash = lower.indexOf(\"/\");\n  if (slash > 0) {\n    const subtype = lower.slice(slash + 1).split(\";\")[0]?.trim() ?? \"\";\n    if (subtype && subtype !== \"octet-stream\" && /^[a-z0-9.+-]+$/i.test(subtype)) {\n      return `attachment.${subtype}`;\n    }\n  }\n  return \"attachment\";\n}\n\n/**\n * Check if a message has valid (non-empty) content for sending to the API.\n * Filters out messages with empty content that would cause validation errors.\n *\n */\nconst hasValidContent = (message: AgentWidgetMessage): boolean => {\n  // Check contentParts (multi-modal content)\n  if (message.contentParts && message.contentParts.length > 0) {\n    return true;\n  }\n  // Check llmContent (explicit LLM content)\n  if (message.llmContent && message.llmContent.trim().length > 0) {\n    return true;\n  }\n  // Check rawContent (structured parser output)\n  if (message.rawContent && message.rawContent.trim().length > 0) {\n    return true;\n  }\n  // Check content (display content)\n  if (message.content && message.content.trim().length > 0) {\n    return true;\n  }\n  return false;\n};\n\n/**\n * Maps parserType string to the corresponding parser factory function\n */\nfunction getParserFromType(parserType?: \"plain\" | \"json\" | \"regex-json\" | \"xml\"): () => AgentWidgetStreamParser {\n  switch (parserType) {\n    case \"json\":\n      return createJsonStreamParser;\n    case \"regex-json\":\n      return createRegexJsonParser;\n    case \"xml\":\n      return createXmlParser;\n    case \"plain\":\n    default:\n      return createPlainTextParser;\n  }\n}\n\nexport type SSEEventCallback = (eventType: string, payload: unknown) => void;\n\nconst looksStructured = (value: string) =>\n  value.startsWith(\"{\") || value.startsWith(\"[\") || value.startsWith(\"<\");\n\n/**\n * Choose the best content source for sealed-segment reconciliation.\n * Prefers the final structured payload from step_complete when the raw\n * buffer is only a partial/unparseable prefix of the same structured format.\n */\nexport function preferFinalStructuredContent(\n  rawBuffer: string | undefined,\n  finalString: string\n): string {\n  if (!rawBuffer) return finalString;\n\n  const rawTrimmed = rawBuffer.trim();\n  const finalTrimmed = finalString.trim();\n  if (rawTrimmed.length === 0) return finalString;\n  if (finalTrimmed.length === 0) return rawBuffer;\n\n  const rawLooksStructured = looksStructured(rawTrimmed);\n  const finalLooksStructured = looksStructured(finalTrimmed);\n\n  if (!finalLooksStructured) return rawBuffer;\n  if (!rawLooksStructured) return finalString;\n  if (finalTrimmed === rawTrimmed) return finalString;\n  if (finalTrimmed.startsWith(rawTrimmed)) return finalString;\n\n  const rawJsonText = extractTextFromJson(rawBuffer);\n  const finalJsonText = extractTextFromJson(finalString);\n  if (finalJsonText !== null && rawJsonText === null) return finalString;\n\n  return rawBuffer;\n}\n\nexport class AgentWidgetClient {\n  private readonly apiUrl: string;\n  private readonly headers: Record<string, string>;\n  private readonly debug: boolean;\n  private readonly createStreamParser: () => AgentWidgetStreamParser;\n  private readonly contextProviders: AgentWidgetContextProvider[];\n  private readonly requestMiddleware?: AgentWidgetRequestMiddleware;\n  private readonly customFetch?: AgentWidgetCustomFetch;\n  private readonly parseSSEEvent?: AgentWidgetSSEEventParser;\n  private readonly getHeaders?: AgentWidgetHeadersFunction;\n  private onSSEEvent?: SSEEventCallback;\n  \n  // Client token mode properties\n  private clientSession: ClientSession | null = null;\n  private sessionInitPromise: Promise<ClientSession> | null = null;\n\n  // Diff-only / send-once WebMCP tool dispatch (client-token mode ONLY).\n  // Fingerprint of the clientTools[] last *sent in full* and confirmed by a\n  // successful stream start; null => the next client-token turn sends the full\n  // array. Paired with the sessionId it was sent under so a session change\n  // (silent re-init / expiry) forces a fresh full send.\n  private lastSentClientToolsFingerprint: string | null = null;\n  private clientToolsFingerprintSessionId: string | null = null;\n\n  // WebMCP: page-discovered tool consumption (see ./webmcp-bridge).\n  // Constructed lazily: null when `config.webmcp?.enabled !== true`.\n  private readonly webMcpBridge: WebMcpBridge | null;\n\n  constructor(private config: AgentWidgetConfig = {}) {\n    if (config.target && (config.agentId || config.flowId || config.agent)) {\n      throw new Error(\n        \"[Persona] `target` is mutually exclusive with `agentId`, `flowId`, and `agent`. Set only one routing field.\",\n      );\n    }\n    this.apiUrl = config.apiUrl ?? DEFAULT_ENDPOINT;\n    this.headers = {\n      \"Content-Type\": \"application/json\",\n      \"X-Persona-Version\": VERSION,\n      ...config.headers\n    };\n    this.debug = Boolean(config.debug);\n    // Use custom stream parser if provided, otherwise use parserType, or fall back to plain text parser\n    this.createStreamParser = config.streamParser ?? getParserFromType(config.parserType);\n    this.contextProviders = config.contextProviders ?? [];\n    this.requestMiddleware = config.requestMiddleware;\n    this.customFetch = config.customFetch;\n    this.parseSSEEvent = config.parseSSEEvent;\n    this.getHeaders = config.getHeaders;\n    this.webMcpBridge =\n      config.webmcp?.enabled === true ? new WebMcpBridge(config.webmcp) : null;\n  }\n\n  /**\n   * Refresh config in place WITHOUT tearing down the live connection or the\n   * WebMCP bridge. `AgentWidgetSession.updateConfig` calls this when only\n   * connection-irrelevant fields changed (theme, copy, layout, suggestions, …),\n   * so a UI update that lands mid-turn: e.g. a `webmcp:*` tool restyling the\n   * widget while the agent's turn is still streaming: doesn't abandon the\n   * in-flight stream/resume. Connection or request-shaping changes (apiUrl,\n   * clientToken, webmcp, headers, parser, …) take the full client rebuild path\n   * in the session instead, which is the only place the bridge is recreated.\n   *\n   * Only the live-read `config` is refreshed (e.g. `iterationDisplay`); the\n   * constructor-derived request-shaping fields (apiUrl, headers, parser,\n   * contextProviders, middleware, …) are left untouched because the session\n   * routes any change to those down the full-rebuild path instead, so they are\n   * guaranteed unchanged here. The `webMcpBridge` instance and its\n   * installed-polyfill memo are deliberately preserved, which keeps any\n   * in-flight resolve alive.\n   */\n  public updateConfig(next: AgentWidgetConfig): void {\n    this.config = next;\n  }\n\n  /**\n   * Set callback for capturing raw SSE events\n   */\n  public setSSEEventCallback(callback: SSEEventCallback): void {\n    this.onSSEEvent = callback;\n  }\n\n  /**\n   * WebMCP: wire (or replace) the confirm-bubble handler. Called from\n   * `ui.ts` once the widget panel is built and the approval-bubble\n   * chrome is ready to render.\n   */\n  public setWebMcpConfirmHandler(handler: WebMcpConfirmHandler | null): void {\n    this.webMcpBridge?.setConfirmHandler(handler);\n  }\n\n  /**\n   * WebMCP: `true` when the bridge installed the polyfill and can both\n   * snapshot the page registry and execute returned `webmcp:*` tool calls.\n   * `false` for any guard miss (no `document.modelContext`, polyfill not yet\n   * installed, or `config.webmcp.enabled` not set).\n   */\n  public isWebMcpOperational(): boolean {\n    return this.webMcpBridge?.isOperational() === true;\n  }\n\n  /**\n   * WebMCP: execute a returned `webmcp:<name>` tool call against the page's\n   * registry and return the normalized MCP-shaped result for `/resume`. The\n   * bridge handles confirm-bubble gating, the 30s timeout, error\n   * normalization, and `signal`-driven abort: callers never see throws.\n   *\n   * Returns `null` when WebMCP is not enabled on this client (signal to the\n   * session that it should fall back to the legacy local-tool resume path,\n   * if any).\n   */\n  public executeWebMcpToolCall(\n    wireToolName: string,\n    args: unknown,\n    signal?: AbortSignal,\n  ): Promise<import(\"./types\").WebMcpToolResult> | null {\n    if (!this.webMcpBridge) return null;\n    return this.webMcpBridge.executeToolCall(wireToolName, args, signal);\n  }\n\n  /**\n   * Get the current SSE event callback (used to preserve across client recreation)\n   */\n  public getSSEEventCallback(): SSEEventCallback | undefined {\n    return this.onSSEEvent;\n  }\n\n  /**\n   * Check if running in client token mode\n   */\n  public isClientTokenMode(): boolean {\n    return !!this.config.clientToken;\n  }\n\n  /**\n   * Resolve the effective backend routing for the current config. Combines the\n   * explicit `agentId`/`flowId` fields with the normalized `target` string\n   * (resolved via `resolveTarget`). Computed on demand so it stays correct\n   * across `update()`; the `target`/explicit-field conflict is rejected in the\n   * constructor, so at most one source is set here.\n   */\n  private routing(): {\n    agentId?: string;\n    flowId?: string;\n    targetPayload?: Record<string, unknown>;\n  } {\n    const { agentId, flowId, target, targetProviders } = this.config;\n    if (!target) {\n      return { agentId, flowId };\n    }\n    const resolved = resolveTarget(target, targetProviders);\n    if (resolved.kind === \"agentId\") return { agentId: resolved.agentId };\n    if (resolved.kind === \"flowId\") return { flowId: resolved.flowId };\n    return { targetPayload: resolved.payload };\n  }\n\n  /**\n   * Check if operating in agent execution mode\n   */\n  public isAgentMode(): boolean {\n    return !!(this.config.agent || this.routing().agentId);\n  }\n\n  /**\n   * Get the appropriate API URL based on mode\n   */\n  private getClientApiUrl(endpoint: 'init' | 'chat' | 'resume'): string {\n    const baseUrl = this.config.apiUrl?.replace(/\\/+$/, '').replace(/\\/v1\\/dispatch$/, '') || DEFAULT_CLIENT_API_BASE;\n    return `${baseUrl}/v1/client/${endpoint}`;\n  }\n\n  /**\n   * Get the current client session (if any)\n   */\n  public getClientSession(): ClientSession | null {\n    return this.clientSession;\n  }\n\n  /**\n   * Initialize session for client token mode.\n   * Called automatically on first message if not already initialized.\n   */\n  public async initSession(): Promise<ClientSession> {\n    if (!this.isClientTokenMode()) {\n      throw new Error('initSession() only available in client token mode');\n    }\n\n    // Return existing session if valid\n    if (this.clientSession && new Date() < this.clientSession.expiresAt) {\n      return this.clientSession;\n    }\n\n    // Deduplicate concurrent init calls\n    if (this.sessionInitPromise) {\n      return this.sessionInitPromise;\n    }\n\n    this.sessionInitPromise = this._doInitSession();\n    try {\n      const session = await this.sessionInitPromise;\n      this.clientSession = session;\n      // A freshly-minted session must resend the full WebMCP tool list on its\n      // next turn: drop any diff-only fingerprint cached under a prior session,\n      // so we never claim \"unchanged\" against a session the server didn't store\n      // the set under. (Belt-and-suspenders with the sessionId comparison in the\n      // send decision and the server's 409 resend signal.)\n      this.resetClientToolsFingerprint();\n      this.config.onSessionInit?.(session);\n      return session;\n    } finally {\n      this.sessionInitPromise = null;\n    }\n  }\n\n  private async _doInitSession(): Promise<ClientSession> {\n    // Get stored session_id if available (for session resumption)\n    const storedSessionId = this.config.getStoredSessionId?.() || null;\n    \n    const routed = this.routing();\n    const sessionTargetId = routed.agentId ?? routed.flowId;\n    const requestBody: Record<string, unknown> = {\n      token: this.config.clientToken,\n      ...(sessionTargetId && { flowId: sessionTargetId }),\n      ...(storedSessionId && { sessionId: storedSessionId }),\n    };\n\n    const response = await fetch(this.getClientApiUrl('init'), {\n      method: 'POST',\n      headers: {\n        'Content-Type': 'application/json',\n        'X-Persona-Version': VERSION,\n      },\n      body: JSON.stringify(requestBody),\n    });\n\n    if (!response.ok) {\n      const error = await response.json().catch(() => ({ error: 'Session initialization failed' }));\n      if (response.status === 401) {\n        throw new Error(`Invalid client token: ${error.hint || error.error}`);\n      }\n      if (response.status === 403) {\n        throw new Error(`Origin not allowed: ${error.hint || error.error}`);\n      }\n      throw new Error(error.error || 'Failed to initialize session');\n    }\n\n    const data: ClientInitResponse = await response.json();\n\n    // Store the new sessionId for future resumption\n    if (this.config.setStoredSessionId) {\n      this.config.setStoredSessionId(data.sessionId);\n    }\n\n    return {\n      sessionId: data.sessionId,\n      expiresAt: new Date(data.expiresAt),\n      flow: data.flow,\n      config: {\n        welcomeMessage: data.config.welcomeMessage,\n        placeholder: data.config.placeholder,\n        theme: data.config.theme,\n      },\n    };\n  }\n\n  /**\n   * Clear the current client session\n   */\n  public clearClientSession(): void {\n    this.clientSession = null;\n    this.sessionInitPromise = null;\n    this.resetClientToolsFingerprint();\n  }\n\n  /**\n   * Forget the diff-only WebMCP tool fingerprint so the next client-token turn\n   * resends the full `clientTools[]`. Called when the session is cleared and\n   * when the conversation is reset (`WidgetSession.clearMessages`).\n   */\n  public resetClientToolsFingerprint(): void {\n    this.lastSentClientToolsFingerprint = null;\n    this.clientToolsFingerprintSessionId = null;\n  }\n\n  /**\n   * Get the feedback API URL\n   */\n  private getFeedbackApiUrl(): string {\n    const baseUrl = this.config.apiUrl?.replace(/\\/+$/, '').replace(/\\/v1\\/dispatch$/, '') || DEFAULT_CLIENT_API_BASE;\n    return `${baseUrl}/v1/client/feedback`;\n  }\n\n  /**\n   * Send feedback for a message (client token mode only).\n   * Supports upvote, downvote, copy, csat, and nps feedback types.\n   * \n   * @param feedback - The feedback request payload\n   * @returns Promise that resolves when feedback is sent successfully\n   * @throws Error if not in client token mode or if session is invalid\n   * \n   * @example\n   * ```typescript\n   * // Message feedback (upvote/downvote/copy)\n   * await client.sendFeedback({\n   *   sessionId: sessionId,\n   *   messageId: messageId,\n   *   type: 'upvote'\n   * });\n   *\n   * // CSAT feedback (1-5 rating)\n   * await client.sendFeedback({\n   *   sessionId: sessionId,\n   *   type: 'csat',\n   *   rating: 5,\n   *   comment: 'Great experience!'\n   * });\n   *\n   * // NPS feedback (0-10 rating)\n   * await client.sendFeedback({\n   *   sessionId: sessionId,\n   *   type: 'nps',\n   *   rating: 9\n   * });\n   * ```\n   */\n  public async sendFeedback(feedback: ClientFeedbackRequest): Promise<void> {\n    if (!this.isClientTokenMode()) {\n      throw new Error('sendFeedback() only available in client token mode');\n    }\n\n    const session = this.getClientSession();\n    if (!session) {\n      throw new Error('No active session. Please initialize session first.');\n    }\n\n    // Validate messageId is provided for message-level feedback types\n    const messageFeedbackTypes: ClientFeedbackType[] = ['upvote', 'downvote', 'copy'];\n    if (messageFeedbackTypes.includes(feedback.type) && !feedback.messageId) {\n      throw new Error(`messageId is required for ${feedback.type} feedback type`);\n    }\n\n    // Validate rating is provided for csat/nps feedback types\n    if (feedback.type === 'csat') {\n      if (feedback.rating === undefined || feedback.rating < 1 || feedback.rating > 5) {\n        throw new Error('CSAT rating must be between 1 and 5');\n      }\n    }\n    if (feedback.type === 'nps') {\n      if (feedback.rating === undefined || feedback.rating < 0 || feedback.rating > 10) {\n        throw new Error('NPS rating must be between 0 and 10');\n      }\n    }\n\n    if (this.debug) {\n      // eslint-disable-next-line no-console\n      console.debug(\"[AgentWidgetClient] sending feedback\", feedback);\n    }\n\n    // Scope the feedback request to the caller's client token, sourced the same\n    // way as the chat/init requests. sendFeedback is client-token-mode only\n    // (guarded above), so clientToken is always present here and an API key can\n    // never leak into the body. Left undefined only when the embed has none.\n    const requestBody = {\n      ...feedback,\n      ...(this.config.clientToken && { token: this.config.clientToken }),\n    };\n\n    const response = await fetch(this.getFeedbackApiUrl(), {\n      method: 'POST',\n      headers: {\n        'Content-Type': 'application/json',\n        'X-Persona-Version': VERSION,\n      },\n      body: JSON.stringify(requestBody),\n    });\n\n    if (!response.ok) {\n      const errorData = await response.json().catch(() => ({ error: 'Feedback submission failed' }));\n      \n      if (response.status === 401) {\n        this.clientSession = null;\n        this.config.onSessionExpired?.();\n        throw new Error('Session expired. Please refresh to continue.');\n      }\n      \n      throw new Error(errorData.error || 'Failed to submit feedback');\n    }\n  }\n\n  /**\n   * Submit message feedback (upvote, downvote, or copy).\n   * Convenience method for sendFeedback with message-level feedback.\n   * \n   * @param messageId - The ID of the message to provide feedback for\n   * @param type - The feedback type: 'upvote', 'downvote', or 'copy'\n   */\n  public async submitMessageFeedback(\n    messageId: string, \n    type: 'upvote' | 'downvote' | 'copy'\n  ): Promise<void> {\n    const session = this.getClientSession();\n    if (!session) {\n      throw new Error('No active session. Please initialize session first.');\n    }\n\n    return this.sendFeedback({\n      sessionId: session.sessionId,\n      messageId: messageId,\n      type,\n    });\n  }\n\n  /**\n   * Submit CSAT (Customer Satisfaction) feedback.\n   * Convenience method for sendFeedback with CSAT feedback.\n   *\n   * @param rating - Rating from 1 to 5\n   * @param comment - Optional comment\n   */\n  public async submitCSATFeedback(rating: number, comment?: string): Promise<void> {\n    const session = this.getClientSession();\n    if (!session) {\n      throw new Error('No active session. Please initialize session first.');\n    }\n\n    return this.sendFeedback({\n      sessionId: session.sessionId,\n      type: 'csat',\n      rating,\n      comment,\n    });\n  }\n\n  /**\n   * Submit NPS (Net Promoter Score) feedback.\n   * Convenience method for sendFeedback with NPS feedback.\n   *\n   * @param rating - Rating from 0 to 10\n   * @param comment - Optional comment\n   */\n  public async submitNPSFeedback(rating: number, comment?: string): Promise<void> {\n    const session = this.getClientSession();\n    if (!session) {\n      throw new Error('No active session. Please initialize session first.');\n    }\n\n    return this.sendFeedback({\n      sessionId: session.sessionId,\n      type: 'nps',\n      rating,\n      comment,\n    });\n  }\n\n  /**\n   * Send a message - handles both proxy and client token modes\n   */\n  public async dispatch(options: DispatchOptions, onEvent: SSEHandler) {\n    if (this.isClientTokenMode()) {\n      return this.dispatchClientToken(options, onEvent);\n    }\n    if (this.isAgentMode()) {\n      return this.dispatchAgent(options, onEvent);\n    }\n    return this.dispatchProxy(options, onEvent);\n  }\n\n  /**\n   * Client token mode dispatch\n   */\n  private async dispatchClientToken(options: DispatchOptions, onEvent: SSEHandler) {\n    const controller = new AbortController();\n    if (options.signal) {\n      options.signal.addEventListener(\"abort\", () => controller.abort());\n    }\n\n    onEvent({ type: \"status\", status: \"connecting\" });\n\n    try {\n      // Ensure session is initialized\n      const session = await this.initSession();\n\n      // Check if session is about to expire (within 1 minute)\n      if (new Date() >= new Date(session.expiresAt.getTime() - 60000)) {\n        // Session expired or expiring soon\n        this.clearClientSession();\n        this.config.onSessionExpired?.();\n        const error = new Error('Session expired. Please refresh to continue.');\n        onEvent({ type: \"error\", error });\n        throw error;\n      }\n\n      // Build the standard payload to get context/metadata from middleware\n      const basePayload = await this.buildPayload(options.messages);\n\n      // Build the chat request payload with message IDs for feedback tracking\n      // Filter out sessionId from metadata if present (it's only for local storage)\n      const sanitizedMetadata = basePayload.metadata\n        ? Object.fromEntries(\n            Object.entries(basePayload.metadata).filter(([key]) => key !== 'sessionId' && key !== 'session_id')\n          )\n        : undefined;\n      \n      // Common (tools-independent) fields for the chat request.\n      const baseChatRequest: Omit<ClientChatRequest, 'clientTools' | 'clientToolsFingerprint'> = {\n        sessionId: session.sessionId,\n        // Filter out messages with empty content to prevent validation errors\n        messages: options.messages.filter(hasValidContent).map(m => ({\n          id: m.id, // Include message ID for tracking\n          role: m.role,\n          // Priority: contentParts (multi-modal) > llmContent (explicit LLM content) > rawContent (structured parsers) > content (display)\n          content: m.contentParts ?? m.llmContent ?? m.rawContent ?? m.content,\n        })),\n        // Include pre-generated assistant message ID if provided\n        ...(options.assistantMessageId && { assistantMessageId: options.assistantMessageId }),\n        // Include metadata/context from middleware if present (excluding sessionId)\n        ...(sanitizedMetadata && Object.keys(sanitizedMetadata).length > 0 && { metadata: sanitizedMetadata }),\n        ...(basePayload.inputs && Object.keys(basePayload.inputs).length > 0 && { inputs: basePayload.inputs }),\n        ...(basePayload.context && { context: basePayload.context }),\n      };\n\n      // Diff-only / send-once WebMCP tool dispatch. `buildPayload()` already\n      // snapshotted the full set; decide whether to ship it again or just its\n      // fingerprint. First turn of a session, or a changed set, sends the full\n      // array; an unchanged set sends only the fingerprint and the server\n      // reuses its stored copy. The cache is committed only after a successful\n      // stream start (below), so a 409/failure leaves it untouched.\n      const fullClientTools = basePayload.clientTools;\n      const hasClientTools = !!(fullClientTools && fullClientTools.length > 0);\n      const clientToolsFingerprint = hasClientTools\n        ? computeClientToolsFingerprint(fullClientTools!)\n        : undefined;\n      const sameSession = this.clientToolsFingerprintSessionId === session.sessionId;\n      const unchanged =\n        hasClientTools && sameSession && this.lastSentClientToolsFingerprint === clientToolsFingerprint;\n\n      // `forceFull` flips to true after a 409 cache-miss so the single retry\n      // resends the full list. Capture any error body read inside the loop so\n      // the `!response.ok` handler below doesn't re-consume the stream.\n      let forceFull = false;\n      let errorData: { error?: string; hint?: string } | null = null;\n      let response: Response;\n      for (let attempt = 0; ; attempt++) {\n        const sendFull = hasClientTools && (forceFull || !unchanged);\n        const chatRequest: ClientChatRequest = {\n          ...baseChatRequest,\n          ...(sendFull && fullClientTools ? { clientTools: fullClientTools } : {}),\n          ...(clientToolsFingerprint ? { clientToolsFingerprint } : {}),\n        };\n\n        if (this.debug) {\n          // eslint-disable-next-line no-console\n          console.debug(\"[AgentWidgetClient] client token dispatch\", chatRequest);\n        }\n\n        response = await fetch(this.getClientApiUrl('chat'), {\n          method: 'POST',\n          headers: {\n            'Content-Type': 'application/json',\n            'X-Persona-Version': VERSION,\n          },\n          body: JSON.stringify(chatRequest),\n          signal: controller.signal,\n        });\n\n        // Diff-only cache miss: the server has no stored tool set matching our\n        // fingerprint. Retry exactly once with the full list. A second miss\n        // falls through to the normal error handling below (no infinite loop).\n        if (response.status === 409 && attempt === 0 && hasClientTools) {\n          const body = (await response.json().catch(() => null)) as\n            | { error?: string; hint?: string }\n            | null;\n          if (body?.error === 'client_tools_resend_required') {\n            forceFull = true;\n            // Invalidate so future turns also resend until a clean success\n            // commits a fresh fingerprint.\n            this.lastSentClientToolsFingerprint = null;\n            continue;\n          }\n          // Some other 409: keep the parsed body for the handler below.\n          errorData = body ?? { error: 'Chat request failed' };\n        }\n        break;\n      }\n\n      if (!response.ok) {\n        const data = errorData ?? (await response.json().catch(() => ({ error: 'Chat request failed' })));\n\n        if (response.status === 401) {\n          // Session expired\n          this.clearClientSession();\n          this.config.onSessionExpired?.();\n          const error = new Error('Session expired. Please refresh to continue.');\n          onEvent({ type: \"error\", error });\n          throw error;\n        }\n\n        if (response.status === 429) {\n          const error = new Error(data.hint || 'Message limit reached for this session.');\n          onEvent({ type: \"error\", error });\n          throw error;\n        }\n\n        const error = new Error(data.error || 'Failed to send message');\n        onEvent({ type: \"error\", error });\n        throw error;\n      }\n\n      if (!response.body) {\n        const error = new Error('No response body received');\n        onEvent({ type: \"error\", error });\n        throw error;\n      }\n\n      // Stream is good: the server now holds this tool set under this\n      // fingerprint for the session. Commit the cache so unchanged follow-up\n      // turns can send fingerprint-only.\n      this.lastSentClientToolsFingerprint = clientToolsFingerprint ?? null;\n      this.clientToolsFingerprintSessionId = session.sessionId;\n\n      onEvent({ type: \"status\", status: \"connected\" });\n      \n      // Stream the response (same SSE handling as proxy mode)\n      try {\n        await this.streamResponse(response.body, onEvent, options.assistantMessageId);\n      } finally {\n        onEvent({ type: \"status\", status: \"idle\" });\n      }\n    } catch (error) {\n      const err = error instanceof Error ? error : new Error(String(error));\n      // Only emit error if it wasn't already emitted\n      if (!err.message.includes('Session expired') && !err.message.includes('Message limit')) {\n        onEvent({ type: \"error\", error: err });\n      }\n      throw err;\n    }\n  }\n\n  /**\n   * Proxy mode dispatch (original implementation)\n   */\n  private async dispatchProxy(options: DispatchOptions, onEvent: SSEHandler) {\n    const controller = new AbortController();\n    if (options.signal) {\n      options.signal.addEventListener(\"abort\", () => controller.abort());\n    }\n\n    onEvent({ type: \"status\", status: \"connecting\" });\n\n    const payload = await this.buildPayload(options.messages);\n\n    if (this.debug) {\n      // eslint-disable-next-line no-console\n      console.debug(\"[AgentWidgetClient] dispatch payload\", payload);\n    }\n\n    // Build headers - merge static headers with dynamic headers if provided\n    let headers = { ...this.headers };\n    if (this.getHeaders) {\n      try {\n        const dynamicHeaders = await this.getHeaders();\n        headers = { ...headers, ...dynamicHeaders };\n      } catch (error) {\n        if (typeof console !== \"undefined\") {\n          // eslint-disable-next-line no-console\n          console.error(\"[AgentWidget] getHeaders error:\", error);\n        }\n      }\n    }\n\n    // Use customFetch if provided, otherwise use default fetch\n    let response: Response;\n    if (this.customFetch) {\n      try {\n        response = await this.customFetch(\n          this.apiUrl,\n          {\n            method: \"POST\",\n            headers,\n            body: JSON.stringify(payload),\n            signal: controller.signal\n          },\n          payload\n        );\n      } catch (error) {\n        const err = error instanceof Error ? error : new Error(String(error));\n        onEvent({ type: \"error\", error: err });\n        throw err;\n      }\n    } else {\n      response = await fetch(this.apiUrl, {\n        method: \"POST\",\n        headers,\n        body: JSON.stringify(payload),\n        signal: controller.signal\n      });\n    }\n\n    if (!response.ok || !response.body) {\n      const error = new Error(\n        `Chat backend request failed: ${response.status} ${response.statusText}`\n      );\n      onEvent({ type: \"error\", error });\n      throw error;\n    }\n\n    onEvent({ type: \"status\", status: \"connected\" });\n    try {\n      await this.streamResponse(response.body, onEvent);\n    } finally {\n      onEvent({ type: \"status\", status: \"idle\" });\n    }\n  }\n\n  /**\n   * Agent mode dispatch\n   */\n  private async dispatchAgent(options: DispatchOptions, onEvent: SSEHandler) {\n    const controller = new AbortController();\n    if (options.signal) {\n      options.signal.addEventListener(\"abort\", () => controller.abort());\n    }\n\n    onEvent({ type: \"status\", status: \"connecting\" });\n\n    const payload = await this.buildAgentPayload(options.messages);\n\n    if (this.debug) {\n      // eslint-disable-next-line no-console\n      console.debug(\"[AgentWidgetClient] agent dispatch payload\", payload);\n    }\n\n    // Build headers - merge static headers with dynamic headers if provided\n    let headers = { ...this.headers };\n    if (this.getHeaders) {\n      try {\n        const dynamicHeaders = await this.getHeaders();\n        headers = { ...headers, ...dynamicHeaders };\n      } catch (error) {\n        if (typeof console !== \"undefined\") {\n          // eslint-disable-next-line no-console\n          console.error(\"[AgentWidget] getHeaders error:\", error);\n        }\n      }\n    }\n\n    // Use customFetch if provided, otherwise use default fetch\n    let response: Response;\n    if (this.customFetch) {\n      try {\n        response = await this.customFetch(\n          this.apiUrl,\n          {\n            method: \"POST\",\n            headers,\n            body: JSON.stringify(payload),\n            signal: controller.signal\n          },\n          payload as unknown as AgentWidgetRequestPayload\n        );\n      } catch (error) {\n        const err = error instanceof Error ? error : new Error(String(error));\n        onEvent({ type: \"error\", error: err });\n        throw err;\n      }\n    } else {\n      response = await fetch(this.apiUrl, {\n        method: \"POST\",\n        headers,\n        body: JSON.stringify(payload),\n        signal: controller.signal\n      });\n    }\n\n    if (!response.ok || !response.body) {\n      const error = new Error(\n        `Agent execution request failed: ${response.status} ${response.statusText}`\n      );\n      onEvent({ type: \"error\", error });\n      throw error;\n    }\n\n    onEvent({ type: \"status\", status: \"connected\" });\n    try {\n      await this.streamResponse(response.body, onEvent, options.assistantMessageId);\n    } finally {\n      onEvent({ type: \"status\", status: \"idle\" });\n    }\n  }\n\n  /**\n   * Process an external SSE stream through the SDK's event pipeline.\n   * This allows piping responses from endpoints like agent approval\n   * through the same message/tool/reasoning handling as dispatch().\n   */\n  public async processStream(\n    body: ReadableStream<Uint8Array>,\n    onEvent: SSEHandler,\n    assistantMessageId?: string\n  ): Promise<void> {\n    onEvent({ type: \"status\", status: \"connected\" });\n    try {\n      await this.streamResponse(body, onEvent, assistantMessageId);\n    } finally {\n      onEvent({ type: \"status\", status: \"idle\" });\n    }\n  }\n\n  /**\n   * Send an approval decision to the API and return the response\n   * for streaming continuation.\n   */\n  public async resolveApproval(\n    approval: { agentId: string; executionId: string; approvalId: string },\n    decision: 'approved' | 'denied'\n  ): Promise<Response> {\n    const baseUrl = this.config.apiUrl\n      ?.replace(/\\/+$/, '')\n      .replace(/\\/v1\\/dispatch$/, '') || DEFAULT_CLIENT_API_BASE;\n    const url = `${baseUrl}/v1/agents/${approval.agentId}/approve`;\n\n    let headers: Record<string, string> = {\n      'Content-Type': 'application/json',\n      ...this.headers\n    };\n    if (this.getHeaders) {\n      Object.assign(headers, await this.getHeaders());\n    }\n\n    return fetch(url, {\n      method: 'POST',\n      headers,\n      body: JSON.stringify({\n        executionId: approval.executionId,\n        approvalId: approval.approvalId,\n        decision,\n        streamResponse: true,\n      }),\n    });\n  }\n\n  /**\n   * Resume a paused flow execution by supplying outputs for LOCAL\n   * (client-executed) tools. Used by the built-in `ask_user_question`\n   * answer-pill sheet, but generic enough for any LOCAL tool.\n   *\n   * Routes by mode:\n   *  - **client-token mode**: POST `${apiBase}/v1/client/resume` (the\n   *    session-authenticated sibling of `/v1/client/chat`; runtypelabs/core#3889),\n   *    with the active `sessionId` in the body and no Bearer key: a browser\n   *    client-token page holds no secret. `clientTools` are already persisted\n   *    server-side from the dispatch turn, so only `toolOutputs` is re-sent.\n   *  - **dispatch / proxy mode**: POST `${apiUrl}/resume`: Runtype mounts\n   *    resume as a child of `/v1/dispatch`, so the URL is `${apiUrl}/resume`,\n   *    and proxies follow the same shape (`/api/chat/dispatch/resume`).\n   *\n   * Returns the raw Response so the caller can pipe its SSE body through\n   * `connectStream()`.\n   *\n   * @param executionId - The paused execution id carried on `step_await`.\n   * @param toolOutputs - Map keyed by per-call `toolCallId` (core#3878),\n   *   falling back to tool name for legacy servers → the tool's result value.\n   */\n  public async resumeFlow(\n    executionId: string,\n    toolOutputs: Record<string, unknown>,\n    options?: { streamResponse?: boolean; signal?: AbortSignal }\n  ): Promise<Response> {\n    const isClientToken = this.isClientTokenMode();\n    const url = isClientToken\n      ? this.getClientApiUrl('resume')\n      : `${this.config.apiUrl?.replace(/\\/+$/, '') || DEFAULT_CLIENT_API_BASE}/resume`;\n\n    // The client-token resume route authenticates the session, not a Bearer\n    // key. A WebMCP approval can sit awaiting user input for a long time, so by\n    // the time we resume the original session may have expired. Re-validate (and\n    // silently re-init if needed) via initSession(): which returns the live\n    // session when `new Date() < expiresAt`, else mints a fresh one: instead of\n    // trusting the possibly-stale `this.clientSession`. (core#3889; BugBot\n    // PR #214 r3367875360.)\n    let resumeSessionId: string | undefined;\n    if (isClientToken) {\n      resumeSessionId = (await this.initSession()).sessionId;\n    }\n\n    let headers: Record<string, string> = {\n      'Content-Type': 'application/json',\n      ...this.headers\n    };\n    if (this.getHeaders) {\n      Object.assign(headers, await this.getHeaders());\n    }\n\n    const body: Record<string, unknown> = {\n      executionId,\n      toolOutputs,\n      streamResponse: options?.streamResponse ?? true,\n    };\n    // Thread the (refreshed) sessionId through like `/v1/client/chat` does.\n    if (resumeSessionId) {\n      body.sessionId = resumeSessionId;\n    }\n\n    return fetch(url, {\n      method: 'POST',\n      headers,\n      body: JSON.stringify(body),\n      signal: options?.signal,\n    });\n  }\n\n  private async buildAgentPayload(\n    messages: AgentWidgetMessage[]\n  ): Promise<AgentWidgetAgentRequestPayload> {\n    const routedAgentId = this.routing().agentId;\n    if (!this.config.agent && !routedAgentId) {\n      throw new Error('Agent configuration required for agent mode');\n    }\n\n    // Filter out messages with empty content and normalize\n    const normalizedMessages = messages\n      .slice()\n      .filter(hasValidContent)\n      .filter(m => m.role === \"user\" || m.role === \"assistant\" || m.role === \"system\")\n      .filter(m => !m.variant || m.variant === \"assistant\")\n      .sort((a, b) => {\n        const timeA = new Date(a.createdAt).getTime();\n        const timeB = new Date(b.createdAt).getTime();\n        return timeA - timeB;\n      })\n      .map((message) => ({\n        role: message.role,\n        content: message.contentParts ?? message.llmContent ?? message.rawContent ?? message.content,\n        createdAt: message.createdAt\n      }));\n\n    const payload: AgentWidgetAgentRequestPayload = {\n      agent: this.config.agent ?? { agentId: routedAgentId! },\n      messages: normalizedMessages,\n      options: {\n        streamResponse: true,\n        recordMode: 'virtual',\n        ...this.config.agentOptions\n      }\n    };\n\n    // Client tools: built-in widget tools (ask_user_question, when exposed)\n    // plus the per-turn WebMCP page-registry snapshot. Name collisions are\n    // impossible: WebMCP entries are `webmcp:`-prefixed server-side while\n    // `sdk`-origin built-ins keep bare names. Both kinds ride the same\n    // diff-only fingerprint path in client-token mode. Kept to a single await\n    // so dispatch microtask timing is unchanged.\n    const clientTools = [\n      ...builtInClientToolsForDispatch(this.config),\n      ...((await this.webMcpBridge?.snapshotForDispatch()) ?? []),\n    ];\n    if (clientTools.length > 0) {\n      payload.clientTools = clientTools;\n    }\n\n    // Add context from providers\n    if (this.contextProviders.length) {\n      const contextAggregate: Record<string, unknown> = {};\n      await Promise.all(\n        this.contextProviders.map(async (provider) => {\n          try {\n            const result = await provider({\n              messages,\n              config: this.config\n            });\n            if (result && typeof result === \"object\") {\n              Object.assign(contextAggregate, result);\n            }\n          } catch (error) {\n            if (typeof console !== \"undefined\") {\n              // eslint-disable-next-line no-console\n              console.warn(\"[AgentWidget] Context provider failed:\", error);\n            }\n          }\n        })\n      );\n\n      if (Object.keys(contextAggregate).length) {\n        payload.context = contextAggregate;\n      }\n    }\n\n    return payload;\n  }\n\n  private async buildPayload(\n    messages: AgentWidgetMessage[]\n  ): Promise<AgentWidgetRequestPayload> {\n    // Filter out messages with empty content to prevent validation errors\n    const normalizedMessages = messages\n      .slice()\n      .filter(hasValidContent)\n      .sort((a, b) => {\n        const timeA = new Date(a.createdAt).getTime();\n        const timeB = new Date(b.createdAt).getTime();\n        return timeA - timeB;\n      })\n      .map((message) => ({\n        role: message.role,\n        // Priority: contentParts (multi-modal) > llmContent (explicit LLM content) > rawContent (structured parsers) > content (display)\n        content: message.contentParts ?? message.llmContent ?? message.rawContent ?? message.content,\n        createdAt: message.createdAt\n      }));\n\n    const routed = this.routing();\n    const payload: AgentWidgetRequestPayload = {\n      messages: normalizedMessages,\n      ...(routed.agentId\n        ? { agent: { agentId: routed.agentId } }\n        : routed.flowId\n          ? { flowId: routed.flowId }\n          : {})\n    };\n\n    // Custom-provider targets (e.g. `eve:support`) resolve to a payload\n    // fragment that is merged into the dispatch body so a BYO backend can read\n    // whatever routing keys its resolver chose. `messages` is authoritative and\n    // can never be overridden by a resolver.\n    if (routed.targetPayload) {\n      for (const [key, value] of Object.entries(routed.targetPayload)) {\n        if (key === \"messages\") continue;\n        (payload as Record<string, unknown>)[key] = value;\n      }\n    }\n\n    // Client tools: same built-in + WebMCP merge as buildAgentPayload\n    // (flow-dispatch path).\n    const clientTools = [\n      ...builtInClientToolsForDispatch(this.config),\n      ...((await this.webMcpBridge?.snapshotForDispatch()) ?? []),\n    ];\n    if (clientTools.length > 0) {\n      payload.clientTools = clientTools;\n    }\n\n    if (this.contextProviders.length) {\n      const contextAggregate: Record<string, unknown> = {};\n      await Promise.all(\n        this.contextProviders.map(async (provider) => {\n          try {\n            const result = await provider({\n              messages,\n              config: this.config\n            });\n            if (result && typeof result === \"object\") {\n              Object.assign(contextAggregate, result);\n            }\n          } catch (error) {\n            if (typeof console !== \"undefined\") {\n              // eslint-disable-next-line no-console\n              console.warn(\"[AgentWidget] Context provider failed:\", error);\n            }\n          }\n        })\n      );\n\n      if (Object.keys(contextAggregate).length) {\n        payload.context = contextAggregate;\n      }\n    }\n\n    if (this.requestMiddleware) {\n      try {\n        const result = await this.requestMiddleware({\n          payload: { ...payload },\n          config: this.config\n        });\n        if (result && typeof result === \"object\") {\n          const next = result as AgentWidgetRequestPayload;\n          // Preserve `clientTools` if the middleware returned a fresh\n          // payload object without it. Naive middlewares often rebuild\n          // the payload by listing the fields they care about and\n          // dropping `clientTools` accidentally; the WebMCP wire surface\n          // is invisible to them. The integrator can still set\n          // `clientTools: []` or `clientTools: undefined` explicitly to\n          // strip them on purpose: we only fall back when the field is\n          // entirely absent from the returned object.\n          if (\n            payload.clientTools !== undefined &&\n            !(\"clientTools\" in next)\n          ) {\n            next.clientTools = payload.clientTools;\n          }\n          return next;\n        }\n      } catch (error) {\n        if (typeof console !== \"undefined\") {\n          // eslint-disable-next-line no-console\n          console.error(\"[AgentWidget] Request middleware error:\", error);\n        }\n      }\n    }\n\n    return payload;\n  }\n\n  /**\n   * Handle custom SSE event parsing via parseSSEEvent callback\n   * Returns true if event was handled, false otherwise\n   */\n  private async handleCustomSSEEvent(\n    payload: unknown,\n    onEvent: SSEHandler,\n    assistantMessageRef: { current: AgentWidgetMessage | null },\n    emitMessage: (msg: AgentWidgetMessage) => void,\n    nextSequence: () => number,\n    partIdState: { current: string | null }\n  ): Promise<boolean> {\n    if (!this.parseSSEEvent) return false;\n\n    try {\n      const result = await this.parseSSEEvent(payload);\n      if (result === null) return false; // Event should be ignored\n\n      const createNewAssistant = (partId?: string): AgentWidgetMessage => {\n        const msg: AgentWidgetMessage = {\n          id: `assistant-${Date.now()}-${Math.random().toString(16).slice(2)}`,\n          role: \"assistant\",\n          content: \"\",\n          createdAt: new Date().toISOString(),\n          streaming: true,\n          variant: \"assistant\",\n          sequence: nextSequence(),\n          ...(partId !== undefined && { partId })\n        };\n        assistantMessageRef.current = msg;\n        emitMessage(msg);\n        return msg;\n      };\n\n      const ensureAssistant = (partId?: string) => {\n        if (assistantMessageRef.current) return assistantMessageRef.current;\n        return createNewAssistant(partId);\n      };\n\n      if (result.text !== undefined) {\n        // partId-based message segmentation: when partId changes, seal current\n        // message and start a new one for chronological tool/text interleaving\n        if (result.partId !== undefined && partIdState.current !== null && result.partId !== partIdState.current) {\n          // Seal the current assistant message\n          if (assistantMessageRef.current) {\n            assistantMessageRef.current.streaming = false;\n            emitMessage(assistantMessageRef.current);\n          }\n          // Create a new assistant message for the new text segment\n          createNewAssistant(result.partId);\n        }\n\n        // Update partId tracking (only when partId is provided: backward compatible)\n        if (result.partId !== undefined) {\n          partIdState.current = result.partId;\n        }\n\n        const assistant = ensureAssistant(result.partId);\n        // Tag the message with partId if present and not already set\n        if (result.partId !== undefined && !assistant.partId) {\n          assistant.partId = result.partId;\n        }\n        assistant.content += result.text;\n        emitMessage(assistant);\n      }\n\n      if (result.done) {\n        if (assistantMessageRef.current) {\n          assistantMessageRef.current.streaming = false;\n          emitMessage(assistantMessageRef.current);\n        }\n        partIdState.current = null;\n        onEvent({ type: \"status\", status: \"idle\" });\n      }\n\n      if (result.error) {\n        partIdState.current = null;\n        onEvent({\n          type: \"error\",\n          error: new Error(result.error)\n        });\n      }\n\n      return true; // Event was handled\n    } catch (error) {\n      if (typeof console !== \"undefined\") {\n        // eslint-disable-next-line no-console\n        console.error(\"[AgentWidget] parseSSEEvent error:\", error);\n      }\n      return false;\n    }\n  }\n\n  private async streamResponse(\n    body: ReadableStream<Uint8Array>,\n    onEvent: SSEHandler,\n    assistantMessageId?: string\n  ) {\n    const reader = body.getReader();\n    const decoder = new TextDecoder();\n    let buffer = \"\";\n\n    const baseSequence = Date.now();\n    let sequenceCounter = 0;\n    const nextSequence = () => baseSequence + sequenceCounter++;\n\n    const cloneMessage = (msg: AgentWidgetMessage): AgentWidgetMessage => {\n      const reasoning = msg.reasoning\n        ? {\n            ...msg.reasoning,\n            chunks: [...msg.reasoning.chunks]\n          }\n        : undefined;\n      const toolCall = msg.toolCall\n        ? {\n            ...msg.toolCall,\n            chunks: msg.toolCall.chunks ? [...msg.toolCall.chunks] : undefined\n          }\n        : undefined;\n      const tools = msg.tools\n        ? msg.tools.map((tool) => ({\n            ...tool,\n            chunks: tool.chunks ? [...tool.chunks] : undefined\n          }))\n        : undefined;\n\n      return {\n        ...msg,\n        reasoning,\n        toolCall,\n        tools\n      };\n    };\n\n    const shouldEmitMessage = (msg: AgentWidgetMessage): boolean => {\n      if (msg.role !== \"assistant\" || msg.variant) return true;\n\n      const hasContentParts =\n        Array.isArray(msg.contentParts) && msg.contentParts.length > 0;\n      const hasRawContent =\n        typeof msg.rawContent === \"string\" && msg.rawContent.trim() !== \"\";\n      const hasVisibleText =\n        typeof msg.content === \"string\" && msg.content.trim() !== \"\";\n\n      // Do not surface assistant text bubbles that only contain whitespace.\n      // Some providers emit newline-only text parts around a leading tool call;\n      // rendering those as normal messages creates an empty bubble above the\n      // tool card. Keep media/component/stop-reason messages renderable.\n      return hasVisibleText || hasContentParts || hasRawContent || Boolean(msg.stopReason);\n    };\n\n    const emitMessage = (msg: AgentWidgetMessage) => {\n      if (!shouldEmitMessage(msg)) return;\n      onEvent({\n        type: \"message\",\n        message: cloneMessage(msg)\n      });\n    };\n\n    let assistantMessage: AgentWidgetMessage | null = null;\n    // Tracks the most recently touched assistant text message for the\n    // current agent turn so `agent_turn_complete.stopReason` can attach\n    // to the final visible text segment even after `assistantMessage`\n    // has been finalized at a tool-call boundary within the turn.\n    let lastAssistantInTurn: AgentWidgetMessage | null = null;\n    // Reference to track assistant message for custom event handler\n    const assistantMessageRef = { current: null as AgentWidgetMessage | null };\n    // Segmentation state for the `parseSSEEvent` extensibility callback (the\n    // consumer's own `partId` field) — independent of the wire.\n    const customParsePartId = { current: null as string | null };\n    // Unified text-channel block id (from `text_start`/`text_delta` `id`). Drives\n    // bubble-id segmentation on the wire in place of the legacy `partId`:\n    // a new block id means a new bubble, sealed at `text_complete`/tool boundaries.\n    let currentTextBlockId: string | null = null;\n    // Raw text accumulated for the open flow block before its bubble is\n    // materialized — lets a whitespace-only block resolve without a stray bubble.\n    let pendingFlowRaw = \"\";\n    // Nested flow-as-tool attribution (PR #4602): a text/reasoning block whose\n    // `parentToolCallId` matches a `tool_start.toolCallId` belongs to a flow\n    // running as that tool. Keyed by the wire block id, these route the block's\n    // deltas into a message tagged `agentMetadata.parentToolId` (the parent tool's\n    // row) instead of the top-level assistant/reasoning channel.\n    const nestedBlockParent = new Map<string, string>();\n    const nestedBlockMessages = new Map<string, AgentWidgetMessage>();\n    const nestedBlockRaw = new Map<string, string>();\n    const reasoningMessages = new Map<string, AgentWidgetMessage>();\n    const toolMessages = new Map<string, AgentWidgetMessage>();\n    const reasoningContext = {\n      lastId: null as string | null,\n      byStep: new Map<string, string>()\n    };\n    const toolContext = {\n      lastId: null as string | null,\n      byCall: new Map<string, string>()\n    };\n\n    const normalizeKey = (value: unknown): string | null => {\n      if (value === null || value === undefined) return null;\n      try {\n        return String(value);\n      } catch (error) {\n        return null;\n      }\n    };\n\n    const getStepKey = (payload: Record<string, any>) =>\n      normalizeKey(\n        payload.stepId ??\n          payload.step_id ??\n          payload.step ??\n          payload.parentId ??\n          payload.flowStepId ??\n          payload.flow_step_id\n      );\n\n    const getToolCallKey = (payload: Record<string, any>) =>\n      normalizeKey(\n        payload.callId ??\n          payload.call_id ??\n          payload.requestId ??\n          payload.request_id ??\n          payload.toolCallId ??\n          payload.tool_call_id ??\n          payload.stepId ??\n          payload.step_id\n      );\n\n    const baseAssistantId = assistantMessageId;\n    let assistantIdConsumed = false;\n\n    const ensureAssistantMessage = () => {\n      if (assistantMessage) return assistantMessage;\n      let id: string;\n      const segment = currentTextBlockId;\n      if (!assistantIdConsumed && baseAssistantId) {\n        id = baseAssistantId;\n        assistantIdConsumed = true;\n      } else if (baseAssistantId && segment) {\n        id = `${baseAssistantId}_${segment}`;\n      } else {\n        id = `assistant-${Date.now()}-${Math.random().toString(16).slice(2)}`;\n      }\n      assistantMessage = {\n        id,\n        role: \"assistant\",\n        content: \"\",\n        createdAt: new Date().toISOString(),\n        streaming: true,\n        sequence: nextSequence()\n      };\n      emitMessage(assistantMessage);\n      return assistantMessage;\n    };\n\n    const trackReasoningId = (stepKey: string | null, id: string) => {\n      reasoningContext.lastId = id;\n      if (stepKey) {\n        reasoningContext.byStep.set(stepKey, id);\n      }\n    };\n\n    const resolveReasoningId = (\n      payload: Record<string, any>,\n      allowCreate: boolean\n    ): string | null => {\n      const rawId = payload.reasoningId ?? payload.id;\n      const stepKey = getStepKey(payload);\n      if (rawId) {\n        const resolved = String(rawId);\n        trackReasoningId(stepKey, resolved);\n        return resolved;\n      }\n      if (stepKey) {\n        const existing = reasoningContext.byStep.get(stepKey);\n        if (existing) {\n          reasoningContext.lastId = existing;\n          return existing;\n        }\n      }\n      if (reasoningContext.lastId && !allowCreate) {\n        return reasoningContext.lastId;\n      }\n      if (!allowCreate) {\n        return null;\n      }\n      const generated = `reason-${nextSequence()}`;\n      trackReasoningId(stepKey, generated);\n      return generated;\n    };\n\n    const ensureReasoningMessage = (reasoningId: string) => {\n      const existing = reasoningMessages.get(reasoningId);\n      if (existing) {\n        return existing;\n      }\n\n      const message: AgentWidgetMessage = {\n        id: `reason-${reasoningId}`,\n        role: \"assistant\",\n        content: \"\",\n        createdAt: new Date().toISOString(),\n        streaming: true,\n        variant: \"reasoning\",\n        sequence: nextSequence(),\n        reasoning: {\n          id: reasoningId,\n          status: \"streaming\",\n          chunks: []\n        }\n      };\n\n      reasoningMessages.set(reasoningId, message);\n      emitMessage(message);\n      return message;\n    };\n\n    const trackToolId = (callKey: string | null, id: string) => {\n      toolContext.lastId = id;\n      if (callKey) {\n        toolContext.byCall.set(callKey, id);\n      }\n    };\n\n    // Track tool call IDs for artifact emit tools so we can suppress their UI\n    const artifactToolCallIds = new Set<string>();\n    // Track artifact reference card messages so we can update them on artifact_complete\n    const artifactCardMessages = new Map<string, AgentWidgetMessage>();\n    // Track artifact IDs that already have a reference card (from auto-creation or transcript_insert)\n    const artifactIdsWithCards = new Set<string>();\n    // Accumulate artifact markdown content for embedding in card props on complete\n    const artifactContent = new Map<string, { markdown: string; title?: string }>();\n    const isArtifactEmitToolName = (name: string | undefined): boolean => {\n      if (!name) return false;\n      const normalized = name.replace(/_+/g, \"_\").replace(/^_|_$/g, \"\");\n      return normalized === \"emit_artifact_markdown\" || normalized === \"emit_artifact_component\";\n    };\n\n    const resolveToolId = (\n      payload: Record<string, any>,\n      allowCreate: boolean\n    ): string | null => {\n      const rawId = payload.toolId ?? payload.id;\n      const callKey = getToolCallKey(payload);\n      if (rawId) {\n        const resolved = String(rawId);\n        trackToolId(callKey, resolved);\n        return resolved;\n      }\n      if (callKey) {\n        const existing = toolContext.byCall.get(callKey);\n        if (existing) {\n          toolContext.lastId = existing;\n          return existing;\n        }\n      }\n      if (toolContext.lastId && !allowCreate) {\n        return toolContext.lastId;\n      }\n      if (!allowCreate) {\n        return null;\n      }\n      const generated = `tool-${nextSequence()}`;\n      trackToolId(callKey, generated);\n      return generated;\n    };\n\n    const ensureToolMessage = (toolId: string) => {\n      const existing = toolMessages.get(toolId);\n      if (existing) {\n        return existing;\n      }\n\n      const message: AgentWidgetMessage = {\n        id: `tool-${toolId}`,\n        role: \"assistant\",\n        content: \"\",\n        createdAt: new Date().toISOString(),\n        streaming: true,\n        variant: \"tool\",\n        sequence: nextSequence(),\n        toolCall: {\n          id: toolId,\n          status: \"pending\"\n        }\n      };\n\n      toolMessages.set(toolId, message);\n      emitMessage(message);\n      return message;\n    };\n\n    const resolveTimestamp = (value: unknown) => {\n      if (typeof value === \"number\" && Number.isFinite(value)) {\n        return value;\n      }\n      if (typeof value === \"string\") {\n        const parsed = Number(value);\n        if (!Number.isNaN(parsed) && Number.isFinite(parsed)) {\n          return parsed;\n        }\n        const dateParsed = Date.parse(value);\n        if (!Number.isNaN(dateParsed)) {\n          return dateParsed;\n        }\n      }\n      return Date.now();\n    };\n\n    const ensureStringContent = (value: unknown): string => {\n      if (typeof value === \"string\") {\n        return value;\n      }\n      if (value === null || value === undefined) {\n        return \"\";\n      }\n      // Convert objects/arrays to JSON string\n      try {\n        return JSON.stringify(value);\n      } catch {\n        return String(value);\n      }\n    };\n\n    // Maintain stateful stream parsers per message for incremental parsing\n    const streamParsers = new Map<string, AgentWidgetStreamParser>();\n    // Track accumulated raw content for structured formats (JSON, XML, etc.)\n    const rawContentBuffers = new Map<string, string>();\n    // Rebuild incremental text by sequence so late arrivals can repair already-emitted\n    // content after the reorder buffer's gap-timeout flush.\n    const orderedChunkBuffers = new Map<string, Array<{ seq: number; text: string }>>();\n\n    const insertOrderedChunk = (key: string, seq: number, text: string): string => {\n      let chunks = orderedChunkBuffers.get(key);\n      if (!chunks) {\n        chunks = [];\n        orderedChunkBuffers.set(key, chunks);\n      }\n\n      let lo = 0;\n      let hi = chunks.length;\n      while (lo < hi) {\n        const mid = (lo + hi) >>> 1;\n        if (chunks[mid].seq < seq) {\n          lo = mid + 1;\n        } else {\n          hi = mid;\n        }\n      }\n\n      if (chunks[lo]?.seq === seq) {\n        chunks[lo] = { seq, text };\n      } else {\n        chunks.splice(lo, 0, { seq, text });\n      }\n\n      let accumulated = \"\";\n      for (let index = 0; index < chunks.length; index++) {\n        accumulated += chunks[index].text;\n      }\n      return accumulated;\n    };\n\n    /**\n     * After text_end + didSplitByPartId, merge the authoritative final response into the\n     * sealed message when streaming left content short (e.g. async parser lag).\n     */\n    const reconcileSealedAssistantWithFinalResponse = (\n      msg: AgentWidgetMessage,\n      finalContent: unknown\n    ) => {\n      const finalString = ensureStringContent(finalContent);\n      const rawBuffer = rawContentBuffers.get(msg.id);\n      const contentToProcess = preferFinalStructuredContent(rawBuffer, finalString);\n      msg.rawContent = contentToProcess;\n      const parser = streamParsers.get(msg.id);\n\n      const mergeIfBetter = (mergedDisplay: string) => {\n        const cur = msg.content ?? \"\";\n        if (mergedDisplay.trim() === \"\") return;\n        // Only replace when empty, or when the stream left a strict prefix of the\n        // authoritative final (truncation). Do not use length alone: multi-segment\n        // flows can have a short last bubble whose content is not a prefix of the\n        // full step response.\n        if (\n          cur.trim().length === 0 ||\n          mergedDisplay.startsWith(cur) ||\n          mergedDisplay.trimStart().startsWith(cur.trim())\n        ) {\n          msg.content = mergedDisplay;\n        }\n      };\n\n      const finalizeCleanup = () => {\n        if (parser) {\n          const closeResult = parser.close?.();\n          if (closeResult instanceof Promise) closeResult.catch(() => {});\n        }\n        streamParsers.delete(msg.id);\n        rawContentBuffers.delete(msg.id);\n        msg.streaming = false;\n        emitMessage(msg);\n      };\n\n      if (!parser) {\n        mergeIfBetter(finalString);\n        finalizeCleanup();\n        return;\n      }\n\n      // Prefer JSON fast path when the final payload is JSON-shaped\n      const extractedFromJson = extractTextFromJson(contentToProcess);\n      if (extractedFromJson !== null && extractedFromJson.trim() !== \"\") {\n        mergeIfBetter(extractedFromJson);\n        finalizeCleanup();\n        return;\n      }\n\n      const bestDisplayText = (\n        result: AgentWidgetStreamParserResult | string | null\n      ): string => {\n        const text =\n          typeof result === \"string\" ? result : result?.text ?? null;\n        if (text !== null && text.trim() !== \"\") return text;\n        const extracted = parser.getExtractedText();\n        if (extracted !== null && extracted.trim() !== \"\") return extracted;\n        return finalString;\n      };\n\n      let parsedResult: ReturnType<typeof parser.processChunk>;\n      try {\n        parsedResult = parser.processChunk(contentToProcess);\n      } catch {\n        mergeIfBetter(finalString);\n        finalizeCleanup();\n        return;\n      }\n\n      if (parsedResult instanceof Promise) {\n        parsedResult\n          .then((result) => {\n            mergeIfBetter(bestDisplayText(result));\n            finalizeCleanup();\n          })\n          .catch(() => {\n            mergeIfBetter(finalString);\n            finalizeCleanup();\n          });\n        return;\n      }\n\n      mergeIfBetter(bestDisplayText(parsedResult));\n      finalizeCleanup();\n    };\n\n    // === Unified flow text channel ===\n    // Flow prompt-step text streams as `text_delta` blocks (segmented by\n    // `text_start`/`text_complete`) and can be structured JSON, so each block\n    // runs through the per-bubble structured-content parser — agent text stays\n    // plain. This is the legacy step_delta parser core, re-keyed from `partId`\n    // to the wire block-id bubble. The caller materializes the bubble lazily\n    // (whitespace-only blocks around tool boundaries never leave a stray bubble)\n    // and `step_complete.result.response` reconciles the authoritative final.\n    let lastSealedFlowBubble: AgentWidgetMessage | null = null;\n\n    // Stream one accumulated chunk of flow block text through the parser, setting\n    // display `content` (extracted) + `rawContent` (raw) and emitting. Mirrors the\n    // legacy step_delta chunk path; plain text bypasses the structured parser.\n    const applyFlowTextChunk = (\n      assistant: AgentWidgetMessage,\n      accumulatedRaw: string,\n      chunk: string,\n      chunkSeq: number | undefined\n    ) => {\n      assistant.rawContent = accumulatedRaw;\n      if (!streamParsers.has(assistant.id)) {\n        streamParsers.set(assistant.id, this.createStreamParser());\n      }\n      const parser = streamParsers.get(assistant.id)!;\n      const looksLikeJson =\n        accumulatedRaw.trim().startsWith(\"{\") || accumulatedRaw.trim().startsWith(\"[\");\n      if (looksLikeJson) {\n        rawContentBuffers.set(assistant.id, accumulatedRaw);\n      }\n      const isPlainTextParser = (parser as any).__isPlainTextParser === true;\n      if (isPlainTextParser) {\n        assistant.content =\n          chunkSeq !== undefined ? accumulatedRaw : assistant.content + chunk;\n        rawContentBuffers.delete(assistant.id);\n        streamParsers.delete(assistant.id);\n        assistant.rawContent = undefined;\n        emitMessage(assistant);\n        return;\n      }\n      const parsedResult = parser.processChunk(accumulatedRaw);\n      if (parsedResult instanceof Promise) {\n        parsedResult\n          .then((result) => {\n            const text = typeof result === \"string\" ? result : result?.text ?? null;\n            if (text !== null && text.trim() !== \"\") {\n              assistant.content = text;\n              emitMessage(assistant);\n            } else if (!looksLikeJson && !accumulatedRaw.trim().startsWith(\"<\")) {\n              assistant.content =\n                chunkSeq !== undefined ? accumulatedRaw : assistant.content + chunk;\n              rawContentBuffers.delete(assistant.id);\n              streamParsers.delete(assistant.id);\n              assistant.rawContent = undefined;\n              emitMessage(assistant);\n            }\n          })\n          .catch(() => {\n            assistant.content =\n              chunkSeq !== undefined ? accumulatedRaw : assistant.content + chunk;\n            rawContentBuffers.delete(assistant.id);\n            streamParsers.delete(assistant.id);\n            assistant.rawContent = undefined;\n            emitMessage(assistant);\n          });\n      } else {\n        const text =\n          typeof parsedResult === \"string\" ? parsedResult : parsedResult?.text ?? null;\n        if (text !== null && text.trim() !== \"\") {\n          assistant.content = text;\n          emitMessage(assistant);\n        } else if (!looksLikeJson && !accumulatedRaw.trim().startsWith(\"<\")) {\n          assistant.content =\n            chunkSeq !== undefined ? accumulatedRaw : assistant.content + chunk;\n          rawContentBuffers.delete(assistant.id);\n          streamParsers.delete(assistant.id);\n          assistant.rawContent = undefined;\n          emitMessage(assistant);\n        }\n      }\n    };\n\n    // Seal a flow text block at `text_complete`: run final structured extraction\n    // off the accumulated raw buffer (U2: `text_complete.text` mirrors that raw\n    // buffer, so we never double-count), then finalize the bubble. The structured\n    // `step_complete.result.response` reconciles afterward.\n    const finalizeFlowTextBlock = (\n      assistant: AgentWidgetMessage,\n      finalContent?: unknown\n    ) => {\n      const effectiveFinal =\n        finalContent !== undefined && finalContent !== null\n          ? finalContent\n          : assistant.content;\n      if (\n        effectiveFinal === undefined ||\n        effectiveFinal === null ||\n        effectiveFinal === \"\"\n      ) {\n        assistant.streaming = false;\n        emitMessage(assistant);\n        return;\n      }\n      const rawBuffer = rawContentBuffers.get(assistant.id);\n      const contentToProcess = rawBuffer ?? ensureStringContent(effectiveFinal);\n      assistant.rawContent = contentToProcess;\n      const parser = streamParsers.get(assistant.id);\n      let extractedText: string | null = null;\n      let asyncPending = false;\n      if (parser) {\n        extractedText = parser.getExtractedText();\n        if (extractedText === null) {\n          extractedText = extractTextFromJson(contentToProcess);\n        }\n        if (extractedText === null) {\n          const parsedResult = parser.processChunk(contentToProcess);\n          if (parsedResult instanceof Promise) {\n            asyncPending = true;\n            parsedResult\n              .then((result) => {\n                const text =\n                  typeof result === \"string\" ? result : result?.text ?? null;\n                if (text !== null) {\n                  assistant.content = text;\n                  assistant.streaming = false;\n                  streamParsers.delete(assistant.id);\n                  rawContentBuffers.delete(assistant.id);\n                  emitMessage(assistant);\n                }\n              })\n              .catch(() => {});\n          } else {\n            extractedText =\n              typeof parsedResult === \"string\"\n                ? parsedResult\n                : parsedResult?.text ?? null;\n          }\n        }\n      }\n      if (!asyncPending) {\n        if (extractedText !== null && extractedText.trim() !== \"\") {\n          assistant.content = extractedText;\n        } else if (!rawContentBuffers.has(assistant.id)) {\n          assistant.content = ensureStringContent(effectiveFinal);\n        }\n        const parserToClose = streamParsers.get(assistant.id);\n        if (parserToClose) {\n          const closeResult = parserToClose.close?.();\n          if (closeResult instanceof Promise) closeResult.catch(() => {});\n          streamParsers.delete(assistant.id);\n        }\n        rawContentBuffers.delete(assistant.id);\n        assistant.streaming = false;\n        emitMessage(assistant);\n      }\n    };\n\n    // Materialize (lazily) the message for a nested flow-as-tool block, tagged\n    // with the parent tool-call id so the UI renders it in the parent tool's row.\n    const ensureNestedBlockMessage = (\n      blockId: string,\n      parentToolCallId: string,\n      variant?: \"reasoning\"\n    ): AgentWidgetMessage => {\n      const existing = nestedBlockMessages.get(blockId);\n      if (existing) return existing;\n      const message: AgentWidgetMessage = {\n        id: `nested-${parentToolCallId}-${blockId}`,\n        role: \"assistant\",\n        content: \"\",\n        createdAt: new Date().toISOString(),\n        streaming: true,\n        sequence: nextSequence(),\n        ...(variant ? { variant } : {}),\n        ...(variant === \"reasoning\"\n          ? { reasoning: { id: blockId, status: \"streaming\", chunks: [] } }\n          : {}),\n        agentMetadata: { parentToolId: parentToolCallId },\n      };\n      nestedBlockMessages.set(blockId, message);\n      emitMessage(message);\n      return message;\n    };\n\n    // Ready queue of parsed wire frames awaiting a drain. The API streams the\n    // 33-event wire vocabulary; each frame is parsed in the SSE loop\n    // below and rendered directly by the handler (no translation bridge), then\n    // pushed here. The wire stream is a single, in-order SSE connection, so\n    // frames drain straight through with no reordering.\n    const seqReadyQueue: Array<{ payloadType: string; payload: any }> = [];\n    // Declared here so later closures can reference it; assigned after all\n    // handler-scoped variables are initialised (before the SSE loop).\n    let drainReadyQueue: () => void;\n    // Per-stream media-block buffer: the media triad\n    // (media_start/media_delta/media_complete) is reassembled here into a single\n    // synthetic message at media_complete, keyed by the block id.\n    const mediaBuffers = new Map<\n      string,\n      { mediaType?: string; role?: string; toolCallId?: unknown; parts: string[] }\n    >();\n    // Tracks the last iteration surfaced as a per-iteration message boundary, so\n    // `turn_start` advancing the iteration rotates the bubble in 'separate' mode.\n    let lastIterationSeen = 0;\n    // Execution kind, resolved from the leading `execution_start` frame. Drives\n    // the agent-vs-flow branches that the single wire vocabulary collapses.\n    let executionKind: \"agent\" | \"flow\" = \"agent\";\n    // Whether `executionKind` was set authoritatively by an `execution_start`\n    // frame. Continuation streams (e.g. a tool-driven `/resume`) do NOT re-emit\n    // `execution_start`, so a fresh `streamResponse` for the continuation starts\n    // with the default `\"agent\"`. For a flow that mis-routes the final\n    // prompt-step finalization and duplicates the last message (the streamed\n    // text block is sealed, then `step_complete.result.response` re-renders it as\n    // a second bubble). When `execution_start` is absent we recover the flow kind\n    // from the first flow `step_*` frame below.\n    let executionKindResolved = false;\n    // Open turn id (from `turn_start`). Unified text/reasoning deltas carry their\n    // own block id, not the turn id, so the turn id is threaded onto agentMetadata\n    // from here.\n    let openTurnId: string | null = null;\n    // Agent execution state tracking\n    let agentExecution: AgentExecutionState | null = null;\n    // Track assistant messages per agent iteration for 'separate' mode\n    const agentIterationMessages = new Map<number, AgentWidgetMessage>();\n    const iterationDisplay = this.config.iterationDisplay ?? 'separate';\n\n    // Drains the queued transduced events through the main event handler.\n    // Also invoked after the SSE loop exits so any events queued at\n    // end-of-stream are processed.\n    drainReadyQueue = () => {\n      for (let i = 0; i < seqReadyQueue.length; i++) {\n        const payloadType = seqReadyQueue[i].payloadType;\n        const payload = seqReadyQueue[i].payload;\n\n        // Recover the execution kind on continuation streams that omit\n        // `execution_start` (e.g. a tool-driven `/resume`). Flow `step_*` frames\n        // carry a `stepType`; agent loops never do (they use `turn_*`). Without\n        // this, the continuation defaults to `\"agent\"` and a flow's final\n        // prompt-step finalization is duplicated. We only infer when no\n        // `execution_start` resolved the kind, so an explicit `agent` is never\n        // overridden.\n        if (\n          !executionKindResolved &&\n          executionKind !== \"flow\" &&\n          typeof (payload as { stepType?: unknown }).stepType === \"string\"\n        ) {\n          executionKind = \"flow\";\n        }\n\n        if (payloadType === \"reasoning_start\") {\n          // Nested flow-as-tool thinking (PR #4602): route to the parent tool's row.\n          const rStartBlockId = typeof payload.id === \"string\" ? payload.id : null;\n          const rStartParent =\n            typeof payload.parentToolCallId === \"string\" && payload.parentToolCallId\n              ? payload.parentToolCallId\n              : null;\n          if (rStartBlockId && rStartParent) {\n            nestedBlockParent.set(rStartBlockId, rStartParent);\n            ensureNestedBlockMessage(rStartBlockId, rStartParent, \"reasoning\");\n            continue;\n          }\n          const reasoningId =\n            resolveReasoningId(payload, true) ?? `reason-${nextSequence()}`;\n          const reasoningMessage = ensureReasoningMessage(reasoningId);\n          reasoningMessage.reasoning = reasoningMessage.reasoning ?? {\n            id: reasoningId,\n            status: \"streaming\",\n            chunks: []\n          };\n          reasoningMessage.reasoning.startedAt =\n            reasoningMessage.reasoning.startedAt ??\n            resolveTimestamp(payload.startedAt ?? payload.timestamp);\n          reasoningMessage.reasoning.completedAt = undefined;\n          reasoningMessage.reasoning.durationMs = undefined;\n          if (payload.scope === \"loop\" || payload.scope === \"turn\") {\n            reasoningMessage.reasoning.scope = payload.scope;\n          }\n          reasoningMessage.streaming = true;\n          reasoningMessage.reasoning.status = \"streaming\";\n          emitMessage(reasoningMessage);\n        } else if (payloadType === \"reasoning_delta\") {\n          // Nested flow-as-tool thinking: append to the parent-tool-row message.\n          const rDeltaBlockId = typeof payload.id === \"string\" ? payload.id : null;\n          if (\n            rDeltaBlockId &&\n            nestedBlockParent.has(rDeltaBlockId) &&\n            nestedBlockMessages.has(rDeltaBlockId)\n          ) {\n            const nested = nestedBlockMessages.get(rDeltaBlockId)!;\n            const nestedChunk =\n              payload.reasoningText ?? payload.text ?? payload.delta ?? \"\";\n            if (nestedChunk && payload.hidden !== true && nested.reasoning) {\n              nested.reasoning.chunks.push(String(nestedChunk));\n              emitMessage(nested);\n            }\n            continue;\n          }\n          const reasoningId =\n            resolveReasoningId(payload, false) ??\n            resolveReasoningId(payload, true) ??\n            `reason-${nextSequence()}`;\n          const reasoningMessage = ensureReasoningMessage(reasoningId);\n          reasoningMessage.reasoning = reasoningMessage.reasoning ?? {\n            id: reasoningId,\n            status: \"streaming\",\n            chunks: []\n          };\n          reasoningMessage.reasoning.startedAt =\n            reasoningMessage.reasoning.startedAt ??\n            resolveTimestamp(payload.startedAt ?? payload.timestamp);\n          const chunk =\n            payload.reasoningText ??\n            payload.text ??\n            payload.delta ??\n            \"\";\n          if (chunk && payload.hidden !== true) {\n            const reasonSeq = typeof payload.sequenceIndex === \"number\" ? payload.sequenceIndex : undefined;\n            if (reasonSeq !== undefined) {\n              // Rebuild chunks by seq so late arrivals after a gap-timeout flush\n              // are inserted at the correct position rather than appended.\n              const ordered = insertOrderedChunk(reasoningId, reasonSeq, String(chunk));\n              reasoningMessage.reasoning.chunks = [ordered];\n            } else {\n              reasoningMessage.reasoning.chunks.push(String(chunk));\n            }\n          }\n          reasoningMessage.reasoning.status = payload.done ? \"complete\" : \"streaming\";\n          if (payload.done) {\n            reasoningMessage.reasoning.completedAt = resolveTimestamp(\n              payload.completedAt ?? payload.timestamp\n            );\n            const start = reasoningMessage.reasoning.startedAt ?? Date.now();\n            reasoningMessage.reasoning.durationMs = Math.max(\n              0,\n              (reasoningMessage.reasoning.completedAt ?? Date.now()) - start\n            );\n\n          }\n          reasoningMessage.streaming = reasoningMessage.reasoning.status !== \"complete\";\n          emitMessage(reasoningMessage);\n        } else if (payloadType === \"reasoning_complete\") {\n          // Nested flow-as-tool thinking close: seal the parent-tool-row message.\n          const rCompleteBlockId = typeof payload.id === \"string\" ? payload.id : null;\n          if (\n            rCompleteBlockId &&\n            nestedBlockParent.has(rCompleteBlockId) &&\n            nestedBlockMessages.has(rCompleteBlockId)\n          ) {\n            const nested = nestedBlockMessages.get(rCompleteBlockId)!;\n            if (nested.reasoning) {\n              const nestedReflection =\n                typeof payload.text === \"string\" ? payload.text : \"\";\n              if (nestedReflection && nested.reasoning.chunks.length === 0) {\n                nested.reasoning.chunks.push(nestedReflection);\n              }\n              nested.reasoning.status = \"complete\";\n              nested.streaming = false;\n              emitMessage(nested);\n            }\n            nestedBlockParent.delete(rCompleteBlockId);\n            nestedBlockMessages.delete(rCompleteBlockId);\n            continue;\n          }\n          const reasoningId =\n            resolveReasoningId(payload, false) ??\n            resolveReasoningId(payload, true) ??\n            `reason-${nextSequence()}`;\n          // A close carrying text (or scope:\"loop\") is a cross-iteration\n          // reflection fold (merged spec §4 E3): the API streams nothing for the\n          // block, then delivers the whole reflection as `text` on the close.\n          // Materialize a reasoning bubble even if no reasoning_start/delta opened\n          // one, and adopt the close text when the block streamed no chunks (the\n          // common reflection case, where reasoning_start opened an empty bubble).\n          const reflectionText = typeof payload.text === \"string\" ? payload.text : \"\";\n          if (!reasoningMessages.get(reasoningId) && (reflectionText || payload.scope === \"loop\")) {\n            ensureReasoningMessage(reasoningId);\n          }\n          const reasoningMessage = reasoningMessages.get(reasoningId);\n          if (reasoningMessage?.reasoning) {\n            if (payload.scope === \"loop\" || payload.scope === \"turn\") {\n              reasoningMessage.reasoning.scope = payload.scope;\n            }\n            if (reflectionText && reasoningMessage.reasoning.chunks.length === 0) {\n              reasoningMessage.reasoning.chunks.push(reflectionText);\n            }\n            reasoningMessage.reasoning.status = \"complete\";\n            reasoningMessage.reasoning.completedAt = resolveTimestamp(\n              payload.completedAt ?? payload.timestamp\n            );\n            const start = reasoningMessage.reasoning.startedAt ?? Date.now();\n            reasoningMessage.reasoning.durationMs = Math.max(\n              0,\n              (reasoningMessage.reasoning.completedAt ?? Date.now()) - start\n            );\n            reasoningMessage.streaming = false;\n\n            emitMessage(reasoningMessage);\n          }\n          const stepKey = getStepKey(payload);\n          if (stepKey) {\n            reasoningContext.byStep.delete(stepKey);\n          }\n        } else if (payloadType === \"tool_start\") {\n          // Unified tool family (agent + flow). Seal any open assistant bubble so\n          // text→tool→text interleaves chronologically (the API also emits a\n          // text_complete here, so this is usually a no-op — kept for safety).\n          if (assistantMessage) {\n            (assistantMessage as AgentWidgetMessage).streaming = false;\n            emitMessage(assistantMessage as AgentWidgetMessage);\n            assistantMessage = null;\n          }\n          // Unified denormalizes `iteration` onto tool frames too (merged spec §2).\n          // Track it so media/reflection blocks — which carry no iteration of their\n          // own — can be stamped with the enclosing iteration even on tool-only\n          // turns that never emit a `turn_start`.\n          if (typeof payload.iteration === \"number\") lastIterationSeen = payload.iteration;\n          const toolId: string =\n            (typeof payload.toolCallId === \"string\" ? payload.toolCallId : undefined) ??\n            resolveToolId(payload, true) ??\n            `tool-${nextSequence()}`;\n          const toolName = payload.toolName ?? payload.name;\n          // Suppress tool UI for artifact emit tools: artifacts are handled via artifact_* events\n          if (isArtifactEmitToolName(toolName)) {\n            artifactToolCallIds.add(toolId);\n            continue;\n          }\n          trackToolId(getToolCallKey(payload), toolId);\n          const toolMessage = ensureToolMessage(toolId);\n          const tool = toolMessage.toolCall ?? {\n            id: toolId,\n            status: \"pending\"\n          };\n          tool.name = toolName ?? tool.name;\n          tool.status = \"running\";\n          if (payload.parameters !== undefined) {\n            tool.args = payload.parameters;\n          } else if (payload.args !== undefined) {\n            tool.args = payload.args;\n          }\n          tool.startedAt =\n            tool.startedAt ??\n            resolveTimestamp(payload.startedAt ?? payload.timestamp);\n          tool.completedAt = undefined;\n          tool.durationMs = undefined;\n          toolMessage.toolCall = tool;\n          toolMessage.streaming = true;\n          if (payload.executionId) {\n            toolMessage.agentMetadata = {\n              executionId: payload.executionId,\n              iteration: payload.iteration,\n            };\n          }\n          emitMessage(toolMessage);\n        } else if (payloadType === \"tool_output_delta\") {\n          const toolId =\n            resolveToolId(payload, false) ??\n            resolveToolId(payload, true) ??\n            `tool-${nextSequence()}`;\n          if (artifactToolCallIds.has(toolId)) continue;\n          const toolMessage = ensureToolMessage(toolId);\n          const tool = toolMessage.toolCall ?? {\n            id: toolId,\n            status: \"running\"\n          };\n          tool.startedAt =\n            tool.startedAt ??\n            resolveTimestamp(payload.startedAt ?? payload.timestamp);\n          const chunkText =\n            payload.text ?? payload.delta ?? payload.message ?? \"\";\n          if (chunkText) {\n            tool.chunks = tool.chunks ?? [];\n            tool.chunks.push(String(chunkText));\n          }\n          tool.status = \"running\";\n          toolMessage.toolCall = tool;\n          toolMessage.streaming = true;\n          const agentCtxChunk = payload.agentContext;\n          if (agentCtxChunk || payload.executionId) {\n            toolMessage.agentMetadata = toolMessage.agentMetadata ?? {\n              executionId: agentCtxChunk?.executionId ?? payload.executionId,\n              iteration: agentCtxChunk?.iteration ?? payload.iteration,\n            };\n          }\n          emitMessage(toolMessage);\n        } else if (payloadType === \"tool_complete\") {\n          const toolId =\n            resolveToolId(payload, false) ??\n            resolveToolId(payload, true) ??\n            `tool-${nextSequence()}`;\n          if (artifactToolCallIds.has(toolId)) {\n            artifactToolCallIds.delete(toolId);\n            continue;\n          }\n          const toolMessage = ensureToolMessage(toolId);\n          const tool = toolMessage.toolCall ?? {\n            id: toolId,\n            status: \"running\"\n          };\n          tool.status = \"complete\";\n          if (payload.result !== undefined) {\n            tool.result = payload.result;\n          }\n          if (typeof payload.duration === \"number\") {\n            tool.duration = payload.duration;\n          }\n          tool.completedAt = resolveTimestamp(\n            payload.completedAt ?? payload.timestamp\n          );\n          const durationValue = payload.duration ?? payload.executionTime;\n          if (typeof durationValue === \"number\") {\n            tool.durationMs = durationValue;\n          } else {\n            const start = tool.startedAt ?? Date.now();\n            tool.durationMs = Math.max(\n              0,\n              (tool.completedAt ?? Date.now()) - start\n            );\n          }\n          toolMessage.toolCall = tool;\n          toolMessage.streaming = false;\n          const agentCtxComplete = payload.agentContext;\n          if (agentCtxComplete || payload.executionId) {\n            toolMessage.agentMetadata = toolMessage.agentMetadata ?? {\n              executionId: agentCtxComplete?.executionId ?? payload.executionId,\n              iteration: agentCtxComplete?.iteration ?? payload.iteration,\n            };\n          }\n          emitMessage(toolMessage);\n          const callKey = getToolCallKey(payload);\n          if (callKey) {\n            toolContext.byCall.delete(callKey);\n          }\n        } else if (payloadType === \"await\" && payload.toolName) {\n          // LOCAL tool pause. Two wire shapes resolve here, by dispatch target:\n          //  - FLOW dispatch → `step_await` + `awaitReason: \"local_tool_required\"`\n          //    (Runtype's prompt step throws LocalToolRequiredError when the model\n          //    calls a `toolType: \"local\"` tool).\n          //  - AGENT dispatch → `agent_await` (the agent runtime's native pause).\n          // Either way the server emits the tool name, params, and execution id;\n          // the execution pauses until the client POSTs /resume with toolOutputs.\n          // `agent_await` carries a BARE tool name plus an `origin`; page tools\n          // (origin \"webmcp\") are normalized to the `webmcp:`-prefixed form below\n          // so the bridge + session.ts `/resume` keying are identical for both.\n          //\n          // Upsert a fully-populated tool-variant message so the existing\n          // ask_user_question bubble + sheet paths fire. Mark the message with\n          // `awaitingLocalTool: true` so the UI knows to resolve via\n          // resumeFlow rather than the legacy sendMessage fallback.\n          //\n          // Key the message by the per-call `toolCallId` (provider `toolu_…`;\n          // core#3878) when present. Two PARALLEL calls to the SAME tool in one\n          // turn collapse to an identical `toolId` (`runtime_webmcp:<name>_<ms>`)\n          // and `index: 0`: only `toolCallId` distinguishes them. Keying on it\n          // (a) keeps the two awaits as DISTINCT messages with their own args\n          // instead of the second clobbering the first, and (b) merges each\n          // await into the matching `tool_start` bubble (also keyed by\n          // `toolCallId`). Fall back to the collapsed `toolId` for legacy\n          // servers that don't emit `toolCallId`.\n          const toolCallId: string | undefined =\n            typeof payload.toolCallId === \"string\" && payload.toolCallId.length > 0\n              ? (payload.toolCallId as string)\n              : undefined;\n          const toolId =\n            toolCallId ?? (payload.toolId as string) ?? `local-${nextSequence()}`;\n          const toolMessage = ensureToolMessage(toolId);\n          const rawToolName = payload.toolName as string;\n          // `agent_await` page tools arrive with a bare name; synthesize the\n          // `webmcp:` prefix so isWebMcpToolName (and the bridge's prefix-strip on\n          // resume) treat them identically to a flow `step_await`.\n          const toolName =\n            payload.origin === \"webmcp\" &&\n            !isWebMcpToolName(rawToolName)\n              ? `webmcp:${rawToolName}`\n              : rawToolName;\n          const webMcpTool = isWebMcpToolName(toolName);\n          const tool = toolMessage.toolCall ?? { id: toolId, status: \"pending\" as const };\n          tool.name = toolName;\n          tool.args = payload.parameters;\n          // WebMCP tools are executed asynchronously by the browser AFTER this\n          // `step_await` arrives. Keep them running until session.ts resolves\n          // the page tool and records its actual elapsed time. Other local\n          // tools (for example ask_user_question) keep the existing complete\n          // state because they are waiting for a user interaction, not an\n          // automatic page-tool execution.\n          tool.status = webMcpTool ? \"running\" : \"complete\";\n          tool.chunks = tool.chunks ?? [];\n          tool.startedAt =\n            tool.startedAt ??\n            resolveTimestamp(payload.startedAt ?? payload.timestamp ?? payload.awaitedAt);\n          if (webMcpTool) {\n            tool.completedAt = undefined;\n            tool.duration = undefined;\n            tool.durationMs = undefined;\n          } else {\n            tool.completedAt = tool.completedAt ?? tool.startedAt;\n          }\n          toolMessage.toolCall = tool;\n          toolMessage.streaming = false;\n          toolMessage.agentMetadata = {\n            ...toolMessage.agentMetadata,\n            executionId: (payload.executionId as string) ?? toolMessage.agentMetadata?.executionId,\n            awaitingLocalTool: true,\n            // Only set when the server emitted a real per-call id; its presence\n            // is what tells session.ts to batch + key `/resume` by id rather\n            // than by tool name (which can't represent two same-tool calls).\n            ...(toolCallId ? { webMcpToolCallId: toolCallId } : {}),\n          };\n          emitMessage(toolMessage);\n        } else if (payloadType === \"text_start\") {\n          // Nested flow-as-tool text (PR #4602): a `parentToolCallId` means this\n          // block belongs to a flow running as that tool — record the mapping and\n          // leave the top-level assistant bubble untouched (the nested deltas route\n          // into the parent tool's row).\n          const startBlockId = typeof payload.id === \"string\" ? payload.id : null;\n          const startParent =\n            typeof payload.parentToolCallId === \"string\" && payload.parentToolCallId\n              ? payload.parentToolCallId\n              : null;\n          if (startBlockId && startParent) {\n            nestedBlockParent.set(startBlockId, startParent);\n            continue;\n          }\n          // Unified text-channel block open. A new block id means a new bubble, so\n          // seal any open assistant bubble; the next text_delta creates a fresh one\n          // (lazily). The API emits a fresh block at every tool/media/approval/await\n          // boundary, so block-id keying drives segmentation — no partId.\n          const prev = assistantMessage as AgentWidgetMessage | null;\n          if (prev) {\n            // Normally text_complete already sealed the prior block; this is the\n            // defensive path if a producer opens a new block without closing.\n            if (executionKind === \"flow\") {\n              finalizeFlowTextBlock(prev);\n              lastSealedFlowBubble = prev;\n            } else {\n              prev.streaming = false;\n              emitMessage(prev);\n            }\n            assistantMessage = null;\n          }\n          currentTextBlockId =\n            typeof payload.id === \"string\" ? payload.id : currentTextBlockId;\n          pendingFlowRaw = \"\";\n        } else if (payloadType === \"text_delta\") {\n          // Nested flow-as-tool text: route to the parent tool's row, through the\n          // same structured-content parser, never the top-level assistant channel.\n          const deltaBlockId = typeof payload.id === \"string\" ? payload.id : null;\n          const nestedParent = deltaBlockId\n            ? nestedBlockParent.get(deltaBlockId)\n            : undefined;\n          if (deltaBlockId && nestedParent) {\n            const nestedDelta =\n              typeof payload.delta === \"string\" ? payload.delta : \"\";\n            const nestedRaw = (nestedBlockRaw.get(deltaBlockId) ?? \"\") + nestedDelta;\n            nestedBlockRaw.set(deltaBlockId, nestedRaw);\n            if (nestedRaw.trim() === \"\") continue;\n            const nested = ensureNestedBlockMessage(deltaBlockId, nestedParent);\n            nested.agentMetadata = {\n              ...nested.agentMetadata,\n              executionId: payload.executionId,\n              parentToolId: nestedParent,\n            };\n            applyFlowTextChunk(nested, nestedRaw, nestedDelta, undefined);\n            continue;\n          }\n          currentTextBlockId =\n            typeof payload.id === \"string\" ? payload.id : currentTextBlockId;\n          if (executionKind === \"flow\") {\n            // Flow prompt-step text can be structured JSON: accumulate the raw\n            // block and run it through the structured-content parser, keyed by the\n            // block-id bubble. Materialize lazily so a whitespace-only block\n            // (newlines around a tool boundary) never leaves a stray bubble.\n            const delta = typeof payload.delta === \"string\" ? payload.delta : \"\";\n            pendingFlowRaw += delta;\n            if (pendingFlowRaw.trim() === \"\") continue;\n            const assistant = ensureAssistantMessage();\n            assistant.agentMetadata = {\n              executionId: payload.executionId,\n              iteration: payload.iteration,\n            };\n            applyFlowTextChunk(assistant, pendingFlowRaw, delta, undefined);\n            lastAssistantInTurn = assistant;\n            continue;\n          }\n          const assistant = ensureAssistantMessage();\n          assistant.content += payload.delta ?? '';\n          assistant.agentMetadata = {\n            executionId: payload.executionId,\n            iteration: payload.iteration,\n            turnId: openTurnId ?? undefined,\n            agentName: agentExecution?.agentName\n          };\n          lastAssistantInTurn = assistant;\n          emitMessage(assistant);\n        } else if (payloadType === \"text_complete\") {\n          // Nested flow-as-tool text block close: seal its parent-tool-row message.\n          const completeBlockId = typeof payload.id === \"string\" ? payload.id : null;\n          if (completeBlockId && nestedBlockParent.has(completeBlockId)) {\n            const nested = nestedBlockMessages.get(completeBlockId);\n            if (nested) finalizeFlowTextBlock(nested);\n            nestedBlockParent.delete(completeBlockId);\n            nestedBlockRaw.delete(completeBlockId);\n            nestedBlockMessages.delete(completeBlockId);\n            continue;\n          }\n          // Seal the current text block's bubble.\n          const prev = assistantMessage as AgentWidgetMessage | null;\n          if (prev) {\n            if (executionKind === \"flow\") {\n              // Final structured extraction off the accumulated raw buffer; the\n              // authoritative step_complete.result.response reconciles next.\n              finalizeFlowTextBlock(prev);\n              lastSealedFlowBubble = prev;\n            } else {\n              // U2: text_complete carries the assembled text, but the bubble already\n              // holds it from the deltas — only fall back to payload.text when no\n              // delta content was seen, never double-count.\n              if ((prev.content ?? \"\") === \"\" && typeof payload.text === \"string\") {\n                prev.content = payload.text;\n              }\n              prev.streaming = false;\n              emitMessage(prev);\n            }\n            assistantMessage = null;\n          }\n          currentTextBlockId = null;\n          pendingFlowRaw = \"\";\n        } else if (payloadType === \"step_complete\") {\n          // Only process completions for prompt steps, not tool/context steps\n          const stepType = (payload as any).stepType;\n          const executionType = (payload as any).executionType;\n          if (stepType === \"tool\" || executionType === \"context\") {\n            // Skip tool-related completions - they're handled by tool_complete\n            continue;\n          }\n\n          // A failed step (`success:false`) — including the legacy `step_error`\n          // event, which the wire encoder folds into a failed `step_complete`\n          // — surfaces as a terminal error and finalizes the stream.\n          if (payload.success === false) {\n            const e = payload.error;\n            const message =\n              typeof e === \"string\" && e !== \"\"\n                ? e\n                : e != null && typeof e === \"object\" && \"message\" in e\n                  ? String((e as { message?: unknown }).message ?? \"Step failed\")\n                  : \"Step failed\";\n            onEvent({ type: \"error\", error: new Error(message) });\n            const finalMsg = assistantMessage as AgentWidgetMessage | null;\n            if (finalMsg && finalMsg.streaming) {\n              finalMsg.streaming = false;\n              emitMessage(finalMsg);\n            }\n            onEvent({ type: \"status\", status: \"idle\" });\n            continue;\n          }\n\n          // Unified flow: reconcile the just-sealed text block with the\n          // authoritative structured final (`result.response`). Displayed content\n          // stays as streamed — a multi-segment step keeps each bubble's own text;\n          // reconcile only fills/repairs the last sealed block and sets rawContent.\n          // A pure-tool / text-less step (no sealed flow bubble) completes silently.\n          {\n            const sealed = lastSealedFlowBubble;\n            lastSealedFlowBubble = null;\n            const flowStopReason = (payload as any).stopReason as\n              | StopReasonKind\n              | undefined;\n            const finalResponse = payload.result?.response;\n            if (sealed) {\n              if (flowStopReason) sealed.stopReason = flowStopReason;\n              if (finalResponse !== undefined && finalResponse !== null) {\n                reconcileSealedAssistantWithFinalResponse(sealed, finalResponse);\n              } else if (sealed.streaming !== false) {\n                streamParsers.delete(sealed.id);\n                rawContentBuffers.delete(sealed.id);\n                sealed.streaming = false;\n                emitMessage(sealed);\n              }\n            } else {\n              // Buffered / dispatch-mode step: no streamed text block, but the step\n              // carries the final response (and/or a stopReason) — render it as the\n              // assistant message. An empty response + stopReason still surfaces a\n              // sealed bubble so the UI can show an affordance.\n              const hasResponse =\n                finalResponse !== undefined &&\n                finalResponse !== null &&\n                finalResponse !== \"\";\n              if (hasResponse || flowStopReason) {\n                const assistant = ensureAssistantMessage();\n                if (flowStopReason) assistant.stopReason = flowStopReason;\n                if (hasResponse) {\n                  finalizeFlowTextBlock(assistant, finalResponse);\n                } else {\n                  assistant.streaming = false;\n                  emitMessage(assistant);\n                }\n              }\n            }\n            continue;\n          }\n        // ================================================================\n        // Agent Loop Execution Events\n        // ================================================================\n        } else if (payloadType === \"execution_start\") {\n          executionKind = payload.kind === \"flow\" ? \"flow\" : \"agent\";\n          executionKindResolved = true;\n          if (executionKind === \"agent\") {\n            agentExecution = {\n              executionId: payload.executionId,\n              agentId: payload.agentId ?? 'virtual',\n              agentName: payload.agentName ?? '',\n              status: 'running',\n              currentIteration: 0,\n              maxTurns: payload.maxTurns ?? 1,\n              startedAt: resolveTimestamp(payload.startedAt)\n            };\n          }\n        } else if (payloadType === \"turn_start\") {\n          // Unified collapsed `agent_iteration_*` into a denormalized `iteration`\n          // field on the turn (merged spec §2). Reconstruct the per-iteration\n          // message boundary the 'separate' renderer keys off: when the iteration\n          // advances, seal the previous iteration's bubble and rotate to a new one.\n          const iteration =\n            typeof payload.iteration === \"number\" ? payload.iteration : lastIterationSeen;\n          if (iteration !== lastIterationSeen) {\n            if (agentExecution) agentExecution.currentIteration = iteration;\n            if (iterationDisplay === 'separate' && iteration > 1) {\n              const prevMsg = assistantMessage as AgentWidgetMessage | null;\n              if (prevMsg) {\n                prevMsg.streaming = false;\n                emitMessage(prevMsg);\n                agentIterationMessages.set(iteration - 1, prevMsg);\n                assistantMessage = null;\n              }\n            }\n            lastIterationSeen = iteration;\n          }\n          openTurnId = typeof payload.id === \"string\" ? payload.id : null;\n          // Reset the per-turn assistant tracker. lastAssistantInTurn is used by\n          // turn_complete to attach stopReason to the final text segment of the\n          // turn even if that segment was sealed by an intervening tool boundary.\n          lastAssistantInTurn = null;\n        } else if (payloadType === \"tool_input_delta\") {\n          // Streamed tool arguments (display-only; authoritative args ride\n          // tool_input_complete / tool_start).\n          const toolId = payload.toolCallId ?? toolContext.lastId;\n          if (toolId) {\n            const toolMessage = toolMessages.get(toolId);\n            if (toolMessage?.toolCall) {\n              toolMessage.toolCall.chunks = toolMessage.toolCall.chunks ?? [];\n              toolMessage.toolCall.chunks.push(payload.delta ?? '');\n              emitMessage(toolMessage);\n            }\n          }\n        } else if (payloadType === \"tool_input_complete\") {\n          // Authoritative args are set at tool_start; nothing to render here.\n          continue;\n        } else if (payloadType === \"turn_complete\") {\n          // Reasoning is sealed by its own reasoning_complete on the wire\n          // vocabulary; this only attaches the turn-level stopReason to the\n          // assistant message produced by this turn. Falls back to\n          // lastAssistantInTurn when the bubble was sealed at a tool boundary\n          // mid-turn, so the notice still attaches to the final visible segment.\n          const turnStopReason = (payload as any).stopReason as\n            | StopReasonKind\n            | undefined;\n          const stopReasonTarget = assistantMessage ?? lastAssistantInTurn;\n          if (turnStopReason && stopReasonTarget !== null) {\n            const turnId = payload.id;\n            const matchesTurn =\n              !turnId || stopReasonTarget.agentMetadata?.turnId === turnId;\n            if (matchesTurn) {\n              stopReasonTarget.stopReason = turnStopReason;\n              emitMessage(stopReasonTarget);\n            }\n          }\n          if (openTurnId === payload.id) openTurnId = null;\n        } else if (payloadType === \"media_start\") {\n          // Open a media block; buffer fragments until media_complete.\n          const id = String(payload.id);\n          mediaBuffers.set(id, {\n            mediaType: typeof payload.mediaType === \"string\" ? payload.mediaType : undefined,\n            role: typeof payload.role === \"string\" ? payload.role : undefined,\n            toolCallId: payload.toolCallId,\n            parts: [],\n          });\n        } else if (payloadType === \"media_delta\") {\n          const buf = mediaBuffers.get(String(payload.id));\n          if (buf && typeof payload.delta === \"string\") buf.parts.push(payload.delta);\n        } else if (payloadType === \"media_complete\") {\n          // Reassemble the buffered media triad into a single AI SDK–aligned\n          // `MediaContentPart`, then render it as a synthetic assistant message\n          // inserted between the tool bubble and the next text turn:\n          //   { type: 'media', data, mediaType }                // AI SDK v6: base64\n          //   { type: 'image-url', url, mediaType? }            // AI SDK v3/v4\n          //   { type: 'file-url', url, mediaType }              // AI SDK v3/v4\n          const mediaBlockId = String(payload.id);\n          const buf = mediaBuffers.get(mediaBlockId);\n          mediaBuffers.delete(mediaBlockId);\n          const completeMediaType =\n            (typeof payload.mediaType === \"string\" ? payload.mediaType : undefined) ??\n            buf?.mediaType ??\n            \"application/octet-stream\";\n          const completeData = typeof payload.data === \"string\" ? payload.data : undefined;\n          const completeUrl =\n            typeof payload.url === \"string\"\n              ? payload.url\n              : buf && buf.parts.length > 0\n                ? buf.parts.join(\"\")\n                : undefined;\n          let reconstructed: Record<string, unknown> | null = null;\n          if (completeData) {\n            reconstructed = { type: \"media\", data: completeData, mediaType: completeMediaType };\n          } else if (completeUrl) {\n            // The wire is mediaType-only; a URL part with no declared MIME\n            // arrives as the bare bucket hint \"image\" (per the API encoder). Treat\n            // that — and any real `image/*` — as a hosted image so we don't misroute\n            // generated images into the file bucket.\n            const lower = completeMediaType.toLowerCase();\n            const isImage = lower === \"image\" || lower.startsWith(\"image/\");\n            reconstructed = {\n              type: isImage ? \"image-url\" : \"file-url\",\n              url: completeUrl,\n              mediaType: completeMediaType,\n            };\n          }\n          const mediaToolCallId = payload.toolCallId ?? buf?.toolCallId;\n          const rawMedia = reconstructed ? [reconstructed] : [];\n          const mediaContentParts: ContentPart[] = [];\n          for (const part of rawMedia) {\n            if (!part || typeof part !== \"object\") continue;\n            const rec = part as Record<string, unknown>;\n            const partType = typeof rec.type === \"string\" ? rec.type : undefined;\n\n            // Resolve `(src, mediaType)` for the part.\n            // RFC 7231 says MIME types are case-insensitive, so we canonicalize\n            // to lowercase once here. That makes the `startsWith(\"image/\")` /\n            // `\"audio/\"` / `\"video/\"` bucket checks robust to upstream tools\n            // that emit non-canonical casing like `Image/PNG`.\n            const rawMediaType =\n              typeof rec.mediaType === \"string\" ? rec.mediaType.toLowerCase() : \"\";\n            let src: string | null = null;\n            let mediaType = \"\";\n            if (partType === \"media\") {\n              const data = typeof rec.data === \"string\" ? rec.data : undefined;\n              if (!data) continue;\n              // Empty/missing mediaType yields `data:;base64,...` which RFC 2397\n              // resolves to `text/plain`: stamp a default so the data URI is\n              // well-formed and the part lands in the file bucket.\n              mediaType = rawMediaType.length > 0 ? rawMediaType : \"application/octet-stream\";\n              src = `data:${mediaType};base64,${data}`;\n            } else if (partType === \"image-url\") {\n              const url = typeof rec.url === \"string\" ? rec.url : undefined;\n              if (!url) continue;\n              mediaType = rawMediaType;\n              src = url;\n            } else if (partType === \"file-url\") {\n              const url = typeof rec.url === \"string\" ? rec.url : undefined;\n              if (!url) continue;\n              mediaType = rawMediaType;\n              src = url;\n            } else {\n              continue;\n            }\n            if (!src) continue;\n\n            // Pick the right rendering bucket based on mediaType.\n            if (partType === \"image-url\" || mediaType.startsWith(\"image/\")) {\n              mediaContentParts.push({\n                type: \"image\",\n                image: src,\n                // Only a real MIME (`image/png`) is a usable mimeType; the bare\n                // bucket hint \"image\" (a hosted URL with no declared type) is not.\n                ...(mediaType.includes(\"/\") ? { mimeType: mediaType } : {}),\n              });\n            } else if (mediaType.startsWith(\"audio/\")) {\n              mediaContentParts.push({\n                type: \"audio\",\n                audio: src,\n                mimeType: mediaType,\n              });\n            } else if (mediaType.startsWith(\"video/\")) {\n              mediaContentParts.push({\n                type: \"video\",\n                video: src,\n                mimeType: mediaType,\n              });\n            } else {\n              const resolvedMediaType = mediaType || \"application/octet-stream\";\n              mediaContentParts.push({\n                type: \"file\",\n                data: src,\n                mimeType: resolvedMediaType,\n                filename: filenameFromMediaType(resolvedMediaType),\n              });\n            }\n          }\n\n          if (mediaContentParts.length > 0) {\n            // Uniquify per emission. A tool may emit multiple `agent_media`\n            // events for the same `toolCallId` (e.g. streamed/batched media);\n            // sharing an id would let `emitMessage` merge them by id and\n            // overwrite the prior `contentParts`.\n            const seq = nextSequence();\n            const toolCallIdRaw = mediaToolCallId;\n            const mediaIdSuffix =\n              typeof toolCallIdRaw === \"string\" && toolCallIdRaw.length > 0\n                ? `${toolCallIdRaw}-${seq}`\n                : String(seq);\n            const mediaMessage: AgentWidgetMessage = {\n              id: `agent-media-${mediaIdSuffix}`,\n              role: \"assistant\",\n              content: \"\",\n              contentParts: mediaContentParts,\n              createdAt: new Date().toISOString(),\n              streaming: false,\n              sequence: seq,\n              agentMetadata: {\n                executionId: payload.executionId,\n                // Media blocks carry no iteration of their own; stamp the\n                // enclosing iteration tracked from turn/tool frames.\n                iteration:\n                  typeof payload.iteration === \"number\"\n                    ? payload.iteration\n                    : lastIterationSeen,\n              },\n            };\n            emitMessage(mediaMessage);\n\n            // Seal any in-flight assistant text bubble before splitting the\n            // stream. Without this, an orphan bubble retains `streaming: true`\n            // forever: `agent_complete` only finalizes the latest\n            // `assistantMessage`, so the typing/caret indicator would stay on\n            // the prior bubble even though no more deltas will arrive.\n            const prevAssistant = assistantMessage as AgentWidgetMessage | null;\n            if (prevAssistant) {\n              prevAssistant.streaming = false;\n              emitMessage(prevAssistant);\n            }\n            assistantMessage = null;\n            assistantMessageRef.current = null;\n          }\n        } else if (payloadType === \"execution_complete\") {\n          const kind = payload.kind ?? executionKind;\n          if (kind === \"agent\" && agentExecution) {\n            agentExecution.status = payload.success ? 'complete' : 'error';\n            agentExecution.completedAt = resolveTimestamp(payload.completedAt);\n            agentExecution.stopReason = payload.stopReason;\n          }\n\n          // Finalize any still-open assistant message. Per-step reconciliation\n          // (step_complete.result.response) normally sealed the flow blocks\n          // already; this is the defensive close for an unterminated block, and\n          // for flow it runs the final structured extraction off the raw buffer.\n          const finalMsg = assistantMessage as AgentWidgetMessage | null;\n          if (finalMsg) {\n            if (kind === \"flow\" && finalMsg.streaming !== false) {\n              finalizeFlowTextBlock(finalMsg);\n            } else {\n              finalMsg.streaming = false;\n              emitMessage(finalMsg);\n            }\n            assistantMessage = null;\n          }\n          currentTextBlockId = null;\n          pendingFlowRaw = \"\";\n          lastSealedFlowBubble = null;\n\n          onEvent({ type: \"status\", status: \"idle\" });\n        } else if (payloadType === \"execution_error\") {\n          // Terminal failure. The non-terminal `error` is handled\n          // separately (recoverable → warn).\n          const errorMessage = typeof payload.error === 'string'\n            ? payload.error\n            : payload.error?.message ?? 'Execution error';\n          onEvent({\n            type: \"error\",\n            error: new Error(errorMessage)\n          });\n        } else if (payloadType === \"ping\") {\n          // Keep-alive heartbeat - no action needed\n        // ================================================================\n        // Tool Approval Events\n        // ================================================================\n        } else if (payloadType === \"approval_start\") {\n          const approvalId = payload.approvalId ?? `approval-${nextSequence()}`;\n          const approvalMessage: AgentWidgetMessage = {\n            id: `approval-${approvalId}`,\n            role: \"assistant\",\n            content: \"\",\n            createdAt: new Date().toISOString(),\n            streaming: false,\n            variant: \"approval\",\n            sequence: nextSequence(),\n            approval: {\n              id: approvalId,\n              status: \"pending\",\n              agentId: agentExecution?.agentId ?? 'virtual',\n              executionId: payload.executionId ?? agentExecution?.executionId ?? '',\n              toolName: payload.toolName ?? '',\n              toolType: payload.toolType,\n              description: payload.description ?? `Execute ${payload.toolName ?? 'tool'}`,\n              ...(typeof payload.reason === \"string\" && payload.reason\n                ? { reason: payload.reason }\n                : {}),\n              parameters: payload.parameters,\n            },\n          };\n          emitMessage(approvalMessage);\n        } else if (payloadType === \"step_await\" && payload.awaitReason === \"approval_required\") {\n          const approvalId = payload.approvalId ?? `approval-${nextSequence()}`;\n          const approvalMessage: AgentWidgetMessage = {\n            id: `approval-${approvalId}`,\n            role: \"assistant\",\n            content: \"\",\n            createdAt: new Date().toISOString(),\n            streaming: false,\n            variant: \"approval\",\n            sequence: nextSequence(),\n            approval: {\n              id: approvalId,\n              status: \"pending\",\n              agentId: agentExecution?.agentId ?? 'virtual',\n              executionId: payload.executionId ?? agentExecution?.executionId ?? '',\n              toolName: payload.toolName ?? '',\n              toolType: payload.toolType,\n              description: payload.description ?? `Execute ${payload.toolName ?? 'tool'}`,\n              ...(typeof payload.reason === \"string\" && payload.reason\n                ? { reason: payload.reason }\n                : {}),\n              parameters: payload.parameters,\n            },\n          };\n          emitMessage(approvalMessage);\n        } else if (payloadType === \"approval_complete\") {\n          const approvalId = payload.approvalId;\n          if (approvalId) {\n            // Find and update the existing approval message\n            const approvalMessageId = `approval-${approvalId}`;\n            const existingMessage: AgentWidgetMessage = {\n              id: approvalMessageId,\n              role: \"assistant\",\n              content: \"\",\n              createdAt: new Date().toISOString(),\n              streaming: false,\n              variant: \"approval\",\n              sequence: nextSequence(),\n              approval: {\n                id: approvalId,\n                status: (payload.decision as \"approved\" | \"denied\") ?? \"approved\",\n                agentId: agentExecution?.agentId ?? 'virtual',\n                executionId: payload.executionId ?? agentExecution?.executionId ?? '',\n                toolName: payload.toolName ?? '',\n                description: payload.description ?? '',\n                resolvedAt: Date.now(),\n              },\n            };\n            emitMessage(existingMessage);\n          }\n        } else if (\n          payloadType === \"artifact_start\" ||\n          payloadType === \"artifact_delta\" ||\n          payloadType === \"artifact_update\" ||\n          payloadType === \"artifact_complete\"\n        ) {\n          if (payloadType === \"artifact_start\") {\n            const at = payload.artifactType as PersonaArtifactKind;\n            const artId = String(payload.id);\n            const artTitle = typeof payload.title === \"string\" ? payload.title : undefined;\n            onEvent({\n              type: \"artifact_start\",\n              id: artId,\n              artifactType: at,\n              title: artTitle,\n              component: typeof payload.component === \"string\" ? payload.component : undefined\n            });\n            artifactContent.set(artId, { markdown: \"\", title: artTitle });\n            // Insert inline artifact reference card (skip if already present from transcript_insert)\n            if (!artifactIdsWithCards.has(artId)) {\n              artifactIdsWithCards.add(artId);\n              const cardMsg: AgentWidgetMessage = {\n                id: `artifact-ref-${artId}`,\n                role: \"assistant\",\n                content: \"\",\n                createdAt: new Date().toISOString(),\n                streaming: true,\n                sequence: nextSequence(),\n                rawContent: JSON.stringify({\n                  component: \"PersonaArtifactCard\",\n                  props: { artifactId: artId, title: artTitle, artifactType: at, status: \"streaming\" },\n                }),\n              };\n              artifactCardMessages.set(artId, cardMsg);\n              emitMessage(cardMsg);\n            }\n          } else if (payloadType === \"artifact_delta\") {\n            const deltaId = String(payload.id);\n            const deltaText = typeof payload.delta === \"string\" ? payload.delta : String(payload.delta ?? \"\");\n            onEvent({\n              type: \"artifact_delta\",\n              id: deltaId,\n              artDelta: deltaText\n            });\n            const acc = artifactContent.get(deltaId);\n            if (acc) acc.markdown += deltaText;\n          } else if (payloadType === \"artifact_update\") {\n            const props =\n              payload.props && typeof payload.props === \"object\" && !Array.isArray(payload.props)\n                ? (payload.props as Record<string, unknown>)\n                : {};\n            onEvent({\n              type: \"artifact_update\",\n              id: String(payload.id),\n              props,\n              component: typeof payload.component === \"string\" ? payload.component : undefined\n            });\n          } else if (payloadType === \"artifact_complete\") {\n            const artCompleteId = String(payload.id);\n            onEvent({ type: \"artifact_complete\", id: artCompleteId });\n            // Update the inline card to show completed state\n            const refMsg = artifactCardMessages.get(artCompleteId);\n            if (refMsg) {\n              refMsg.streaming = false;\n              try {\n                const parsed = JSON.parse(refMsg.rawContent ?? \"{}\");\n                if (parsed.props) {\n                  parsed.props.status = \"complete\";\n                  // Store markdown content in card props so download works after page refresh\n                  const acc = artifactContent.get(artCompleteId);\n                  if (acc?.markdown) {\n                    parsed.props.markdown = acc.markdown;\n                  }\n                }\n                refMsg.rawContent = JSON.stringify(parsed);\n              } catch { /* ignore parse errors */ }\n              artifactContent.delete(artCompleteId);\n              emitMessage(refMsg);\n              artifactCardMessages.delete(artCompleteId);\n            }\n          }\n        } else if (payloadType === \"transcript_insert\") {\n          const m = payload.message as Record<string, unknown> | undefined;\n          if (!m || typeof m !== \"object\") {\n            continue;\n          }\n          const id = String(m.id ?? `msg-${nextSequence()}`);\n          const roleRaw = m.role;\n          const role =\n            roleRaw === \"user\" ? \"user\" : roleRaw === \"system\" ? \"system\" : \"assistant\";\n          const msg: AgentWidgetMessage = {\n            id,\n            role,\n            content: typeof m.content === \"string\" ? m.content : \"\",\n            rawContent: typeof m.rawContent === \"string\" ? m.rawContent : undefined,\n            createdAt:\n              typeof m.createdAt === \"string\" ? m.createdAt : new Date().toISOString(),\n            streaming: m.streaming === true,\n            // Omit variant unless the stream specifies it. Do not default to `\"assistant\"`:\n            // that value is truthy and skips the component-directive branch (`!message.variant` in ui.ts).\n            ...(typeof m.variant === \"string\"\n              ? { variant: m.variant as AgentWidgetMessage[\"variant\"] }\n              : {}),\n            sequence: nextSequence()\n          };\n          emitMessage(msg);\n          // Detect artifact references in transcript_insert to prevent duplicate auto-cards\n          if (msg.rawContent) {\n            try {\n              const parsed = JSON.parse(msg.rawContent);\n              const refArtId = parsed?.props?.artifactId;\n              if (typeof refArtId === \"string\") {\n                artifactIdsWithCards.add(refArtId);\n              }\n            } catch { /* not JSON or no artifactId */ }\n          }\n          assistantMessage = null;\n          assistantMessageRef.current = null;\n          streamParsers.delete(id);\n          rawContentBuffers.delete(id);\n        } else if (payloadType === \"error\") {\n          // Unified non-terminal error (merged spec). A bare `error` is\n          // recoverable by default — a transient notice such as \"rate limited,\n          // retrying\" — and the execution continues, so it must NOT surface as a\n          // fatal error or finalize the stream. The API routes terminal failures\n          // through `execution_error`. Only an explicit `recoverable: false`\n          // promotes an `error` to terminal.\n          if (\n            payload.recoverable === false &&\n            payload.error != null &&\n            payload.error !== \"\"\n          ) {\n            const errorMessage =\n              typeof payload.error === \"string\"\n                ? payload.error\n                : (payload.error as { message?: unknown })?.message != null\n                  ? String((payload.error as { message?: unknown }).message)\n                  : \"Execution error\";\n            onEvent({ type: \"error\", error: new Error(errorMessage) });\n            const finalMsg = assistantMessage as AgentWidgetMessage | null;\n            if (finalMsg && finalMsg.streaming) {\n              finalMsg.streaming = false;\n              emitMessage(finalMsg);\n            }\n            onEvent({ type: \"status\", status: \"idle\" });\n          }\n        } else if (\n          payloadType === \"step_error\" ||\n          payloadType === \"dispatch_error\" ||\n          payloadType === \"flow_error\"\n        ) {\n          let resolvedError: Error | null = null;\n          if (payload.error instanceof Error) {\n            resolvedError = payload.error;\n          } else if (payloadType === \"dispatch_error\") {\n            const msg = payload.message ?? payload.error;\n            if (msg != null && msg !== \"\") {\n              resolvedError = new Error(String(msg));\n            }\n          } else {\n            const e = payload.error;\n            if (typeof e === \"string\" && e !== \"\") {\n              resolvedError = new Error(e);\n            } else if (e != null && typeof e === \"object\" && \"message\" in e) {\n              resolvedError = new Error(String((e as { message?: unknown }).message ?? e));\n            }\n          }\n\n          if (resolvedError) {\n            onEvent({ type: \"error\", error: resolvedError });\n            const finalMsg = assistantMessage as AgentWidgetMessage | null;\n            if (finalMsg && finalMsg.streaming) {\n              finalMsg.streaming = false;\n              emitMessage(finalMsg);\n            }\n            onEvent({ type: \"status\", status: \"idle\" });\n          }\n        }\n      }\n      seqReadyQueue.length = 0;\n    };\n\n    // eslint-disable-next-line no-constant-condition\n    while (true) {\n      const { done, value } = await reader.read();\n      if (done) break;\n\n      buffer += decoder.decode(value, { stream: true });\n      const events = buffer.split(\"\\n\\n\");\n      buffer = events.pop() ?? \"\";\n\n      for (const event of events) {\n        const lines = event.split(\"\\n\");\n        let eventType = \"message\";\n        let data = \"\";\n\n        for (const line of lines) {\n          if (line.startsWith(\"event:\")) {\n            eventType = line.replace(\"event:\", \"\").trim();\n          } else if (line.startsWith(\"data:\")) {\n            data += line.replace(\"data:\", \"\").trim();\n          }\n        }\n\n        if (!data) continue;\n        let payload: any;\n        try {\n          payload = JSON.parse(data);\n        } catch (error) {\n          onEvent({\n            type: \"error\",\n            error:\n              error instanceof Error\n                ? error\n                : new Error(\"Failed to parse chat stream payload\")\n          });\n          continue;\n        }\n\n        const payloadType =\n          eventType !== \"message\" ? eventType : payload.type ?? \"message\";\n\n        // Tap: capture raw SSE event for event stream inspector\n        this.onSSEEvent?.(payloadType, payload);\n\n        // If custom SSE event parser is provided, try it first\n        if (this.parseSSEEvent) {\n          // Keep assistant message ref in sync\n          assistantMessageRef.current = assistantMessage;\n          const handled = await this.handleCustomSSEEvent(\n            payload,\n            onEvent,\n            assistantMessageRef,\n            emitMessage,\n            nextSequence,\n            customParsePartId\n          );\n          // Update assistantMessage from ref (in case it was created or replaced by partId segmentation)\n          if (assistantMessageRef.current && assistantMessageRef.current !== assistantMessage) {\n            assistantMessage = assistantMessageRef.current;\n          }\n          if (handled) continue; // Skip default handling if custom handler processed it\n        }\n\n        // The wire is the wire vocabulary; the handler consumes it\n        // natively. The stream is single-connection and in order, so each frame\n        // drains straight through.\n        seqReadyQueue.push({ payloadType, payload });\n        drainReadyQueue();\n      }\n    }\n\n    drainReadyQueue();\n  }\n}\n","/**\n * Message ID utilities for client-side message tracking\n * Used for feedback integration with the Runtype API\n */\n\n/**\n * Generate a unique message ID for tracking\n * Format: msg_{timestamp_base36}_{random_8chars}\n */\nexport function generateMessageId(): string {\n  const timestamp = Date.now().toString(36);\n  const random = Math.random().toString(36).substring(2, 10);\n  return `msg_${timestamp}_${random}`;\n}\n\n/**\n * Generate a unique user message ID\n * Format: usr_{timestamp_base36}_{random_8chars}\n */\nexport function generateUserMessageId(): string {\n  const timestamp = Date.now().toString(36);\n  const random = Math.random().toString(36).substring(2, 10);\n  return `usr_${timestamp}_${random}`;\n}\n\n/**\n * Generate a unique assistant message ID\n * Format: ast_{timestamp_base36}_{random_8chars}\n */\nexport function generateAssistantMessageId(): string {\n  const timestamp = Date.now().toString(36);\n  const random = Math.random().toString(36).substring(2, 10);\n  return `ast_${timestamp}_${random}`;\n}\n\n\n\n","/**\n * Content Utilities\n *\n * Helper functions for working with multi-modal message content.\n */\n\nimport type { MessageContent, ContentPart, TextContentPart, ImageContentPart, FileContentPart } from '../types';\n\n/**\n * Fallback display text for messages that only contain image attachments.\n */\nexport const IMAGE_ONLY_MESSAGE_FALLBACK_TEXT = \"[Image]\";\n\n/**\n * Normalize content to ContentPart[] format.\n * Converts string content to a single text content part.\n */\nexport function normalizeContent(content: MessageContent): ContentPart[] {\n  if (typeof content === 'string') {\n    return [{ type: 'text', text: content }];\n  }\n  return content;\n}\n\n/**\n * Extract display text from content parts.\n * Concatenates all text parts into a single string.\n */\nexport function getDisplayText(content: MessageContent): string {\n  if (typeof content === 'string') {\n    return content;\n  }\n  return content\n    .filter((part): part is TextContentPart => part.type === 'text')\n    .map(part => part.text)\n    .join('');\n}\n\n/**\n * Check if content contains any images.\n */\nexport function hasImages(content: MessageContent): boolean {\n  if (typeof content === 'string') {\n    return false;\n  }\n  return content.some(part => part.type === 'image');\n}\n\n/**\n * Get all image parts from content.\n */\nexport function getImageParts(content: MessageContent): ImageContentPart[] {\n  if (typeof content === 'string') {\n    return [];\n  }\n  return content.filter((part): part is ImageContentPart => part.type === 'image');\n}\n\n/**\n * Create a text-only content part.\n */\nexport function createTextPart(text: string): TextContentPart {\n  return { type: 'text', text };\n}\n\n/**\n * Create an image content part from a base64 data URI or URL.\n *\n * @param image - Base64 data URI (data:image/...) or URL\n * @param options - Optional mimeType and alt text\n */\nexport function createImagePart(\n  image: string,\n  options?: { mimeType?: string; alt?: string }\n): ImageContentPart {\n  return {\n    type: 'image',\n    image,\n    ...(options?.mimeType && { mimeType: options.mimeType }),\n    ...(options?.alt && { alt: options.alt }),\n  };\n}\n\n/**\n * Convert a File object to an image content part.\n * Reads the file and converts it to a base64 data URI.\n */\nexport async function fileToImagePart(file: File): Promise<ImageContentPart> {\n  return new Promise((resolve, reject) => {\n    const reader = new FileReader();\n    reader.onload = () => {\n      const dataUri = reader.result as string;\n      resolve({\n        type: 'image',\n        image: dataUri,\n        mimeType: file.type,\n        alt: file.name,\n      });\n    };\n    reader.onerror = () => reject(new Error('Failed to read file'));\n    reader.readAsDataURL(file);\n  });\n}\n\n/**\n * Validate that a file is an acceptable image type.\n *\n * @param file - The file to validate\n * @param acceptedTypes - Array of accepted MIME types (default: common image types)\n * @param maxSizeBytes - Maximum file size in bytes (default: 10MB)\n */\nexport function validateImageFile(\n  file: File,\n  acceptedTypes: string[] = ['image/png', 'image/jpeg', 'image/gif', 'image/webp'],\n  maxSizeBytes: number = 10 * 1024 * 1024\n): { valid: boolean; error?: string } {\n  if (!acceptedTypes.includes(file.type)) {\n    return {\n      valid: false,\n      error: `Invalid file type. Accepted types: ${acceptedTypes.join(', ')}`,\n    };\n  }\n\n  if (file.size > maxSizeBytes) {\n    const maxSizeMB = Math.round(maxSizeBytes / (1024 * 1024));\n    return {\n      valid: false,\n      error: `File too large. Maximum size: ${maxSizeMB}MB`,\n    };\n  }\n\n  return { valid: true };\n}\n\n// ============================================================================\n// Generic File Utilities (for PDF, TXT, DOCX, etc.)\n// ============================================================================\n\n/**\n * Common image MIME types\n */\nexport const IMAGE_MIME_TYPES = [\n  'image/png',\n  'image/jpeg',\n  'image/gif',\n  'image/webp',\n  'image/svg+xml',\n  'image/bmp',\n];\n\n/**\n * Common document MIME types\n */\nexport const DOCUMENT_MIME_TYPES = [\n  'application/pdf',\n  'text/plain',\n  'text/markdown',\n  'text/csv',\n  'application/msword',\n  'application/vnd.openxmlformats-officedocument.wordprocessingml.document',\n  'application/vnd.ms-excel',\n  'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet',\n  'application/json',\n];\n\n/**\n * All supported file types (images + documents)\n */\nexport const ALL_SUPPORTED_MIME_TYPES = [...IMAGE_MIME_TYPES, ...DOCUMENT_MIME_TYPES];\n\n/**\n * Check if a MIME type is an image\n */\nexport function isImageMimeType(mimeType: string): boolean {\n  return IMAGE_MIME_TYPES.includes(mimeType) || mimeType.startsWith('image/');\n}\n\n/**\n * Check if a file is an image\n */\nexport function isImageFile(file: File): boolean {\n  return isImageMimeType(file.type);\n}\n\n/**\n * Create a file content part from a base64 data URI.\n */\nexport function createFilePart(\n  data: string,\n  mimeType: string,\n  filename: string\n): FileContentPart {\n  return {\n    type: 'file',\n    data,\n    mimeType,\n    filename,\n  };\n}\n\n/**\n * Convert a File object to a content part.\n * Returns ImageContentPart for images, FileContentPart for other files.\n */\nexport async function fileToContentPart(file: File): Promise<ImageContentPart | FileContentPart> {\n  return new Promise((resolve, reject) => {\n    const reader = new FileReader();\n    reader.onload = () => {\n      const dataUri = reader.result as string;\n\n      if (isImageFile(file)) {\n        // Return image content part for images\n        resolve({\n          type: 'image',\n          image: dataUri,\n          mimeType: file.type,\n          alt: file.name,\n        });\n      } else {\n        // Return file content part for documents\n        resolve({\n          type: 'file',\n          data: dataUri,\n          mimeType: file.type,\n          filename: file.name,\n        });\n      }\n    };\n    reader.onerror = () => reject(new Error('Failed to read file'));\n    reader.readAsDataURL(file);\n  });\n}\n\n/**\n * Validate that a file is an acceptable type.\n *\n * @param file - The file to validate\n * @param acceptedTypes - Array of accepted MIME types\n * @param maxSizeBytes - Maximum file size in bytes (default: 10MB)\n */\nexport function validateFile(\n  file: File,\n  acceptedTypes: string[] = ALL_SUPPORTED_MIME_TYPES,\n  maxSizeBytes: number = 10 * 1024 * 1024\n): { valid: boolean; error?: string } {\n  if (!acceptedTypes.includes(file.type)) {\n    return {\n      valid: false,\n      error: `Invalid file type \"${file.type}\". Accepted types: ${acceptedTypes.join(', ')}`,\n    };\n  }\n\n  if (file.size > maxSizeBytes) {\n    const maxSizeMB = Math.round(maxSizeBytes / (1024 * 1024));\n    return {\n      valid: false,\n      error: `File too large. Maximum size: ${maxSizeMB}MB`,\n    };\n  }\n\n  return { valid: true };\n}\n\n/**\n * Get file parts from content.\n */\nexport function getFileParts(content: MessageContent): FileContentPart[] {\n  if (typeof content === 'string') {\n    return [];\n  }\n  return content.filter((part): part is FileContentPart => part.type === 'file');\n}\n\n/**\n * Check if content contains any files.\n */\nexport function hasFiles(content: MessageContent): boolean {\n  if (typeof content === 'string') {\n    return false;\n  }\n  return content.some(part => part.type === 'file');\n}\n\n/**\n * Get file extension from filename\n */\nexport function getFileExtension(filename: string): string {\n  const parts = filename.split('.');\n  return parts.length > 1 ? parts.pop()!.toLowerCase() : '';\n}\n\n/**\n * Get a display-friendly file type name\n */\nexport function getFileTypeName(mimeType: string, filename: string): string {\n  const ext = getFileExtension(filename).toUpperCase();\n\n  const typeMap: Record<string, string> = {\n    'application/pdf': 'PDF',\n    'text/plain': 'TXT',\n    'text/markdown': 'MD',\n    'text/csv': 'CSV',\n    'application/msword': 'DOC',\n    'application/vnd.openxmlformats-officedocument.wordprocessingml.document': 'DOCX',\n    'application/vnd.ms-excel': 'XLS',\n    'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet': 'XLSX',\n    'application/json': 'JSON',\n  };\n\n  return typeMap[mimeType] || ext || 'FILE';\n}\n","// Runtype Voice Provider\n//\n// Real-time streaming voice client for Runtype's `/ws/agents/:agentId/voice`\n// endpoint. The \"call\" is a single WebSocket session:\n//\n//   - up:   continuous mic audio as raw PCM16 LE mono @ 16kHz (binary frames)\n//   - down: WAV-wrapped PCM16 LE mono @ 24kHz audio (binary frames) +\n//           JSON control frames (transcript_interim / transcript_final /\n//           audio_end / metrics).\n//\n// The server's STT owns turn-taking, so the client streams continuously and\n// has no client-side VAD, barge-in monitoring, or batch upload. Auth rides the\n// `Sec-WebSocket-Protocol` subprotocol (`['runtype.bearer', clientToken]`),\n// never the query string: the token is never placed in a URL or logged.\n//\n// A continuous always-hot mic is, in UX terms, a permanent barge-in session, so\n// `getInterruptionMode()` reports the constant `'barge-in'` and the existing\n// mic-button wiring (ui.ts) treats a click as \"hang up at any state\" unchanged.\n\nimport type {\n  VoiceProvider,\n  VoiceResult,\n  VoiceStatus,\n  VoiceConfig,\n  VoiceMetrics,\n  VoicePlaybackEngine,\n} from \"../types\";\nimport { AudioPlaybackManager } from \"./audio-playback-manager\";\n\nconst CAPTURE_SAMPLE_RATE = 16000;\nconst PLAYBACK_SAMPLE_RATE = 24000;\nconst CAPTURE_BUFFER_SIZE = 4096;\nconst RIFF_MAGIC = 0x52494646; // \"RIFF\"\n\n/**\n * Strip the canonical 44-byte WAV header (if present) and return the raw PCM16\n * payload. The ElevenLabs realtime path WAV-wraps each frame; the Cloudflare DO\n * path may send raw PCM: detect the RIFF magic and handle both.\n */\nfunction stripWavHeader(buf: ArrayBuffer): Uint8Array {\n  if (buf.byteLength >= 44) {\n    const view = new DataView(buf);\n    if (view.getUint32(0, false) === RIFF_MAGIC) {\n      return new Uint8Array(buf, 44);\n    }\n  }\n  return new Uint8Array(buf);\n}\n\n/** Derive a ws(s):// base URL from a configured host (full URL or bare host). */\nfunction toWsBase(host: string): string {\n  const trimmed = host.replace(/\\/+$/, \"\");\n  if (/^wss?:\\/\\//i.test(trimmed)) return trimmed;\n  if (/^https?:\\/\\//i.test(trimmed)) return trimmed.replace(/^http/i, \"ws\");\n  const secure =\n    typeof window !== \"undefined\" && window.location?.protocol === \"https:\";\n  return `${secure ? \"wss:\" : \"ws:\"}//${trimmed}`;\n}\n\nexport class RuntypeVoiceProvider implements VoiceProvider {\n  type: \"runtype\" = \"runtype\";\n\n  private ws: WebSocket | null = null;\n  private captureContext: AudioContext | null = null;\n  private mediaStream: MediaStream | null = null;\n  private sourceNode: MediaStreamAudioSourceNode | null = null;\n  private processor: ScriptProcessorNode | null = null;\n  private playback: VoicePlaybackEngine | null = null;\n\n  // True while a call (WS session) is live: drives the idempotent start guard\n  // and `isBargeInActive()`.\n  private callLive = false;\n  private isSpeaking = false;\n\n  // Invalidates in-flight async work (playback-engine creation, late frames,\n  // status transitions) after a teardown/restart so a stale callback can't act\n  // on a newer call's resources. Bumped on every start and every cleanup.\n  private callGeneration = 0;\n\n  // Distinguishes a user-initiated close (code 1000) from a dropped connection.\n  private intentionalClose = false;\n\n  private resultCallbacks: ((result: VoiceResult) => void)[] = [];\n  private errorCallbacks: ((error: Error) => void)[] = [];\n  private statusCallbacks: ((status: VoiceStatus) => void)[] = [];\n  private transcriptCallbacks: ((\n    role: \"user\" | \"assistant\",\n    text: string,\n    isFinal: boolean,\n  ) => void)[] = [];\n  private metricsCallbacks: ((metrics: VoiceMetrics) => void)[] = [];\n\n  constructor(private config: VoiceConfig[\"runtype\"]) {}\n\n  // --- VoiceProvider lifecycle ----------------------------------------------\n\n  /** No-op: the WS session opens lazily in `startListening` (the \"call\"). */\n  async connect(): Promise<void> {}\n\n  /** Start the call: acquire mic, open the WS, stream PCM until hang-up. */\n  async startListening(): Promise<void> {\n    if (this.callLive) return; // idempotent: a call is already live\n\n    const agentId = this.config?.agentId;\n    const token = this.config?.clientToken;\n    const host = this.config?.host;\n    if (!agentId) throw new Error(\"Runtype voice requires an agentId\");\n    if (!token) throw new Error(\"Runtype voice requires a clientToken\");\n    if (!host) throw new Error(\"Runtype voice requires a host (or widget apiUrl)\");\n\n    const generation = ++this.callGeneration;\n    this.intentionalClose = false;\n    this.callLive = true;\n\n    try {\n      const stream = await navigator.mediaDevices.getUserMedia({\n        audio: {\n          sampleRate: CAPTURE_SAMPLE_RATE,\n          channelCount: 1,\n          echoCancellation: true,\n        },\n      });\n      if (generation !== this.callGeneration) {\n        stream.getTracks().forEach((t) => t.stop());\n        return;\n      }\n      this.mediaStream = stream;\n\n      // Create + resume both contexts inside the click gesture (iOS autoplay).\n      const AudioCtx =\n        (window as any).AudioContext || (window as any).webkitAudioContext;\n      const captureContext: AudioContext = new AudioCtx({\n        sampleRate: CAPTURE_SAMPLE_RATE,\n      });\n      if (captureContext.state === \"suspended\") {\n        await captureContext.resume().catch(() => {});\n      }\n      this.captureContext = captureContext;\n\n      const engine = this.config?.createPlaybackEngine\n        ? await this.config.createPlaybackEngine()\n        : new AudioPlaybackManager(PLAYBACK_SAMPLE_RATE);\n      if (generation !== this.callGeneration) {\n        // Torn down while async work was in flight: free what we acquired.\n        void engine.destroy();\n        stream.getTracks().forEach((t) => t.stop());\n        captureContext.close().catch(() => {});\n        return;\n      }\n      this.playback = engine;\n      engine.onFinished(() => {\n        if (generation !== this.callGeneration) return;\n        this.isSpeaking = false;\n        // Reply drained: the call stays open, so return to listening.\n        if (this.ws && this.ws.readyState === WebSocket.OPEN) {\n          this.emitStatus(\"listening\");\n        }\n      });\n\n      const wsUrl = `${toWsBase(host)}/ws/agents/${encodeURIComponent(agentId)}/voice`;\n      // Token rides the subprotocol; `runtype.bearer` is the marker the server\n      // echoes as the negotiated subprotocol (browsers fail the handshake if an\n      // offered subprotocol goes unanswered).\n      const ws = new WebSocket(wsUrl, [\"runtype.bearer\", token]);\n      ws.binaryType = \"arraybuffer\";\n      this.ws = ws;\n\n      ws.onopen = () => {\n        if (generation !== this.callGeneration) return;\n        this.emitStatus(\"listening\");\n        this.startCapture(captureContext, stream, ws, generation);\n      };\n\n      ws.onmessage = (event) => this.handleMessage(event, generation);\n\n      ws.onerror = () => {\n        if (generation !== this.callGeneration) return;\n        this.emitError(new Error(\"Voice connection failed\"));\n        this.emitStatus(\"error\");\n        this.cleanup();\n      };\n\n      ws.onclose = (evt) => {\n        if (this.intentionalClose) {\n          this.intentionalClose = false;\n          return;\n        }\n        if (generation !== this.callGeneration) return;\n        if (evt.code !== 1000) {\n          const codeMsg = evt.code ? ` (code ${evt.code})` : \"\";\n          this.emitError(new Error(`Voice connection closed${codeMsg}`));\n          this.emitStatus(\"error\");\n        } else {\n          this.emitStatus(\"idle\");\n        }\n        this.cleanup();\n      };\n    } catch (error) {\n      this.cleanup();\n      this.emitError(error as Error);\n      this.emitStatus(\"error\");\n      throw error;\n    }\n  }\n\n  /** End the call (hang up). */\n  async stopListening(): Promise<void> {\n    this.cleanup();\n    this.emitStatus(\"idle\");\n  }\n\n  /** Tear down the call and drop all callbacks (used by `cleanupVoice`). */\n  async disconnect(): Promise<void> {\n    this.cleanup();\n    this.emitStatus(\"disconnected\");\n    this.resultCallbacks = [];\n    this.errorCallbacks = [];\n    this.statusCallbacks = [];\n    this.transcriptCallbacks = [];\n    this.metricsCallbacks = [];\n  }\n\n  /** Stop the spoken reply without ending the call. */\n  stopPlayback(): void {\n    if (this.playback) this.playback.flush();\n    this.isSpeaking = false;\n    if (this.ws && this.ws.readyState === WebSocket.OPEN) {\n      this.emitStatus(\"listening\");\n    }\n  }\n\n  // --- Barge-in surface (constants for the continuous hot-mic model) --------\n\n  /** A continuous call is a permanent barge-in session. */\n  getInterruptionMode(): \"none\" | \"cancel\" | \"barge-in\" {\n    return \"barge-in\";\n  }\n\n  /** True while the call (hot mic) is live. */\n  isBargeInActive(): boolean {\n    return this.callLive;\n  }\n\n  /** \"Hang up\" the always-on mic. */\n  async deactivateBargeIn(): Promise<void> {\n    this.cleanup();\n    this.emitStatus(\"idle\");\n  }\n\n  // --- Capture ---------------------------------------------------------------\n\n  private startCapture(\n    context: AudioContext,\n    stream: MediaStream,\n    ws: WebSocket,\n    generation: number,\n  ): void {\n    const source = context.createMediaStreamSource(stream);\n    this.sourceNode = source;\n    const processor = context.createScriptProcessor(CAPTURE_BUFFER_SIZE, 1, 1);\n    this.processor = processor;\n\n    processor.onaudioprocess = (e) => {\n      if (generation !== this.callGeneration) return;\n      if (ws.readyState !== WebSocket.OPEN) return;\n      const input = e.inputBuffer.getChannelData(0);\n      const pcm16 = new Int16Array(input.length);\n      for (let i = 0; i < input.length; i++) {\n        const s = Math.max(-1, Math.min(1, input[i]));\n        pcm16[i] = s < 0 ? s * 0x8000 : s * 0x7fff;\n      }\n      ws.send(pcm16.buffer);\n    };\n\n    source.connect(processor);\n    // The processor must be connected to the graph to run; it writes no output,\n    // so the destination receives silence (no mic echo).\n    processor.connect(context.destination);\n  }\n\n  // --- Downstream ------------------------------------------------------------\n\n  private handleMessage(event: MessageEvent, generation: number): void {\n    if (generation !== this.callGeneration) return;\n\n    if (event.data instanceof ArrayBuffer) {\n      this.handleAudioFrame(event.data, generation);\n      return;\n    }\n\n    let msg: any;\n    try {\n      msg = JSON.parse(event.data as string);\n    } catch {\n      return; // non-JSON, non-binary frame: ignore\n    }\n\n    switch (msg.type) {\n      case \"transcript_interim\":\n        this.emitStatus(\"listening\");\n        this.emitTranscript(\"user\", msg.text ?? \"\", false);\n        break;\n\n      case \"transcript_final\": {\n        const role = msg.role === \"assistant\" ? \"assistant\" : \"user\";\n        // user final → agent is now thinking; assistant final → reply incoming.\n        this.emitStatus(role === \"user\" ? \"processing\" : \"speaking\");\n        this.emitTranscript(role, msg.text ?? \"\", true);\n        break;\n      }\n\n      case \"audio_end\":\n        if (this.playback) {\n          this.playback.markStreamEnd();\n        } else {\n          this.isSpeaking = false;\n          this.emitStatus(\"listening\");\n        }\n        break;\n\n      case \"metrics\":\n        this.emitMetrics({\n          llmMs: msg.llm_ms,\n          ttsMs: msg.tts_ms,\n          firstAudioMs: msg.first_audio_ms,\n          totalMs: msg.total_ms,\n        });\n        break;\n\n      case \"error\":\n        this.emitError(new Error(msg.error || \"Voice error\"));\n        this.emitStatus(\"error\");\n        break;\n    }\n  }\n\n  private handleAudioFrame(buf: ArrayBuffer, generation: number): void {\n    if (generation !== this.callGeneration) return;\n    if (!this.playback) return;\n    const pcm = stripWavHeader(buf);\n    if (pcm.length === 0) return;\n    if (!this.isSpeaking) {\n      this.isSpeaking = true;\n      this.emitStatus(\"speaking\");\n    }\n    this.playback.enqueue(pcm);\n  }\n\n  // --- Teardown --------------------------------------------------------------\n\n  private cleanup(): void {\n    // Invalidate any in-flight async continuation / late frames first.\n    this.callGeneration += 1;\n    this.callLive = false;\n    this.isSpeaking = false;\n\n    if (this.processor) {\n      this.processor.onaudioprocess = null;\n      this.processor.disconnect();\n      this.processor = null;\n    }\n    if (this.sourceNode) {\n      this.sourceNode.disconnect();\n      this.sourceNode = null;\n    }\n    if (this.mediaStream) {\n      this.mediaStream.getTracks().forEach((t) => t.stop());\n      this.mediaStream = null;\n    }\n    if (this.captureContext) {\n      this.captureContext.close().catch(() => {});\n      this.captureContext = null;\n    }\n    if (this.playback) {\n      void this.playback.destroy();\n      this.playback = null;\n    }\n    if (this.ws) {\n      this.intentionalClose = true;\n      try {\n        this.ws.close(1000, \"client ended call\");\n      } catch {\n        // ignore\n      }\n      this.ws = null;\n    }\n  }\n\n  // --- Callback registration + emit -----------------------------------------\n\n  onResult(callback: (result: VoiceResult) => void): void {\n    this.resultCallbacks.push(callback);\n  }\n\n  onError(callback: (error: Error) => void): void {\n    this.errorCallbacks.push(callback);\n  }\n\n  onStatusChange(callback: (status: VoiceStatus) => void): void {\n    this.statusCallbacks.push(callback);\n  }\n\n  onTranscript(\n    callback: (role: \"user\" | \"assistant\", text: string, isFinal: boolean) => void,\n  ): void {\n    this.transcriptCallbacks.push(callback);\n  }\n\n  onMetrics(callback: (metrics: VoiceMetrics) => void): void {\n    this.metricsCallbacks.push(callback);\n  }\n\n  private emitStatus(status: VoiceStatus): void {\n    this.statusCallbacks.forEach((cb) => cb(status));\n  }\n\n  private emitError(error: Error): void {\n    this.errorCallbacks.forEach((cb) => cb(error));\n  }\n\n  private emitTranscript(\n    role: \"user\" | \"assistant\",\n    text: string,\n    isFinal: boolean,\n  ): void {\n    this.transcriptCallbacks.forEach((cb) => cb(role, text, isFinal));\n  }\n\n  private emitMetrics(metrics: VoiceMetrics): void {\n    this.metricsCallbacks.forEach((cb) => cb(metrics));\n  }\n}\n","// Browser Voice Provider\n// Fallback implementation using Web Speech API\n\nimport type { VoiceProvider, VoiceResult, VoiceStatus, VoiceConfig } from '../types';\n\nexport class BrowserVoiceProvider implements VoiceProvider {\n  type: 'browser' = 'browser';\n  private recognition: any = null;\n  private resultCallbacks: ((result: VoiceResult) => void)[] = [];\n  private errorCallbacks: ((error: Error) => void)[] = [];\n  private statusCallbacks: ((status: VoiceStatus) => void)[] = [];\n  private isListening = false;\n  private w: any = typeof window !== 'undefined' ? window : undefined;\n\n  constructor(private config: VoiceConfig['browser'] = {}) {}\n\n  async connect() {\n    // Browser provider doesn't need connection\n    this.statusCallbacks.forEach(cb => cb('connected'));\n  }\n\n  async startListening() {\n    try {\n      if (this.isListening) {\n        throw new Error('Already listening');\n      }\n      \n      if (!this.w) {\n        throw new Error('Window object not available');\n      }\n      \n      // @ts-ignore - Browser SpeechRecognition API\n      const SpeechRecognition = this.w!.SpeechRecognition || this.w!.webkitSpeechRecognition;\n      \n      if (!SpeechRecognition) {\n        throw new Error('Browser speech recognition not supported');\n      }\n      \n      this.recognition = new SpeechRecognition();\n      this.recognition.lang = this.config?.language || 'en-US';\n      this.recognition.continuous = this.config?.continuous || false;\n      this.recognition.interimResults = true;\n      \n      this.recognition.onresult = (event: any) => {\n        const transcript = Array.from(event.results)\n          .map((result: any) => result[0])\n          .map((result: any) => result.transcript)\n          .join('');\n        \n        const isFinal = event.results[event.results.length - 1].isFinal;\n        \n        this.resultCallbacks.forEach(cb => cb({\n          text: transcript,\n          confidence: isFinal ? 0.8 : 0.5,\n          provider: 'browser'\n        }));\n        \n        if (isFinal && !this.config?.continuous) {\n          this.stopListening();\n        }\n      };\n      \n      this.recognition.onerror = (event: any) => {\n        this.errorCallbacks.forEach(cb => cb(new Error(event.error)));\n        this.statusCallbacks.forEach(cb => cb('error'));\n      };\n      \n      this.recognition.onstart = () => {\n        this.isListening = true;\n        this.statusCallbacks.forEach(cb => cb('listening'));\n      };\n      \n      this.recognition.onend = () => {\n        this.isListening = false;\n        this.statusCallbacks.forEach(cb => cb('idle'));\n      };\n      \n      this.recognition.start();\n      \n    } catch (error) {\n      this.errorCallbacks.forEach(cb => cb(error as Error));\n      this.statusCallbacks.forEach(cb => cb('error'));\n      throw error;\n    }\n  }\n\n  async stopListening() {\n    if (this.recognition) {\n      this.recognition.stop();\n      this.recognition = null;\n    }\n    \n    this.isListening = false;\n    this.statusCallbacks.forEach(cb => cb('idle'));\n  }\n\n  onResult(callback: (result: VoiceResult) => void): void {\n    this.resultCallbacks.push(callback);\n  }\n\n  onError(callback: (error: Error) => void): void {\n    this.errorCallbacks.push(callback);\n  }\n\n  onStatusChange(callback: (status: VoiceStatus) => void): void {\n    this.statusCallbacks.push(callback);\n  }\n\n  async disconnect(): Promise<void> {\n    await this.stopListening();\n    this.statusCallbacks.forEach(cb => cb('disconnected'));\n  }\n\n  // Check if browser supports speech recognition\n  static isSupported(): boolean {\n    // @ts-ignore\n    return 'SpeechRecognition' in window || 'webkitSpeechRecognition' in window;\n  }\n}","// Voice Provider Factory\n// Creates appropriate voice provider based on configuration\n\nimport type { VoiceProvider, VoiceConfig } from '../types';\nimport { RuntypeVoiceProvider } from './runtype-voice-provider';\nimport { BrowserVoiceProvider } from './browser-voice-provider';\n\nexport function createVoiceProvider(config: VoiceConfig): VoiceProvider {\n  switch (config.type) {\n    case 'runtype':\n      if (!config.runtype) {\n        throw new Error('Runtype voice provider requires configuration');\n      }\n      return new RuntypeVoiceProvider(config.runtype);\n    \n    case 'browser':\n      if (!BrowserVoiceProvider.isSupported()) {\n        throw new Error('Browser speech recognition not supported');\n      }\n      return new BrowserVoiceProvider(config.browser || {});\n    \n    case 'custom': {\n      // Bring-your-own provider: `custom` is either a ready VoiceProvider\n      // instance or a `() => VoiceProvider` factory (deferred construction, so\n      // resources like a WebSocket or AudioContext are only created when voice\n      // is actually set up). Resolve and sanity-check the shape.\n      const custom = config.custom;\n      if (!custom) {\n        throw new Error(\n          'Custom voice provider requires a `custom` provider instance or factory'\n        );\n      }\n      const provider = typeof custom === 'function' ? custom() : custom;\n      if (!provider || typeof provider.startListening !== 'function') {\n        throw new Error(\n          'Custom voice provider `custom` must be a VoiceProvider (or a factory returning one)'\n        );\n      }\n      return provider;\n    }\n\n    default:\n      throw new Error(`Unknown voice provider type: ${config.type}`);\n  }\n}\n\n// Auto-select the best available provider\nexport function createBestAvailableVoiceProvider(config?: Partial<VoiceConfig>): VoiceProvider {\n  // Honor an explicit bring-your-own provider before any built-in.\n  if (config?.type === 'custom' && config.custom) {\n    return createVoiceProvider({ type: 'custom', custom: config.custom });\n  }\n\n  // Prefer Runtype if configured\n  if (config?.type === 'runtype' && config.runtype) {\n    return createVoiceProvider({ type: 'runtype', runtype: config.runtype });\n  }\n\n  // Fall back to browser if supported\n  if (BrowserVoiceProvider.isSupported()) {\n    return createVoiceProvider({ \n      type: 'browser', \n      browser: config?.browser || { language: 'en-US' }\n    });\n  }\n  \n  throw new Error('No supported voice providers available');\n}\n\n// Check if any voice provider is available\nexport function isVoiceSupported(config?: Partial<VoiceConfig>): boolean {\n  try {\n    createBestAvailableVoiceProvider(config);\n    return true;\n  } catch (error) {\n    return false;\n  }\n}","// Browser Speech Engine\n//\n// Default `SpeechEngine` for the per-message \"Read aloud\" action and the\n// auto-speak path, backed by the browser Web Speech API\n// (`window.speechSynthesis`). Zero-backend and offline-capable, but limited to\n// the OS/browser voice set.\n//\n// A hosted engine (Runtype TTS, ElevenLabs, a server proxy, …) implements the\n// same `SpeechEngine` interface and is supplied via\n// `textToSpeech.createEngine`; such an engine can stream PCM into the realtime\n// voice `VoicePlaybackEngine` (see `audio-playback-manager.ts`). Nothing in the\n// `ReadAloudController` is browser-specific — only this file is.\n\nimport type { SpeechCallbacks, SpeechEngine, SpeechRequest } from \"../types\";\n\n/**\n * Pick the best available English voice from the browser's voice list.\n * Prefers high-quality remote/natural voices, then enhanced local voices,\n * then standard local voices, then any English voice, then the first voice.\n */\nexport function pickBestVoice(voices: SpeechSynthesisVoice[]): SpeechSynthesisVoice {\n  // Priority list: high-quality voices across browsers/platforms.\n  const preferred = [\n    // Edge Online Natural (highest quality)\n    \"Microsoft Jenny Online (Natural) - English (United States)\",\n    \"Microsoft Aria Online (Natural) - English (United States)\",\n    \"Microsoft Guy Online (Natural) - English (United States)\",\n    // Google remote (good quality, cross-platform in Chrome)\n    \"Google US English\",\n    \"Google UK English Female\",\n    // Apple premium/enhanced (macOS)\n    \"Ava (Premium)\",\n    \"Evan (Enhanced)\",\n    \"Samantha (Enhanced)\",\n    // Apple standard (macOS/iOS)\n    \"Samantha\",\n    \"Daniel\",\n    \"Karen\",\n    // Windows SAPI\n    \"Microsoft David Desktop - English (United States)\",\n    \"Microsoft Zira Desktop - English (United States)\",\n  ];\n\n  for (const name of preferred) {\n    const match = voices.find((v) => v.name === name);\n    if (match) return match;\n  }\n\n  // Fallback: any English voice, then first available.\n  return voices.find((v) => v.lang.startsWith(\"en\")) ?? voices[0];\n}\n\nexport interface BrowserSpeechEngineOptions {\n  /** Custom voice picker, used when no exact `voice` name is requested. */\n  pickVoice?: (voices: SpeechSynthesisVoice[]) => SpeechSynthesisVoice;\n}\n\n/** Default `SpeechEngine` backed by the browser Web Speech API. */\nexport class BrowserSpeechEngine implements SpeechEngine {\n  readonly id = \"browser\";\n  // speechSynthesis exposes pause()/resume(); reliable for start/stop and\n  // serviceable for pause on most engines (Chrome has known quirks on resume).\n  readonly supportsPause = true;\n\n  constructor(private options: BrowserSpeechEngineOptions = {}) {}\n\n  /** Whether the Web Speech API is available in this environment. */\n  static isSupported(): boolean {\n    return typeof window !== \"undefined\" && \"speechSynthesis\" in window;\n  }\n\n  speak(request: SpeechRequest, callbacks: SpeechCallbacks): void {\n    if (!BrowserSpeechEngine.isSupported()) {\n      callbacks.onError?.(new Error(\"Web Speech API is unavailable\"));\n      return;\n    }\n\n    const synth = window.speechSynthesis;\n    synth.cancel();\n\n    const utterance = new SpeechSynthesisUtterance(request.text);\n    const voices = synth.getVoices();\n    if (request.voice) {\n      const match = voices.find((v) => v.name === request.voice);\n      if (match) utterance.voice = match;\n    } else if (voices.length > 0) {\n      utterance.voice = this.options.pickVoice\n        ? this.options.pickVoice(voices)\n        : pickBestVoice(voices);\n    }\n    if (request.rate !== undefined) utterance.rate = request.rate;\n    if (request.pitch !== undefined) utterance.pitch = request.pitch;\n\n    utterance.onend = () => callbacks.onEnd?.();\n    utterance.onerror = (event) => {\n      // A stop()/superseding utterance fires \"canceled\"/\"interrupted\" — that's\n      // a normal end of playback, not an error worth surfacing. `event` is a\n      // SpeechSynthesisErrorEvent, so `.error` is the failure reason.\n      const reason = event.error;\n      if (reason === \"canceled\" || reason === \"interrupted\") {\n        callbacks.onEnd?.();\n      } else {\n        callbacks.onError?.(new Error(reason || \"Speech synthesis failed\"));\n      }\n    };\n\n    // Chrome bug: cancel() immediately followed by speak() can drop rate/pitch.\n    // A short delay lets the engine reset before the new utterance starts.\n    setTimeout(() => {\n      synth.speak(utterance);\n      // `utterance.onstart` is unreliable across browsers — Chrome can leave it\n      // unfired even while actively speaking, which would strand the UI in the\n      // \"loading\" state. Browser TTS has no real async prepare phase, so treat\n      // scheduling as the start; onend/onerror still drive the return to idle.\n      callbacks.onStart?.();\n    }, 50);\n  }\n\n  pause(): void {\n    if (BrowserSpeechEngine.isSupported()) window.speechSynthesis.pause();\n  }\n\n  resume(): void {\n    if (BrowserSpeechEngine.isSupported()) window.speechSynthesis.resume();\n  }\n\n  stop(): void {\n    if (BrowserSpeechEngine.isSupported()) window.speechSynthesis.cancel();\n  }\n}\n","// Read-Aloud Controller\n//\n// Engine-agnostic orchestrator for the per-message \"Read aloud\" action and the\n// auto-speak path. Owns the three things the UI shouldn't: which message is\n// currently active, its playback state, and the single speech engine instance.\n//\n// The button drives a single `toggle(id, request)`; listeners are notified on\n// every state change so the UI can reflect play / pause / resume. The engine is\n// resolved lazily (on first playback, inside the user gesture) via the\n// `resolveEngine` factory, so swapping the browser engine for a hosted one\n// (Runtype or custom) is a config change — this class is unchanged.\n\nimport type { ReadAloudState, SpeechEngine, SpeechRequest } from \"../types\";\n\nexport type ReadAloudListener = (\n  activeId: string | null,\n  state: ReadAloudState\n) => void;\n\nexport class ReadAloudController {\n  private engine: SpeechEngine | null = null;\n  private activeId: string | null = null;\n  private state: ReadAloudState = \"idle\";\n  private listeners = new Set<ReadAloudListener>();\n  // Bumped on every play/stop so a late async engine creation or a stale\n  // utterance callback from a superseded request can't mutate current state.\n  private generation = 0;\n\n  constructor(\n    private resolveEngine: () => SpeechEngine | Promise<SpeechEngine> | null\n  ) {}\n\n  /** Whether the active engine supports pause/resume (vs. stop-only). */\n  get supportsPause(): boolean {\n    return this.engine?.supportsPause ?? true;\n  }\n\n  /** Playback state for a message id (`idle` unless it's the active message). */\n  stateFor(id: string): ReadAloudState {\n    return this.activeId === id ? this.state : \"idle\";\n  }\n\n  /** The message currently being read aloud, if any. */\n  activeMessageId(): string | null {\n    return this.activeId;\n  }\n\n  /** Subscribe to state changes. Returns an unsubscribe function. */\n  onChange(listener: ReadAloudListener): () => void {\n    this.listeners.add(listener);\n    return () => this.listeners.delete(listener);\n  }\n\n  /**\n   * Primary entry point for the button: cycle this message through\n   * play → pause → resume (or play → stop when the engine can't pause), and\n   * start fresh when a different message is requested.\n   */\n  toggle(id: string, request: SpeechRequest): void {\n    if (this.activeId === id) {\n      if (this.state === \"playing\") {\n        if (this.engine?.supportsPause) {\n          this.engine.pause();\n          this.set(id, \"paused\");\n        } else {\n          this.stop();\n        }\n        return;\n      }\n      if (this.state === \"paused\") {\n        this.engine?.resume();\n        this.set(id, \"playing\");\n        return;\n      }\n      if (this.state === \"loading\") {\n        this.stop();\n        return;\n      }\n    }\n    void this.play(id, request);\n  }\n\n  /** Start (or restart) playback for a message, stopping any current playback. */\n  async play(id: string, request: SpeechRequest): Promise<void> {\n    const generation = ++this.generation;\n    this.engine?.stop();\n    this.set(id, \"loading\");\n\n    try {\n      if (!this.engine) {\n        const resolved = await this.resolveEngine();\n        if (generation !== this.generation) return; // superseded mid-resolve\n        if (!resolved) {\n          this.set(null, \"idle\");\n          return;\n        }\n        this.engine = resolved;\n      }\n\n      this.engine.speak(request, {\n        onStart: () => {\n          if (generation === this.generation) this.set(id, \"playing\");\n        },\n        onEnd: () => {\n          if (generation === this.generation) this.set(null, \"idle\");\n        },\n        onError: () => {\n          if (generation === this.generation) this.set(null, \"idle\");\n        },\n      });\n    } catch {\n      if (generation === this.generation) this.set(null, \"idle\");\n    }\n  }\n\n  /** Stop playback and return to idle. */\n  stop(): void {\n    this.generation++;\n    this.engine?.stop();\n    this.set(null, \"idle\");\n  }\n\n  /** Drop the controller and its engine (called on widget teardown). */\n  destroy(): void {\n    this.stop();\n    this.engine?.destroy?.();\n    this.engine = null;\n    this.listeners.clear();\n  }\n\n  private set(id: string | null, state: ReadAloudState): void {\n    this.activeId = state === \"idle\" ? null : id;\n    this.state = state;\n    for (const listener of this.listeners) listener(this.activeId, this.state);\n  }\n}\n","/**\n * Resolve the text that should be spoken for an assistant message.\n *\n * Some flows return an action-format envelope as the message body — e.g.\n * `{\"action\":\"message\",\"text\":\"Hello\"}`, often inside a ```json fence — rather\n * than plain prose. Speaking the raw JSON (or, after fenced-code stripping,\n * nothing at all) is useless, so we first try to extract the human `text` field\n * (mirroring the widget's `defaultJsonActionParser`, which also keys on\n * `.text`), and otherwise fall back to stripping Markdown from the raw content.\n */\nexport function resolveSpeakableText(raw: string): string {\n  if (!raw) return \"\";\n  const actionText = extractActionMessageText(raw);\n  if (actionText !== null) return stripMarkdownForSpeech(actionText);\n  return stripMarkdownForSpeech(raw);\n}\n\n/**\n * If `raw` is (or wraps, in a ```json fence) a JSON object with a string `text`\n * property, return that text; otherwise return `null`. Matches the action\n * envelope shape `{ action, text, ... }` used by the widget's action system.\n */\nexport function extractActionMessageText(raw: string): string | null {\n  let body = raw.trim();\n  const fence = body.match(/^```(?:json)?\\s*([\\s\\S]*?)\\s*```$/i);\n  if (fence) body = fence[1].trim();\n  if (!body.startsWith(\"{\")) return null;\n  try {\n    const parsed = JSON.parse(body);\n    if (parsed && typeof parsed === \"object\" && typeof (parsed as { text?: unknown }).text === \"string\") {\n      return (parsed as { text: string }).text;\n    }\n  } catch {\n    // Not valid JSON — fall through to the Markdown path.\n  }\n  return null;\n}\n\n/**\n * Convert Markdown to plain text suitable for text-to-speech.\n *\n * Read-aloud should speak the prose a user sees, not the markup: code fences,\n * backticks, emphasis markers, link/image syntax and raw HTML are all noise\n * when spoken. This is intentionally lightweight (regex-based, no full Markdown\n * parser) — it favours predictable, dependency-free output over perfect\n * fidelity. Speech engines receive the result of this function, never raw\n * Markdown.\n */\nexport function stripMarkdownForSpeech(markdown: string): string {\n  if (!markdown) return \"\";\n  let text = markdown;\n\n  // Fenced code blocks: drop entirely (reading source aloud is noise).\n  text = text.replace(/```[\\s\\S]*?```/g, \" \");\n  text = text.replace(/~~~[\\s\\S]*?~~~/g, \" \");\n\n  // Inline code: keep the inner text, drop the backticks.\n  text = text.replace(/`([^`]+)`/g, \"$1\");\n\n  // Images: speak the alt text only.\n  text = text.replace(/!\\[([^\\]]*)\\]\\([^)]*\\)/g, \"$1\");\n\n  // Inline links: speak the link text, drop the URL.\n  text = text.replace(/\\[([^\\]]+)\\]\\([^)]*\\)/g, \"$1\");\n\n  // Reference-style links/images: [text][ref] -> text.\n  text = text.replace(/\\[([^\\]]+)\\]\\[[^\\]]*\\]/g, \"$1\");\n\n  // Raw HTML tags.\n  text = text.replace(/<\\/?[a-zA-Z][^>]*>/g, \" \");\n\n  // Line-leading markers: headings, blockquotes, list bullets, ordered items.\n  text = text.replace(/^[ \\t]*#{1,6}[ \\t]+/gm, \"\");\n  text = text.replace(/^[ \\t]*>[ \\t]?/gm, \"\");\n  text = text.replace(/^[ \\t]*[-*+][ \\t]+/gm, \"\");\n  text = text.replace(/^[ \\t]*\\d+\\.[ \\t]+/gm, \"\");\n\n  // Horizontal rules.\n  text = text.replace(/^[ \\t]*([-*_])([ \\t]*\\1){2,}[ \\t]*$/gm, \" \");\n\n  // Emphasis / strikethrough markers (bold before italic so ** is consumed).\n  text = text.replace(/(\\*\\*|__)(.*?)\\1/g, \"$2\");\n  text = text.replace(/(\\*|_)(.*?)\\1/g, \"$2\");\n  text = text.replace(/~~(.*?)~~/g, \"$1\");\n\n  // Decode a few common HTML entities so they aren't spoken literally.\n  text = text\n    .replace(/&amp;/g, \"&\")\n    .replace(/&lt;/g, \"<\")\n    .replace(/&gt;/g, \">\")\n    .replace(/&quot;/g, '\"')\n    .replace(/&#39;/g, \"'\")\n    .replace(/&nbsp;/g, \" \");\n\n  // Collapse whitespace.\n  text = text.replace(/[ \\t]+/g, \" \");\n  text = text.replace(/[ \\t]*\\n[ \\t]*/g, \"\\n\");\n  text = text.replace(/\\n{2,}/g, \"\\n\");\n\n  return text.trim();\n}\n","// Deferred loader for the hosted Runtype TTS read-aloud engine.\n//\n// The engine (`RuntypeSpeechEngine` + the `AudioPlaybackManager` it bundles) is\n// ~4–5 kB and only used when `textToSpeech.provider: 'runtype'` is configured —\n// an opt-in. To keep it out of the CDN payload (`index.global.js`), the IIFE\n// build marks the default `import(\"./runtype-speech-engine\")` below external and\n// `index-global.ts` registers a loader that imports the standalone\n// `runtype-tts.js` chunk from a sibling URL instead. Mirrors how the WebMCP\n// polyfill is deferred (see `setWebMcpPolyfillLoader` in `webmcp-bridge.ts`).\n//\n// In every other build (ESM/CJS main entry, theme-editor) no loader is\n// registered, so the default relative import resolves and the engine is inlined\n// — those bundlers code-split or have headroom, so there's no runtime fetch for\n// npm consumers.\n\nimport type { RuntypeSpeechEngine } from \"./runtype-speech-engine\";\nimport type { FallbackSpeechEngine } from \"./fallback-speech-engine\";\n\n/** The slice of the engine chunk the session consumes. */\nexport type RuntypeTtsModule = {\n  RuntypeSpeechEngine: typeof RuntypeSpeechEngine;\n  FallbackSpeechEngine: typeof FallbackSpeechEngine;\n};\n\nlet loader: (() => Promise<RuntypeTtsModule>) | null = null;\n\n/**\n * Override how the Runtype TTS engine module is obtained. By default the session\n * does `import(\"./runtype-tts-entry\")`, which bundlers resolve/inline. The\n * IIFE/CDN entry registers a loader that imports the self-contained\n * `runtype-tts.js` chunk from a URL derived from the widget script's own `src`.\n * Pass `null` to restore the default (used by tests).\n */\nexport const setRuntypeTtsLoader = (\n  l: (() => Promise<RuntypeTtsModule>) | null,\n): void => {\n  loader = l;\n};\n\n/** Resolve the Runtype TTS engine module (registered loader, else inlined import). */\nexport const loadRuntypeTts = (): Promise<RuntypeTtsModule> =>\n  loader ? loader() : import(\"./runtype-tts-entry\");\n","import { AgentWidgetClient, type SSEEventCallback } from \"./client\";\nimport { isWebMcpToolName } from \"./webmcp-bridge\";\nimport {\n  SUGGEST_REPLIES_TOOL_NAME,\n  suggestRepliesToolResult,\n} from \"./suggest-replies-tool\";\nimport {\n  AgentWidgetConfig,\n  AgentWidgetEvent,\n  AgentWidgetMessage,\n  AgentWidgetApproval,\n  AgentWidgetApprovalDecisionOptions,\n  WebMcpConfirmInfo,\n  AgentExecutionState,\n  ClientSession,\n  ContentPart,\n  InjectMessageOptions,\n  InjectAssistantMessageOptions,\n  InjectUserMessageOptions,\n  InjectSystemMessageOptions,\n  InjectComponentDirectiveOptions,\n  PersonaArtifactRecord,\n  PersonaArtifactManualUpsert\n} from \"./types\";\nimport {\n  generateUserMessageId,\n  generateAssistantMessageId\n} from \"./utils/message-id\";\nimport { IMAGE_ONLY_MESSAGE_FALLBACK_TEXT } from \"./utils/content\";\nimport type {\n  VoiceProvider,\n  VoiceStatus,\n  VoiceConfig,\n  ReadAloudState,\n  SpeechEngine\n} from \"./types\";\nimport {\n  createVoiceProvider,\n  isVoiceSupported,\n  BrowserSpeechEngine,\n  pickBestVoice,\n  ReadAloudController,\n  type ReadAloudListener\n} from \"./voice\";\nimport { resolveSpeakableText } from \"./utils/speech-text\";\nimport { loadRuntypeTts } from \"./voice/runtype-tts-loader\";\n\nexport type AgentWidgetSessionStatus =\n  | \"idle\"\n  | \"connecting\"\n  | \"connected\"\n  | \"error\";\n\n/**\n * Config fields the `AgentWidgetClient` reads to shape the connection and each\n * request. When NONE of these change, `updateConfig` can refresh the live\n * client in place (preserving the WebMCP bridge and any in-flight stream/resume)\n * instead of swapping in a fresh client and tearing down WebMCP state. Fields\n * outside this list (theme, copy, layout, suggestionChips, iterationDisplay,\n * postprocessMessage, feature display toggles, …) are display-only and safe to\n * apply mid-turn: which is what a self-styling widget needs when a `webmcp:*`\n * theme tool re-renders the widget while the agent's turn is still streaming.\n *\n * Compared by identity (`!==`): primitives by value, functions/objects by\n * reference. A consumer that rebuilds these objects on every render simply\n * takes the (still-correct) full-rebuild path. The default is therefore safe:\n * anything not explicitly listed here can never strand a paused turn.\n */\nconst CONNECTION_CONFIG_KEYS = [\n  \"apiUrl\",\n  \"clientToken\",\n  \"flowId\",\n  \"agentId\",\n  \"target\",\n  \"targetProviders\",\n  \"agent\",\n  \"agentOptions\",\n  \"headers\",\n  \"getHeaders\",\n  \"webmcp\",\n  \"streamParser\",\n  \"parserType\",\n  \"contextProviders\",\n  \"requestMiddleware\",\n  \"customFetch\",\n  \"parseSSEEvent\",\n  \"onSessionInit\",\n  \"onSessionExpired\",\n  \"getStoredSessionId\",\n  \"setStoredSessionId\",\n] as const satisfies ReadonlyArray<keyof AgentWidgetConfig>;\n\nfunction connectionConfigChanged(\n  prev: AgentWidgetConfig,\n  next: AgentWidgetConfig,\n): boolean {\n  return CONNECTION_CONFIG_KEYS.some((key) => prev[key] !== next[key]);\n}\n\ntype SessionCallbacks = {\n  onMessagesChanged: (messages: AgentWidgetMessage[]) => void;\n  onStatusChanged: (status: AgentWidgetSessionStatus) => void;\n  onStreamingChanged: (streaming: boolean) => void;\n  onError?: (error: Error) => void;\n  onVoiceStatusChanged?: (status: VoiceStatus) => void;\n  onArtifactsState?: (state: {\n    artifacts: PersonaArtifactRecord[];\n    selectedId: string | null;\n  }) => void;\n};\n\n/**\n * Build the user-facing content shown when a dispatch fails before any\n * assistant content streamed back. This fires on real network/server errors\n * (connection refused, CORS, 4xx/5xx, malformed stream): not just an\n * un-wired proxy, so the copy stays honest about the failure and surfaces the\n * underlying reason to help with debugging.\n *\n * Callers can override the copy via `config.errorMessage` (a static string or\n * a function of the error). An override that returns an empty string yields \"\"\n * here, which the caller treats as \"suppress the fallback bubble\".\n */\nfunction buildDispatchErrorContent(\n  error: unknown,\n  override?: AgentWidgetConfig[\"errorMessage\"]\n): string {\n  const err = error instanceof Error ? error : new Error(String(error));\n\n  if (typeof override === \"string\") return override;\n  if (typeof override === \"function\") return override(err);\n\n  const base =\n    \"Sorry: I couldn't reach the assistant. The chat service didn't respond. Please check that your proxy or backend is running and reachable, then try again.\";\n  return err.message ? `${base}\\n\\n_Details: ${err.message}_` : base;\n}\n\nconst buildWebMcpErrorResult = (message: string) => ({\n  isError: true,\n  content: [{ type: \"text\" as const, text: message }],\n});\n\nconst getWebMcpErrorMessage = (\n  error: unknown,\n  fallback = \"WebMCP tool execution failed.\",\n): string => {\n  if (error instanceof Error && error.message) return error.message;\n  if (typeof error === \"string\" && error) return error;\n  return fallback;\n};\n\n/**\n * Tool names whose `step_await` the widget resolves automatically (no user\n * pill click): `webmcp:*` page tools and the built-in fire-and-forget\n * `suggest_replies`. These share the await-batch / dedupe / resume machinery;\n * `ask_user_question` is NOT one of them: it blocks on the answer sheet.\n */\nconst isAutoResolvedLocalToolName = (name: string): boolean =>\n  isWebMcpToolName(name) || name === SUGGEST_REPLIES_TOOL_NAME;\n\nexport class AgentWidgetSession {\n  private client: AgentWidgetClient;\n  private messages: AgentWidgetMessage[];\n  private status: AgentWidgetSessionStatus = \"idle\";\n  private streaming = false;\n  private abortController: AbortController | null = null;\n  private sequenceCounter = Date.now();\n  \n  // Client token session management\n  private clientSession: ClientSession | null = null;\n\n  // Agent execution state\n  private agentExecution: AgentExecutionState | null = null;\n\n  private artifacts = new Map<string, PersonaArtifactRecord>();\n  private selectedArtifactId: string | null = null;\n\n  // WebMCP dedupe: keys are `${executionId}:${toolCallId}` so they're\n  // naturally scoped to a single dispatch. A later dispatch (new executionId)\n  // that happens to recycle a `toolCall.id` never collides with prior entries,\n  // and a stale re-emit from an in-flight prior dispatch stays blocked because\n  // its executionId is still in the set.\n  //\n  //   webMcpInflightKeys: currently executing; cleared on completion of\n  //                         EITHER /resume success OR /resume throw. Blocks\n  //                         concurrent re-fire during the resolve round-trip.\n  //   webMcpResolvedKeys: /resume HTTP returned 2xx; not cleared on a new\n  //                         dispatch (executionId scoping makes that\n  //                         unnecessary). Blocks stale step_await re-emits\n  //                         for the same execution.\n  //\n  // If `/resume` throws (network error, server 5xx), we DO want a retry path:\n  // the dispatch is recoverable. Such a tool stays in neither set, so a\n  // subsequent re-emit will re-trigger.\n  private webMcpInflightKeys: Set<string> = new Set();\n  private webMcpResolvedKeys: Set<string> = new Set();\n  // Per-resolve AbortControllers, kept in a set so multiple `webmcp:*`\n  // step_await resolves in one turn never abort one another. The shared\n  // `this.abortController` is intentionally NOT used by resolveWebMcpToolCall:\n  // in a CHAINED turn (tool A → /resume → tool B, where the server emits B's\n  // step_await inside A's resume SSE stream) the shared controller is still\n  // piping A's resume stream: the very stream that just delivered B. Aborting\n  // it mid-chain (the prior shared-controller pre-abort) tore that stream down,\n  // so B never reached execute() and its /resume was never POSTed, pausing the\n  // dispatch forever. cancel(), clearMessages(), hydrateMessages(), and\n  // sendMessage() iterate this set to tear every in-flight resolve down on a\n  // real stop / new turn.\n  private webMcpResolveControllers: Set<AbortController> = new Set();\n  // Bumped on every teardown / new-turn boundary (cancel, clearMessages,\n  // hydrateMessages, sendMessage). A resolveWebMcpToolCall deferred via\n  // queueMicrotask captures the epoch at queue time and bails if it changed,\n  // so a resolve queued just before a teardown can't escape it by installing a\n  // fresh controller after the set was already cleared.\n  private webMcpEpoch = 0;\n  // WebMCP native approval-bubble gate. When no custom `webmcp.onConfirm` is\n  // supplied, the bridge's confirm handler routes here: we inject an\n  // approval-variant message and park the bridge on a Promise that resolves\n  // when the user clicks Approve/Deny (see requestWebMcpApproval /\n  // resolveWebMcpApproval). Resolvers are keyed by the approval message id.\n  private webMcpApprovalResolvers: Map<string, (approved: boolean) => void> =\n    new Map();\n  private webMcpApprovalSeq = 0;\n  // Parallel local-tool batching (core#3878). A single model turn can emit\n  // multiple `step_await(local_tool_required)` events for ONE paused\n  // executionId: including two PARALLEL calls to the SAME tool (\"add SHOE-001\n  // and SHOE-007\"). Those collapse to an identical `toolId`/`index` and differ\n  // only by the per-call `webMcpToolCallId`. We collect all awaits for an\n  // executionId that arrive in the same stream tick, then post ONE `/resume`\n  // keyed by `webMcpToolCallId`: NOT one `/resume` per tool keyed by name\n  // (which collides for same-tool calls, and whose concurrent posts on one\n  // execution raced → the second 404'd → the turn hung). Keyed by executionId;\n  // `seen` dedupes duplicate step_await re-emits within a batch. Cleared on\n  // every teardown via `abortWebMcpResolves`.\n  private webMcpAwaitBatches: Map<\n    string,\n    { snapshots: AgentWidgetMessage[]; seen: Set<string> }\n  > = new Map();\n\n  // Voice support\n  private voiceProvider: VoiceProvider | null = null;\n  private voiceActive = false;\n  private voiceStatus: VoiceStatus = 'disconnected';\n\n  constructor(\n    private config: AgentWidgetConfig = {},\n    private callbacks: SessionCallbacks\n  ) {\n    this.messages = [...(config.initialMessages ?? [])].map((message) => ({\n      ...message,\n      sequence: message.sequence ?? this.nextSequence()\n    }));\n    this.messages = this.sortMessages(this.messages);\n    this.client = new AgentWidgetClient(config);\n    this.wireDefaultWebMcpConfirm();\n\n    // Hydrate artifacts from config (mirrors `initialMessages`). Restored\n    // records are forced to `status: \"complete\"`: a mid-stream artifact should\n    // never reappear after a refresh with its skeleton still showing.\n    for (const rec of config.initialArtifacts ?? []) {\n      this.artifacts.set(rec.id, { ...rec, status: \"complete\" });\n    }\n    if (config.initialSelectedArtifactId != null) {\n      this.selectedArtifactId = config.initialSelectedArtifactId;\n    }\n\n    if (this.messages.length) {\n      this.callbacks.onMessagesChanged([...this.messages]);\n    }\n    if (this.artifacts.size > 0) {\n      this.emitArtifactsState();\n    }\n    this.callbacks.onStatusChanged(this.status);\n    this.prefetchRuntypeTts();\n  }\n\n  /**\n   * Warm the deferred Runtype TTS engine chunk at init so it's resolved before\n   * the first \"Read aloud\" click. Module fetch only — no AudioContext (that\n   * still waits for the user gesture on first playback). In the IIFE/CDN build\n   * this fetches the standalone `runtype-tts.js`; in bundler builds the module\n   * is already inlined, so this is a cheap no-op. Errors are swallowed: a failed\n   * prefetch just means the chunk loads on first click instead (or, on a hard\n   * failure, the browser-voice fallback kicks in).\n   */\n  private prefetchRuntypeTts(): void {\n    const tts = this.config.textToSpeech;\n    if (tts?.provider !== \"runtype\" || tts.createEngine) return;\n    // Only warm the chunk when the engine will actually be built (same gate as\n    // createSpeechEngine) — an incomplete config (e.g. a proxy demo with no\n    // clientToken) falls back to the browser voice, so the fetch would be wasted.\n    const host = tts.host ?? this.config.apiUrl;\n    const agentId =\n      tts.agentId ?? this.config.voiceRecognition?.provider?.runtype?.agentId ?? this.config.agentId;\n    if (!host || !agentId || !this.config.clientToken) return;\n    void loadRuntypeTts().catch(() => {});\n  }\n\n  /**\n   * Set callback for capturing raw SSE events (forwards to client)\n   */\n  public setSSEEventCallback(callback: SSEEventCallback): void {\n    this.client.setSSEEventCallback(callback);\n  }\n\n  /**\n   * Check if running in client token mode\n   */\n  public isClientTokenMode(): boolean {\n    return this.client.isClientTokenMode();\n  }\n\n  /**\n   * Check if running in agent execution mode\n   */\n  public isAgentMode(): boolean {\n    return this.client.isAgentMode();\n  }\n\n  /**\n   * Get current agent execution state (if in agent mode)\n   */\n  public getAgentExecution(): AgentExecutionState | null {\n    return this.agentExecution;\n  }\n\n  /**\n   * Check if an agent execution is currently running\n   */\n  public isAgentExecuting(): boolean {\n    return this.agentExecution?.status === 'running';\n  }\n\n  /**\n   * Check if voice is supported\n   */\n  public isVoiceSupported(): boolean {\n    return isVoiceSupported(this.config.voiceRecognition?.provider);\n  }\n\n  /**\n   * Check if voice is currently active\n   */\n  public isVoiceActive(): boolean {\n    return this.voiceActive;\n  }\n\n  /**\n   * Get current voice status\n   */\n  public getVoiceStatus(): VoiceStatus {\n    return this.voiceStatus;\n  }\n\n  /**\n   * Get the voice interruption mode from the provider (none/cancel/barge-in)\n   */\n  public getVoiceInterruptionMode(): \"none\" | \"cancel\" | \"barge-in\" {\n    if (this.voiceProvider?.getInterruptionMode) {\n      return this.voiceProvider.getInterruptionMode();\n    }\n    return \"none\";\n  }\n\n  /**\n   * Stop voice playback / cancel in-flight request without starting recording.\n   * Returns to idle state.\n   */\n  public stopVoicePlayback(): void {\n    if (this.voiceProvider?.stopPlayback) {\n      this.voiceProvider.stopPlayback();\n    }\n  }\n\n  /** Returns true if the barge-in mic stream is alive (hot mic between turns) */\n  public isBargeInActive(): boolean {\n    return this.voiceProvider?.isBargeInActive?.() ?? false;\n  }\n\n  /** Tear down the barge-in mic pipeline: \"hang up\" the always-on mic */\n  public async deactivateBargeIn(): Promise<void> {\n    if (this.voiceProvider?.deactivateBargeIn) {\n      await this.voiceProvider.deactivateBargeIn();\n    }\n  }\n\n  // Pending placeholder IDs for Runtype two-phase voice flow\n  private pendingVoiceUserMessageId: string | null = null;\n  private pendingVoiceAssistantMessageId: string | null = null;\n\n  // Track message IDs where the Runtype provider already played TTS audio\n  // so browser TTS doesn't double-speak them\n  private ttsSpokenMessageIds = new Set<string>();\n\n  // Owns the per-message \"Read aloud\" action and the auto-speak path: which\n  // message is active, its play/pause state, and the speech engine. The engine\n  // is resolved lazily on first playback (inside the user gesture) so a hosted\n  // engine via `textToSpeech.createEngine` plugs in without changing this class.\n  private readAloud = new ReadAloudController(() => this.createSpeechEngine());\n\n  /**\n   * Resolve the speech engine behind read-aloud (and auto-speak):\n   *   1. An explicit `textToSpeech.createEngine` always wins (full BYO control).\n   *   2. `provider: 'runtype'` builds the hosted {@link RuntypeSpeechEngine}\n   *      from the widget config (host = `apiUrl`, `agentId`, `clientToken`),\n   *      wrapped in a browser {@link FallbackSpeechEngine} unless\n   *      `browserFallback: false` — so a missing endpoint or transient failure\n   *      degrades to the browser voice instead of erroring, and auto-upgrades to\n   *      Runtype voices the day the endpoint ships.\n   *   3. Otherwise the browser Web Speech API engine.\n   */\n  private createSpeechEngine(): SpeechEngine | Promise<SpeechEngine> | null {\n    const tts = this.config.textToSpeech;\n    if (tts?.createEngine) return tts.createEngine();\n\n    const browser = BrowserSpeechEngine.isSupported()\n      ? new BrowserSpeechEngine({ pickVoice: tts?.pickVoice })\n      : null;\n\n    if (tts?.provider === \"runtype\") {\n      const host = tts.host ?? this.config.apiUrl;\n      const agentId =\n        tts.agentId ?? this.config.voiceRecognition?.provider?.runtype?.agentId ?? this.config.agentId;\n      const clientToken = this.config.clientToken;\n      const wantFallback = tts.browserFallback !== false;\n\n      if (host && agentId && clientToken) {\n        // Lazy-load the Runtype engine through the deferred loader: the IIFE/CDN\n        // build serves it from the standalone `runtype-tts.js` chunk (kept out of\n        // `index.global.js`), while bundler consumers get it inlined. Resolved on\n        // first playback (inside the user gesture); `createSpeechEngine` may\n        // return a Promise. Prefetched at init (see `prefetchRuntypeTts`) so the\n        // chunk is warm before the first click.\n        return loadRuntypeTts().then(\n          ({ RuntypeSpeechEngine, FallbackSpeechEngine }) => {\n            const runtype = new RuntypeSpeechEngine({\n              host,\n              agentId,\n              clientToken,\n              voice: tts.voice,\n              prebufferMs: tts.prebufferMs,\n              createPlaybackEngine: tts.createPlaybackEngine,\n            });\n            return wantFallback && browser\n              ? new FallbackSpeechEngine(runtype, browser, {\n                  onFallback: (error) =>\n                    console.warn(\n                      `[persona] Runtype read-aloud failed; using browser voice. ${error.message}`,\n                    ),\n                })\n              : runtype;\n          },\n        );\n      }\n\n      // Runtype was requested but the config is incomplete (no agentId/token —\n      // e.g. a proxy-backed demo). Never break the button: use the browser\n      // voice. Only warn when intent is clear (a clientToken is present) so a\n      // plain proxy demo stays quiet.\n      if (wantFallback && browser) {\n        if (clientToken) {\n          console.warn(\n            \"[persona] textToSpeech.provider 'runtype' is missing an agentId; using the browser voice. Set textToSpeech.agentId (or voiceRecognition.provider.runtype.agentId).\",\n          );\n        }\n        return browser;\n      }\n    }\n\n    return browser;\n  }\n\n  /**\n   * Setup voice recognition with the given configuration\n   */\n  public setupVoice(config?: VoiceConfig) {\n    try {\n      const voiceConfig = config || this.getVoiceConfigFromConfig();\n      if (!voiceConfig) {\n        throw new Error('Voice configuration not provided');\n      }\n\n      this.voiceProvider = createVoiceProvider(voiceConfig);\n\n      // Read configurable text from widget config\n      const voiceRecognitionConfig = this.config.voiceRecognition ?? {};\n      const processingErrorText = voiceRecognitionConfig.processingErrorText ?? 'Voice processing failed. Please try again.';\n\n      // STT-style providers (browser + bring-your-own `custom`) deliver a final\n      // transcript that we send as a normal user message: the agent then runs\n      // via the standard SSE chat path. Only the realtime `runtype` provider is\n      // excluded here: it owns the whole turn and drives onTranscript below.\n      this.voiceProvider.onResult((result) => {\n        if (result.provider !== 'runtype') {\n          if (result.text && result.text.trim()) {\n            this.sendMessage(result.text, { viaVoice: true });\n          }\n        }\n      });\n\n      // Realtime (runtype) voice: drive the chat thread from streaming\n      // transcript frames. Live interim user text grows in place; the user\n      // message finalizes immediately on transcript_final{user}; the assistant\n      // reply lands (a single block, synced with audio) on its final frame.\n      // In-flight bubbles carry voiceProcessing=true so consumers can style\n      // them via messageTransform; it clears once the text is final.\n      if (this.voiceProvider.onTranscript) {\n        this.voiceProvider.onTranscript((role, text, isFinal) => {\n          if (role === 'user') {\n            if (!this.pendingVoiceUserMessageId) {\n              const msg = this.injectMessage({\n                role: 'user',\n                content: text,\n                streaming: false,\n                voiceProcessing: !isFinal\n              });\n              this.pendingVoiceUserMessageId = msg.id;\n            } else {\n              this.upsertMessage({\n                id: this.pendingVoiceUserMessageId,\n                role: 'user',\n                content: text,\n                createdAt: new Date().toISOString(),\n                streaming: false,\n                voiceProcessing: !isFinal\n              });\n            }\n\n            if (isFinal) {\n              // User finished: the agent is now thinking. Release the user\n              // bubble (a new interim starts a fresh turn) and show a typing\n              // indicator in a fresh assistant placeholder.\n              this.pendingVoiceUserMessageId = null;\n              const assistantMsg = this.injectMessage({\n                role: 'assistant',\n                content: '',\n                streaming: true,\n                voiceProcessing: true\n              });\n              this.pendingVoiceAssistantMessageId = assistantMsg.id;\n              this.setStreaming(true);\n            }\n          } else {\n            // assistant: runtype sends a single final; the isFinal=false path\n            // is reserved for delta-streaming providers (future BYO).\n            if (this.pendingVoiceAssistantMessageId) {\n              this.upsertMessage({\n                id: this.pendingVoiceAssistantMessageId,\n                role: 'assistant',\n                content: text,\n                createdAt: new Date().toISOString(),\n                streaming: !isFinal,\n                voiceProcessing: !isFinal\n              });\n            } else {\n              const msg = this.injectMessage({\n                role: 'assistant',\n                content: text,\n                streaming: !isFinal,\n                voiceProcessing: !isFinal\n              });\n              this.pendingVoiceAssistantMessageId = msg.id;\n            }\n\n            if (isFinal) {\n              // The provider plays this reply's audio: mark it spoken so\n              // browser TTS doesn't double-speak when streaming ends. Must run\n              // BEFORE setStreaming(false), which triggers the TTS check.\n              if (this.pendingVoiceAssistantMessageId) {\n                this.ttsSpokenMessageIds.add(this.pendingVoiceAssistantMessageId);\n              }\n              this.setStreaming(false);\n              this.pendingVoiceAssistantMessageId = null;\n            }\n          }\n        });\n      }\n\n      // Surface per-turn latency metrics to the optional config hook.\n      if (this.voiceProvider.onMetrics) {\n        this.voiceProvider.onMetrics((metrics) => {\n          this.config.voiceRecognition?.onMetrics?.(metrics);\n        });\n      }\n\n      this.voiceProvider.onError((error) => {\n        console.error('Voice error:', error);\n\n        // If error occurs while placeholders are pending, update assistant with error text\n        if (this.pendingVoiceAssistantMessageId) {\n          this.upsertMessage({\n            id: this.pendingVoiceAssistantMessageId,\n            role: 'assistant',\n            content: processingErrorText,\n            createdAt: new Date().toISOString(),\n            streaming: false,\n            voiceProcessing: false\n          });\n          this.setStreaming(false);\n          this.pendingVoiceUserMessageId = null;\n          this.pendingVoiceAssistantMessageId = null;\n        }\n      });\n\n      this.voiceProvider.onStatusChange((status) => {\n        this.voiceStatus = status;\n        this.voiceActive = status === 'listening';\n        this.callbacks.onVoiceStatusChanged?.(status);\n      });\n\n      this.voiceProvider.connect();\n\n    } catch (error) {\n      console.error('Failed to setup voice:', error);\n    }\n  }\n\n  /**\n   * Toggle voice recognition on/off\n   */\n  public async toggleVoice() {\n    if (!this.voiceProvider) {\n      console.error('Voice not configured');\n      return;\n    }\n\n    if (this.voiceActive) {\n      await this.voiceProvider.stopListening();\n    } else {\n      // Stop any in-progress TTS so the mic doesn't pick it up\n      this.stopSpeaking();\n      try {\n        await this.voiceProvider.startListening();\n      } catch (error) {\n        console.error('Failed to start voice:', error);\n      }\n    }\n  }\n\n  /**\n   * Cleanup voice resources\n   */\n  public cleanupVoice() {\n    if (this.voiceProvider) {\n      this.voiceProvider.disconnect();\n      this.voiceProvider = null;\n    }\n    this.voiceActive = false;\n    this.voiceStatus = 'disconnected';\n  }\n\n  /**\n   * Extract voice configuration from widget config\n   */\n  private getVoiceConfigFromConfig(): VoiceConfig | undefined {\n    if (!this.config.voiceRecognition?.provider) {\n      return undefined;\n    }\n    \n    const providerConfig = this.config.voiceRecognition.provider;\n    \n    switch (providerConfig.type) {\n      case 'runtype':\n        return {\n          type: 'runtype',\n          runtype: {\n            agentId: providerConfig.runtype?.agentId ?? this.config.agentId ?? '',\n            // Default credentials/endpoint from the widget config so the minimum\n            // voice config collapses to just `agentId`.\n            clientToken: providerConfig.runtype?.clientToken ?? this.config.clientToken,\n            host: providerConfig.runtype?.host ?? this.config.apiUrl,\n            voiceId: providerConfig.runtype?.voiceId,\n            createPlaybackEngine: providerConfig.runtype?.createPlaybackEngine\n          }\n        };\n      \n      case 'browser':\n        return {\n          type: 'browser',\n          browser: {\n            language: providerConfig.browser?.language || 'en-US',\n            continuous: providerConfig.browser?.continuous\n          }\n        };\n\n      case 'custom':\n        // Bring-your-own provider: pass the instance/factory straight through\n        // to the factory, which resolves and validates it.\n        return {\n          type: 'custom',\n          custom: providerConfig.custom\n        };\n\n      default:\n        return undefined;\n    }\n  }\n\n  /**\n   * Initialize the client session (for client token mode).\n   * This is called automatically on first message, but can be called\n   * explicitly to pre-initialize the session and get config from server.\n   */\n  public async initClientSession(): Promise<ClientSession | null> {\n    if (!this.isClientTokenMode()) {\n      return null;\n    }\n    \n    try {\n      const session = await this.client.initSession();\n      this.setClientSession(session);\n      return session;\n    } catch (error) {\n      this.callbacks.onError?.(\n        error instanceof Error ? error : new Error(String(error))\n      );\n      return null;\n    }\n  }\n\n  /**\n   * Set the client session after initialization\n   */\n  public setClientSession(session: ClientSession): void {\n    this.clientSession = session;\n    \n    // Optionally add welcome message from session config\n    if (session.config.welcomeMessage && this.messages.length === 0) {\n      const welcomeMessage: AgentWidgetMessage = {\n        id: `welcome-${Date.now()}`,\n        role: \"assistant\",\n        content: session.config.welcomeMessage,\n        createdAt: new Date().toISOString(),\n        sequence: this.nextSequence()\n      };\n      this.appendMessage(welcomeMessage);\n    }\n  }\n\n  /**\n   * Get current client session\n   */\n  public getClientSession(): ClientSession | null {\n    return this.clientSession ?? this.client.getClientSession();\n  }\n\n  /**\n   * Check if session is valid and not expired\n   */\n  public isSessionValid(): boolean {\n    const session = this.getClientSession();\n    if (!session) return false;\n    return new Date() < session.expiresAt;\n  }\n\n  /**\n   * Clear session (on expiry or error)\n   */\n  public clearClientSession(): void {\n    this.clientSession = null;\n    this.client.clearClientSession();\n  }\n\n  /**\n   * Get the underlying client instance (for advanced use cases like feedback)\n   */\n  public getClient(): AgentWidgetClient {\n    return this.client;\n  }\n\n  /**\n   * Submit message feedback (upvote, downvote, or copy) to the API.\n   * Only available in client token mode.\n   * \n   * @param messageId - The ID of the message to provide feedback for\n   * @param type - The feedback type: 'upvote', 'downvote', or 'copy'\n   */\n  public async submitMessageFeedback(\n    messageId: string,\n    type: 'upvote' | 'downvote' | 'copy'\n  ): Promise<void> {\n    return this.client.submitMessageFeedback(messageId, type);\n  }\n\n  /**\n   * Submit CSAT (Customer Satisfaction) feedback to the API.\n   * Only available in client token mode.\n   * \n   * @param rating - Rating from 1 to 5\n   * @param comment - Optional comment\n   */\n  public async submitCSATFeedback(rating: number, comment?: string): Promise<void> {\n    return this.client.submitCSATFeedback(rating, comment);\n  }\n\n  /**\n   * Submit NPS (Net Promoter Score) feedback to the API.\n   * Only available in client token mode.\n   * \n   * @param rating - Rating from 0 to 10\n   * @param comment - Optional comment\n   */\n  public async submitNPSFeedback(rating: number, comment?: string): Promise<void> {\n    return this.client.submitNPSFeedback(rating, comment);\n  }\n\n  public updateConfig(next: AgentWidgetConfig) {\n    const merged = { ...this.config, ...next };\n\n    // Connection/request-shaping change (apiUrl, clientToken, webmcp, headers,\n    // parser, agent, …) → full client rebuild. UI-only change (theme, copy,\n    // layout, suggestions, …) → refresh in place so the live stream, WebMCP\n    // bridge, and any in-flight resolve survive. The latter is what makes a\n    // self-styling widget work: a `webmcp:*` theme tool mutates config and\n    // re-renders mid-turn; recreating the client there would abort the very\n    // turn that's restyling the widget and strand the paused execution.\n    if (!connectionConfigChanged(this.config, merged)) {\n      this.config = merged;\n      this.client.updateConfig(merged);\n      return;\n    }\n\n    // Replacing the client invalidates every in-flight WebMCP resolve, buffered\n    // parallel-await batch, and pending approval bubble tied to the OLD client/\n    // session. Tear them down BEFORE the swap (the new client has no session\n    // yet) so a deferred batch flush or a parked confirm can't fire against the\n    // fresh client: in client-token mode that would POST /resume without a\n    // valid sessionId and strand the paused turn. Mirrors clearMessages' WebMCP\n    // reset; the client swap already abandons any in-flight stream regardless.\n    this.abortWebMcpResolves();\n    this.webMcpInflightKeys.clear();\n    this.webMcpResolvedKeys.clear();\n    const prevSSECallback = this.client.getSSEEventCallback();\n    this.config = merged;\n    this.client = new AgentWidgetClient(this.config);\n    this.wireDefaultWebMcpConfirm();\n    if (prevSSECallback) {\n      this.client.setSSEEventCallback(prevSSECallback);\n    }\n  }\n\n  public getMessages() {\n    return [...this.messages];\n  }\n\n  public getStatus() {\n    return this.status;\n  }\n\n  public isStreaming() {\n    return this.streaming;\n  }\n\n  /**\n   * @deprecated Use injectMessage() instead.\n   * Injects a raw event into the session event handler.\n   */\n  public injectTestEvent(event: AgentWidgetEvent) {\n    this.handleEvent(event);\n  }\n\n  /**\n   * Inject a message into the conversation.\n   * This is the primary API for adding messages programmatically.\n   *\n   * Supports dual-content where the displayed content differs from what the LLM receives.\n   *\n   * @param options - Message injection options including dual-content support\n   * @returns The created message object\n   *\n   * @example\n   * // Same content for user and LLM\n   * session.injectMessage({\n   *   role: 'assistant',\n   *   content: 'Here are the search results...'\n   * });\n   *\n   * @example\n   * // Different content for user and LLM (redaction)\n   * session.injectMessage({\n   *   role: 'assistant',\n   *   content: '**Found 3 products:**\\n- iPhone 15 Pro ($1,199)\\n- iPhone 15 ($999)',\n   *   llmContent: '[Search results: 3 iPhone products, $799-$1199]'\n   * });\n   */\n  public injectMessage(options: InjectMessageOptions): AgentWidgetMessage {\n    const {\n      role,\n      content,\n      llmContent,\n      contentParts,\n      id,\n      createdAt,\n      sequence,\n      streaming = false,\n      voiceProcessing,\n      rawContent\n    } = options;\n\n    // Generate appropriate ID based on role\n    const messageId =\n      id ??\n      (role === \"user\"\n        ? generateUserMessageId()\n        : role === \"assistant\"\n          ? generateAssistantMessageId()\n          : `system-${Date.now()}-${Math.random().toString(16).slice(2)}`);\n\n    const message: AgentWidgetMessage = {\n      id: messageId,\n      role,\n      content,\n      createdAt: createdAt ?? new Date().toISOString(),\n      sequence: sequence ?? this.nextSequence(),\n      streaming,\n      // Only include optional fields if provided\n      ...(llmContent !== undefined && { llmContent }),\n      ...(contentParts !== undefined && { contentParts }),\n      ...(voiceProcessing !== undefined && { voiceProcessing }),\n      ...(rawContent !== undefined && { rawContent })\n    };\n\n    // Use upsert to handle both new messages and updates (streaming)\n    this.upsertMessage(message);\n\n    return message;\n  }\n\n  /**\n   * Convenience method for injecting assistant messages.\n   * Role defaults to 'assistant'.\n   *\n   * @example\n   * // Simple assistant message\n   * session.injectAssistantMessage({\n   *   content: 'Here are your search results...'\n   * });\n   *\n   * @example\n   * // With redacted LLM content\n   * session.injectAssistantMessage({\n   *   content: 'Full product details for the user...',\n   *   llmContent: '[Product details summary]'\n   * });\n   */\n  public injectAssistantMessage(\n    options: InjectAssistantMessageOptions\n  ): AgentWidgetMessage {\n    return this.injectMessage({ ...options, role: \"assistant\" });\n  }\n\n  /**\n   * Convenience method for injecting user messages.\n   * Role defaults to 'user'.\n   *\n   * @example\n   * session.injectUserMessage({\n   *   content: 'Add iPhone 15 Pro to my cart'\n   * });\n   */\n  public injectUserMessage(\n    options: InjectUserMessageOptions\n  ): AgentWidgetMessage {\n    return this.injectMessage({ ...options, role: \"user\" });\n  }\n\n  /**\n   * Convenience method for injecting system messages.\n   * Role defaults to 'system'.\n   *\n   * @example\n   * // Inject context that guides LLM behavior\n   * session.injectSystemMessage({\n   *   content: '[Context updated]',  // Minimal display\n   *   llmContent: 'User is viewing iPhone 15 Pro. Cart has 2 items.'\n   * });\n   */\n  public injectSystemMessage(\n    options: InjectSystemMessageOptions\n  ): AgentWidgetMessage {\n    return this.injectMessage({ ...options, role: \"system\" });\n  }\n\n  /**\n   * Inject multiple messages in a single batch with one sort and one render pass.\n   */\n  public injectMessageBatch(optionsList: InjectMessageOptions[]): AgentWidgetMessage[] {\n    const results: AgentWidgetMessage[] = [];\n\n    for (const options of optionsList) {\n      const {\n        role,\n        content,\n        llmContent,\n        contentParts,\n        id,\n        createdAt,\n        sequence,\n        streaming = false,\n        voiceProcessing,\n        rawContent\n      } = options;\n\n      const messageId =\n        id ??\n        (role === \"user\"\n          ? generateUserMessageId()\n          : role === \"assistant\"\n            ? generateAssistantMessageId()\n            : `system-${Date.now()}-${Math.random().toString(16).slice(2)}`);\n\n      const message: AgentWidgetMessage = {\n        id: messageId,\n        role,\n        content,\n        createdAt: createdAt ?? new Date().toISOString(),\n        sequence: sequence ?? this.nextSequence(),\n        streaming,\n        ...(llmContent !== undefined && { llmContent }),\n        ...(contentParts !== undefined && { contentParts }),\n        ...(voiceProcessing !== undefined && { voiceProcessing }),\n        ...(rawContent !== undefined && { rawContent })\n      };\n\n      results.push(message);\n    }\n\n    // Add all messages, sort once, notify once\n    this.messages = this.sortMessages([...this.messages, ...results]);\n    this.callbacks.onMessagesChanged([...this.messages]);\n\n    return results;\n  }\n\n  /**\n   * Convenience method for injecting a registered component directive as\n   * an assistant message: the same shape Persona produces from a streamed\n   * `{ \"text\": \"...\", \"component\": \"...\", \"props\": {...} }` payload.\n   *\n   * Sets `content` to `text`, `rawContent` to the JSON directive (so\n   * `extractComponentDirectiveFromMessage` can find it), and forwards\n   * `llmContent` / `id` / `createdAt` / `sequence`.\n   *\n   * @example\n   * session.injectComponentDirective({\n   *   component: \"DynamicForm\",\n   *   props: { title: \"Book a demo\", fields: [...] },\n   *   text: \"Share your details to book a demo.\",\n   *   llmContent: \"[Showed booking form]\"\n   * });\n   */\n  public injectComponentDirective(\n    options: InjectComponentDirectiveOptions\n  ): AgentWidgetMessage {\n    const {\n      component,\n      props = {},\n      text = \"\",\n      llmContent,\n      id,\n      createdAt,\n      sequence\n    } = options;\n\n    const directive: { text: string; component: string; props: Record<string, unknown> } = {\n      text,\n      component,\n      props\n    };\n\n    return this.injectMessage({\n      role: \"assistant\",\n      content: text,\n      rawContent: JSON.stringify(directive),\n      ...(llmContent !== undefined && { llmContent }),\n      ...(id !== undefined && { id }),\n      ...(createdAt !== undefined && { createdAt }),\n      ...(sequence !== undefined && { sequence })\n    });\n  }\n\n  public async sendMessage(\n    rawInput: string,\n    options?: {\n      viaVoice?: boolean;\n      /** Multi-modal content parts (e.g., images) to include with the message */\n      contentParts?: ContentPart[];\n    }\n  ) {\n    const input = rawInput.trim();\n    // Allow sending if there's text OR attachments\n    if (!input && (!options?.contentParts || options.contentParts.length === 0)) return;\n\n    this.stopSpeaking();\n    this.abortController?.abort();\n    // A new user turn supersedes any in-flight WebMCP resolve from the prior\n    // turn. Tear them down here (they own controllers separate from the shared\n    // one) so a lingering resolve can't race the new dispatch or post a stale\n    // /resume against a superseded execution.\n    this.abortWebMcpResolves();\n\n    // Generate IDs for both user message and expected assistant response\n    const userMessageId = generateUserMessageId();\n    const assistantMessageId = generateAssistantMessageId();\n\n    const userMessage: AgentWidgetMessage = {\n      id: userMessageId,\n      role: \"user\",\n      content: input || IMAGE_ONLY_MESSAGE_FALLBACK_TEXT, // Display text (fallback if only images)\n      createdAt: new Date().toISOString(),\n      sequence: this.nextSequence(),\n      viaVoice: options?.viaVoice || false,\n      // Include contentParts if provided (for multi-modal messages)\n      ...(options?.contentParts && options.contentParts.length > 0 && {\n        contentParts: options.contentParts\n      })\n    };\n\n    this.appendMessage(userMessage);\n    this.setStreaming(true);\n\n    const controller = new AbortController();\n    this.abortController = controller;\n\n    const snapshot = [...this.messages];\n\n    try {\n      await this.client.dispatch(\n        {\n          messages: snapshot,\n          signal: controller.signal,\n          assistantMessageId // Pass expected assistant message ID for tracking\n        },\n        this.handleEvent\n      );\n    } catch (error) {\n      // Check if this is an abort error (user canceled, navigated away, etc.)\n      // In these cases, don't show fallback - the request was intentionally interrupted\n      const isAbortError =\n        error instanceof Error &&\n        (error.name === 'AbortError' ||\n         error.message.includes('aborted') ||\n         error.message.includes('abort'));\n\n      if (!isAbortError) {\n        const content = buildDispatchErrorContent(\n          error,\n          this.config.errorMessage\n        );\n        // An override that returns \"\" suppresses the fallback bubble entirely\n        // (onError still fires below).\n        if (content) {\n          const fallback: AgentWidgetMessage = {\n            id: assistantMessageId, // Use the pre-generated ID for fallback too\n            role: \"assistant\",\n            createdAt: new Date().toISOString(),\n            content,\n            sequence: this.nextSequence()\n          };\n\n          this.appendMessage(fallback);\n        }\n      }\n\n      this.setStatus(\"idle\");\n      this.setStreaming(false);\n      this.abortController = null;\n\n      if (!isAbortError) {\n        if (error instanceof Error) {\n          this.callbacks.onError?.(error);\n        } else {\n          this.callbacks.onError?.(new Error(String(error)));\n        }\n      }\n    }\n  }\n\n  /**\n   * Continue the conversation without adding a new user message.\n   * Triggers the model to respond based on the current conversation state.\n   *\n   * Use this for automatic continuation after action handlers inject data\n   * (e.g., search results) that the model should analyze.\n   *\n   * @example\n   * // After injecting search results, trigger model to analyze them\n   * session.injectAssistantMessage({ content: 'Found 5 products...' });\n   * session.continueConversation();\n   */\n  public async continueConversation() {\n    // Don't continue if already streaming\n    if (this.streaming) return;\n\n    this.abortController?.abort();\n\n    const assistantMessageId = generateAssistantMessageId();\n\n    this.setStreaming(true);\n\n    const controller = new AbortController();\n    this.abortController = controller;\n\n    const snapshot = [...this.messages];\n\n    try {\n      await this.client.dispatch(\n        {\n          messages: snapshot,\n          signal: controller.signal,\n          assistantMessageId\n        },\n        this.handleEvent\n      );\n    } catch (error) {\n      // Check if this is an abort error (a prior in-flight stream was canceled,\n      // the user navigated away, etc.). In these cases, don't show fallback or\n      // fire onError - the request was intentionally interrupted.\n      const isAbortError =\n        error instanceof Error &&\n        (error.name === 'AbortError' ||\n         error.message.includes('aborted') ||\n         error.message.includes('abort'));\n\n      if (!isAbortError) {\n        const content = buildDispatchErrorContent(\n          error,\n          this.config.errorMessage\n        );\n        // An override that returns \"\" suppresses the fallback bubble entirely\n        // (onError still fires below).\n        if (content) {\n          const fallback: AgentWidgetMessage = {\n            id: assistantMessageId,\n            role: \"assistant\",\n            createdAt: new Date().toISOString(),\n            content,\n            sequence: this.nextSequence()\n          };\n\n          this.appendMessage(fallback);\n        }\n      }\n      this.setStatus(\"idle\");\n      this.setStreaming(false);\n      this.abortController = null;\n      if (!isAbortError) {\n        if (error instanceof Error) {\n          this.callbacks.onError?.(error);\n        } else {\n          this.callbacks.onError?.(new Error(String(error)));\n        }\n      }\n    }\n  }\n\n  /**\n   * Connect an external SSE stream (e.g. from an approval endpoint) and\n   * process it through the SDK's native event pipeline.\n   */\n  public async connectStream(\n    stream: ReadableStream<Uint8Array>,\n    options?: { assistantMessageId?: string; allowReentry?: boolean }\n  ): Promise<void> {\n    if (this.streaming && !options?.allowReentry) return;\n    if (!options?.allowReentry) {\n      this.abortController?.abort();\n    }\n\n    // Finalize any stale streaming messages from the previous stream\n    // (e.g., tool messages interrupted by approval pause)\n    let hasStale = false;\n    for (const msg of this.messages) {\n      if (msg.streaming) {\n        msg.streaming = false;\n        hasStale = true;\n      }\n    }\n    if (hasStale) {\n      this.callbacks.onMessagesChanged([...this.messages]);\n    }\n\n    this.setStreaming(true);\n\n    try {\n      await this.client.processStream(\n        stream,\n        this.handleEvent,\n        options?.assistantMessageId\n      );\n    } catch (error) {\n      this.setStatus(\"error\");\n      // Mirror the idle/error handlers: a failed resume stream must not tear\n      // down streaming/abortController while another WebMCP resolve is still\n      // confirming or executing. The in-flight resolve's `finally` owns the\n      // teardown once `webMcpResolveControllers` drains.\n      if (this.webMcpResolveControllers.size === 0) {\n        this.setStreaming(false);\n        this.abortController = null;\n      }\n      this.callbacks.onError?.(\n        error instanceof Error ? error : new Error(String(error))\n      );\n    }\n  }\n\n  /**\n   * Install the native approval-bubble confirm handler on the WebMCP bridge\n   * when the integrator hasn't supplied a custom `webmcp.onConfirm`. Without\n   * this, the bridge falls back to a blunt `window.confirm`. Safe to call\n   * repeatedly (e.g. after the client is re-created in `updateConfig`).\n   */\n  private wireDefaultWebMcpConfirm(): void {\n    const webmcp = this.config.webmcp;\n    if (webmcp?.enabled === true && !webmcp.onConfirm) {\n      this.client.setWebMcpConfirmHandler((info) =>\n        this.requestWebMcpApproval(info)\n      );\n    }\n  }\n\n  /**\n   * Default WebMCP confirm gate: render Persona's native in-panel approval\n   * bubble and resolve when the user clicks Approve/Deny. Returns immediately\n   * with `true` when `webmcp.autoApprove(info)` opts the tool out of the gate\n   * (e.g. a read-only catalog search), so no bubble is shown. The bridge\n   * awaits this Promise before executing the page tool.\n   */\n  public requestWebMcpApproval(info: WebMcpConfirmInfo): Promise<boolean> {\n    // Per-tool policy hook: auto-allow opted-out tools without any UI. A\n    // throwing predicate must not block the call, so fall through to an\n    // explicit gate on error.\n    try {\n      if (this.config.webmcp?.autoApprove?.(info) === true) {\n        return Promise.resolve(true);\n      }\n    } catch {\n      // fall through to explicit approval\n    }\n\n    const approval: AgentWidgetApproval = {\n      id: `webmcp-${++this.webMcpApprovalSeq}`,\n      status: \"pending\",\n      agentId: \"\",\n      executionId: \"\",\n      toolName: info.toolName,\n      toolType: \"webmcp\",\n      description:\n        info.description ?? `Allow the assistant to run ${info.toolName}?`,\n      parameters: info.args,\n    };\n    const approvalMessageId = `approval-${approval.id}`;\n\n    this.upsertMessage({\n      id: approvalMessageId,\n      role: \"assistant\",\n      content: \"\",\n      createdAt: new Date().toISOString(),\n      streaming: false,\n      variant: \"approval\",\n      approval,\n    });\n\n    return new Promise<boolean>((resolve) => {\n      this.webMcpApprovalResolvers.set(approvalMessageId, resolve);\n    });\n  }\n\n  /**\n   * Resolve a pending WebMCP approval bubble (from the Approve/Deny click in\n   * `ui.ts`). Updates the bubble to its resolved state and unblocks the\n   * bridge Promise parked in `requestWebMcpApproval`. No-op if already\n   * resolved (double-click, re-render).\n   */\n  public resolveWebMcpApproval(\n    approvalMessageId: string,\n    decision: \"approved\" | \"denied\"\n  ): void {\n    const resolve = this.webMcpApprovalResolvers.get(approvalMessageId);\n    if (!resolve) return;\n    this.webMcpApprovalResolvers.delete(approvalMessageId);\n\n    const existing = this.messages.find((m) => m.id === approvalMessageId);\n    if (existing?.approval) {\n      this.upsertMessage({\n        ...existing,\n        approval: {\n          ...existing.approval,\n          status: decision,\n          resolvedAt: Date.now(),\n        },\n      });\n    }\n\n    resolve(decision === \"approved\");\n  }\n\n  /**\n   * Resolve a tool approval request (approve or deny).\n   * Updates the approval message status, calls the API (or custom onDecision),\n   * and pipes the response stream through connectStream().\n   */\n  public async resolveApproval(\n    approval: AgentWidgetApproval,\n    decision: 'approved' | 'denied',\n    options?: AgentWidgetApprovalDecisionOptions\n  ): Promise<void> {\n    // 1. Update approval message status immediately for responsive UI\n    const approvalMessageId = `approval-${approval.id}`;\n    const updatedApproval: AgentWidgetApproval = {\n      ...approval,\n      status: decision,\n      resolvedAt: Date.now(),\n    };\n    // Anchor the bubble where the agent paused for permission. An approval is a\n    // timeline checkpoint, not a \"now\" event, so resolving it must preserve the\n    // original message's createdAt/sequence: otherwise sortMessages (which\n    // orders by createdAt first) would re-stamp it to now and float it past any\n    // message created later (e.g. a long-pending approval resolved after more\n    // conversation, or restored/replayed transcripts).\n    const existing = this.messages.find((m) => m.id === approvalMessageId);\n    const updatedMessage: AgentWidgetMessage = {\n      id: approvalMessageId,\n      role: \"assistant\",\n      content: \"\",\n      createdAt: existing?.createdAt ?? new Date().toISOString(),\n      ...(existing?.sequence !== undefined ? { sequence: existing.sequence } : {}),\n      streaming: false,\n      variant: \"approval\",\n      approval: updatedApproval,\n    };\n    this.upsertMessage(updatedMessage);\n\n    // Show the standalone typing indicator immediately while we wait for the\n    // approval round-trip. Install an abortController so cancel() works during\n    // the silent gap. See `resolveAskUserQuestion` for the same pattern.\n    this.abortController?.abort();\n    this.abortController = new AbortController();\n    this.setStreaming(true);\n\n    // 2. Call onDecision callback if provided, otherwise use client.resolveApproval()\n    const approvalConfig = this.config.approval;\n    const onDecision = approvalConfig && typeof approvalConfig === 'object' ? approvalConfig.onDecision : undefined;\n\n    try {\n      let response: Response | ReadableStream<Uint8Array> | void;\n\n      if (onDecision) {\n        response = await onDecision(\n          {\n            approvalId: approval.id,\n            executionId: approval.executionId,\n            agentId: approval.agentId,\n            toolName: approval.toolName,\n          },\n          decision,\n          options\n        );\n      } else {\n        response = await this.client.resolveApproval(\n          {\n            agentId: approval.agentId,\n            executionId: approval.executionId,\n            approvalId: approval.id,\n          },\n          decision\n        );\n      }\n\n      // 3. Pipe through connectStream if we got a response with a body\n      if (response) {\n        let stream: ReadableStream<Uint8Array> | null = null;\n        if (response instanceof Response) {\n          if (!response.ok) {\n            const errorData = await response.json().catch(() => null);\n            throw new Error(\n              errorData?.error ?? `Approval request failed: ${response.status}`\n            );\n          }\n          stream = response.body;\n        } else if (response instanceof ReadableStream) {\n          stream = response;\n        }\n\n        if (stream) {\n          await this.connectStream(stream, { allowReentry: true });\n        } else {\n          if (decision === 'denied') {\n            // No stream body for denied: inject a denial message\n            this.appendMessage({\n              id: `denial-${approval.id}`,\n              role: \"assistant\",\n              content: \"Tool execution was denied by user.\",\n              createdAt: new Date().toISOString(),\n              streaming: false,\n              sequence: this.nextSequence(),\n            });\n          }\n          // No body to pipe: drop the pre-set streaming flag so the indicator\n          // doesn't linger forever.\n          this.setStreaming(false);\n          this.abortController = null;\n        }\n      } else {\n        // onDecision returned void / no response: drop the pre-set flag.\n        this.setStreaming(false);\n        this.abortController = null;\n      }\n    } catch (error) {\n      const isAbortError =\n        error instanceof Error &&\n        (error.name === 'AbortError' ||\n         error.message.includes('aborted') ||\n         error.message.includes('abort'));\n\n      this.setStreaming(false);\n      this.abortController = null;\n\n      if (!isAbortError) {\n        this.callbacks.onError?.(\n          error instanceof Error ? error : new Error(String(error))\n        );\n      }\n    }\n  }\n\n  /**\n   * Resolve a paused `ask_user_question` LOCAL tool call.\n   *\n   * When the server emits `step_await` for `ask_user_question`, the widget\n   * renders the answer-pill sheet and calls this method once the user\n   * picks. Steps:\n   *   1. POST the answer to `/resume` via `client.resumeFlow`.\n   *   2. Pipe the resulting SSE stream through `connectStream()` so the\n   *      paused agent execution continues.\n   *   3. Append a user-visible bubble with the answer text so the\n   *      transcript reads naturally.\n   */\n  /**\n   * Persist in-progress answers and the current page index for a multi-question\n   * `ask_user_question` payload, so a refresh resumes on the same page with\n   * prior answers intact. Called by ui.ts on every Back/Next/pick interaction.\n   */\n  public persistAskUserQuestionProgress(\n    toolMessage: AgentWidgetMessage,\n    progress: {\n      answers: Record<string, string | string[]>;\n      currentIndex: number;\n    }\n  ): void {\n    const current = this.messages.find((m) => m.id === toolMessage.id);\n    if (!current) return;\n    this.upsertMessage({\n      ...current,\n      agentMetadata: {\n        ...current.agentMetadata,\n        askUserQuestionAnswers: progress.answers,\n        askUserQuestionIndex: progress.currentIndex,\n      },\n    });\n  }\n\n  /**\n   * Flip an `ask_user_question` tool message from awaiting → answered so\n   * render passes stop re-mounting its answer-pill sheet. Idempotent.\n   * When `answers` is provided, persists the full structured answer Record\n   * atomically with the answered flag: guarding against later events that\n   * could re-emit the tool message and clobber the per-pick persisted\n   * answers via top-level merge.\n   */\n  public markAskUserQuestionResolved(\n    toolMessage: AgentWidgetMessage,\n    answers?: Record<string, string | string[]>\n  ): void {\n    const current = this.messages.find((m) => m.id === toolMessage.id);\n    if (!current) return;\n    this.upsertMessage({\n      ...current,\n      agentMetadata: {\n        ...current.agentMetadata,\n        awaitingLocalTool: false,\n        askUserQuestionAnswered: true,\n        ...(answers ? { askUserQuestionAnswers: answers } : {}),\n      },\n    });\n  }\n\n  public async resolveAskUserQuestion(\n    toolMessage: AgentWidgetMessage,\n    answer: string | Record<string, string | string[]>\n  ): Promise<void> {\n    // Idempotent: guards against rapid double-clicks on answer pills before\n    // the re-render swaps the card to its collapsed/answered state.\n    const live = this.messages.find((m) => m.id === toolMessage.id);\n    if (live?.agentMetadata?.askUserQuestionAnswered === true) return;\n\n    const executionId = toolMessage.agentMetadata?.executionId;\n    const toolName = toolMessage.toolCall?.name;\n    if (!executionId || !toolName) {\n      this.callbacks.onError?.(\n        new Error(\n          \"resolveAskUserQuestion: message is missing executionId or toolCall.name\"\n        )\n      );\n      return;\n    }\n\n    // Flip answered flag first so the next render skips the sheet re-mount,\n    // avoiding the race between removeAskUserQuestionSheet's 180ms slide-out\n    // timer and the renders that fire as the resume stream lands. Pass the\n    // structured answer Record (when present) so it's atomically persisted\n    // alongside the flag: the answered-state review card depends on\n    // `agentMetadata.askUserQuestionAnswers` being populated at render time.\n    //\n    // For single-question payloads, callers (built-in pick handler, plugins)\n    // resolve with a plain string. Derive a `{ [questionText]: answer }` Record\n    // from the toolCall args so the answered-card render path is consistent\n    // with grouped flows.\n    let structuredAnswers: Record<string, string | string[]> | undefined =\n      typeof answer === \"string\" ? undefined : answer;\n    if (structuredAnswers === undefined && typeof answer === \"string\") {\n      const args = toolMessage.toolCall?.args as\n        | { questions?: Array<{ question?: unknown }> }\n        | undefined;\n      const questions = Array.isArray(args?.questions) ? args!.questions : [];\n      if (questions.length === 1) {\n        const qText = typeof questions[0]?.question === \"string\"\n          ? (questions[0].question as string)\n          : \"\";\n        if (qText) structuredAnswers = { [qText]: answer };\n      }\n    }\n    this.markAskUserQuestionResolved(toolMessage, structuredAnswers);\n\n    // Show the standalone typing indicator immediately: the network round-trip\n    // to /resume is otherwise silent, which reads as broken. The render\n    // condition in ui.ts already shows the indicator once streaming flips true\n    // and the last message is a user bubble (the answer we inject below).\n    // Install an abortController so cancel() works during this silent gap.\n    this.abortController?.abort();\n    this.abortController = new AbortController();\n    this.setStreaming(true);\n\n    // Inject Q→A pair messages: one assistant bubble per question, one user\n    // bubble per answer, so the transcript reads like a normal conversation.\n    // The original ask_user_question tool message is suppressed by the\n    // renderer once `askUserQuestionAnswered` is true. Skipped questions get\n    // a muted italic `*Skipped*` user bubble (rendered through the standard\n    // markdown pipeline).\n    const toolCallId = toolMessage.toolCall!.id;\n    const args = toolMessage.toolCall?.args as\n      | { questions?: Array<{ question?: unknown; header?: unknown }> }\n      | undefined;\n    const questions = Array.isArray(args?.questions) ? args!.questions : [];\n    if (questions.length === 0) {\n      const fallback =\n        typeof answer === \"string\"\n          ? answer\n          : Object.entries(answer)\n              .map(\n                ([q, v]) => `${q}: ${Array.isArray(v) ? v.join(\", \") : v}`\n              )\n              .join(\" | \");\n      this.appendMessage({\n        id: `ask-user-answer-${toolCallId}`,\n        role: \"user\",\n        content: fallback,\n        createdAt: new Date().toISOString(),\n        streaming: false,\n        sequence: this.nextSequence(),\n      });\n    } else {\n      const stored = structuredAnswers ?? {};\n      questions.forEach((p, i) => {\n        const qText = typeof p?.question === \"string\" ? p.question : \"\";\n        if (!qText) return;\n        const ans = stored[qText];\n        const answerStr = Array.isArray(ans)\n          ? ans.join(\", \")\n          : typeof ans === \"string\"\n            ? ans\n            : \"\";\n        this.appendMessage({\n          id: `ask-user-q-${toolCallId}-${i}`,\n          role: \"assistant\",\n          content: qText,\n          createdAt: new Date().toISOString(),\n          streaming: false,\n          sequence: this.nextSequence(),\n        });\n        this.appendMessage({\n          id: `ask-user-a-${toolCallId}-${i}`,\n          role: \"user\",\n          content: answerStr || \"*Skipped*\",\n          createdAt: new Date().toISOString(),\n          streaming: false,\n          sequence: this.nextSequence(),\n        });\n      });\n    }\n\n    try {\n      const response = await this.client.resumeFlow(executionId, {\n        [toolName]: answer,\n      });\n\n      if (!response.ok) {\n        const errorData = await response.json().catch(() => null);\n        throw new Error(\n          errorData?.error ?? `Resume failed: ${response.status}`\n        );\n      }\n\n      if (response.body) {\n        await this.connectStream(response.body, { allowReentry: true });\n      } else {\n        // No body to pipe: drop the pre-set streaming flag so the indicator\n        // doesn't linger forever.\n        this.setStreaming(false);\n        this.abortController = null;\n      }\n    } catch (error) {\n      // Mirror sendMessage: a cancel() during the await aborts the controller\n      // and surfaces an AbortError: don't treat that as a real failure.\n      const isAbortError =\n        error instanceof Error &&\n        (error.name === 'AbortError' ||\n         error.message.includes('aborted') ||\n         error.message.includes('abort'));\n\n      this.setStreaming(false);\n      this.abortController = null;\n\n      if (!isAbortError) {\n        this.callbacks.onError?.(\n          error instanceof Error ? error : new Error(String(error))\n        );\n      }\n    }\n  }\n\n  /**\n   * Collect an auto-resolving LOCAL-tool `step_await` (`webmcp:*` page tools\n   * and the built-in `suggest_replies`) into a per-executionId batch\n   * and schedule a single deferred flush. Parallel calls (core#3878) emit\n   * several `step_await`s for ONE paused execution within the same stream tick;\n   * buffering them and flushing once lets us post ONE `/resume` keyed by the\n   * per-call `webMcpToolCallId` rather than racing N name-keyed resumes on the\n   * same execution (which 404'd on the second and hung the turn).\n   *\n   * Deferred via `queueMicrotask` (epoch-guarded) for the same reason the old\n   * direct resolve was: handleEvent must return first so the dispatch's\n   * `connectStream` sees end-of-stream and releases the shared abortController\n   * before a resolve grabs it.\n   *\n   * Awaits without an `executionId` or `toolCall.id` can't be batched (no key)\n   *: route them straight to the single-call path, which surfaces the malformed\n   * wire shape via `onError` / an `isError` resume.\n   */\n  private enqueueWebMcpAwait(toolMessage: AgentWidgetMessage): void {\n    const executionId = toolMessage.agentMetadata?.executionId;\n    const callId = toolMessage.toolCall?.id;\n    if (!executionId || !callId) {\n      const queuedEpoch = this.webMcpEpoch;\n      queueMicrotask(() => {\n        if (queuedEpoch !== this.webMcpEpoch) return;\n        void this.resolveWebMcpToolCall(toolMessage);\n      });\n      return;\n    }\n\n    let batch = this.webMcpAwaitBatches.get(executionId);\n    if (!batch) {\n      batch = { snapshots: [], seen: new Set() };\n      this.webMcpAwaitBatches.set(executionId, batch);\n    }\n    // Duplicate step_await re-emit for a call already in this batch: ignore.\n    if (batch.seen.has(callId)) return;\n    batch.seen.add(callId);\n    batch.snapshots.push(toolMessage);\n    // NB: no flush is scheduled here. Flushing happens once the stream that is\n    // delivering these awaits ENDS (handleEvent's `status: idle` →\n    // scheduleWebMcpBatchFlush). Flushing per-await on the next microtask would\n    // race SSE chunk boundaries: two PARALLEL step_awaits split across separate\n    // `read()` chunks would flush the first alone and post a partial resume.\n    // Waiting for stream end guarantees every parallel await is collected first.\n  }\n\n  /**\n   * Flush every buffered local-tool await batch, one `/resume` per executionId.\n   * Called once a stream ends (`status: idle` / `error`): by then all parallel\n   * `step_await`s the stream carried have been collected, even if split across\n   * SSE chunks. Deferred via `queueMicrotask` (epoch-guarded) so the idle\n   * handler returns first and the stream's end-of-stream teardown (streaming /\n   * abortController) settles before a resolve grabs them: the same ordering the\n   * single-call resolve always relied on.\n   */\n  private scheduleWebMcpBatchFlush(): void {\n    if (this.webMcpAwaitBatches.size === 0) return;\n    const queuedEpoch = this.webMcpEpoch;\n    queueMicrotask(() => {\n      if (queuedEpoch !== this.webMcpEpoch) return;\n      for (const executionId of [...this.webMcpAwaitBatches.keys()]) {\n        this.flushWebMcpAwaitBatch(executionId);\n      }\n    });\n  }\n\n  /**\n   * Run a buffered batch of local-tool awaits for one executionId. Size 1\n   * (single call, or distinct-tool turns that happened to arrive alone) takes\n   * the original single-call path; size >1 (parallel calls) takes the batched\n   * path that posts ONE `/resume`. The batch is removed from the map up front\n   * so any later sibling re-emit (e.g. from a re-pause) forms a fresh batch\n   * rather than mutating one already in flight.\n   */\n  private flushWebMcpAwaitBatch(executionId: string): void {\n    const batch = this.webMcpAwaitBatches.get(executionId);\n    if (!batch) return;\n    this.webMcpAwaitBatches.delete(executionId);\n    const { snapshots } = batch;\n    if (snapshots.length === 1) {\n      void this.resolveWebMcpToolCall(snapshots[0]);\n    } else if (snapshots.length > 1) {\n      void this.resolveWebMcpToolCallBatch(executionId, snapshots);\n    }\n  }\n\n  private resolveWebMcpToolStartedAt(\n    toolMessage: AgentWidgetMessage,\n  ): number {\n    const stored = this.messages.find((m) => m.id === toolMessage.id);\n    const candidates = [\n      stored?.toolCall?.startedAt,\n      toolMessage.toolCall?.startedAt,\n    ];\n    for (const candidate of candidates) {\n      if (typeof candidate === \"number\" && Number.isFinite(candidate)) {\n        return candidate;\n      }\n    }\n    return Date.now();\n  }\n\n  /**\n   * Persisted-resolution guard for `suggest_replies`. The in-memory dedupe\n   * sets (`webMcpInflightKeys` / `webMcpResolvedKeys`) are cleared by\n   * hydrateMessages/clearMessages/cancel, but `suggestRepliesResolved`\n   * survives on the stored message, so a stale `step_await` re-emit after a\n   * hydration must not re-POST `/resume` for an already-resolved call (the\n   * historical double-resume failure mode the batching work exists to avoid).\n   * Checks the LIVE message first; the handleEvent snapshot is a fresh wire\n   * skeleton whose metadata never carries the flag.\n   */\n  private isSuggestRepliesAlreadyResolved(\n    toolMessage: AgentWidgetMessage,\n  ): boolean {\n    if (toolMessage.toolCall?.name !== SUGGEST_REPLIES_TOOL_NAME) return false;\n    const stored = this.messages.find((m) => m.id === toolMessage.id);\n    return (\n      (stored ?? toolMessage).agentMetadata?.suggestRepliesResolved === true\n    );\n  }\n\n  private markWebMcpToolRunning(\n    toolMessage: AgentWidgetMessage,\n  ): number {\n    const startedAt = this.resolveWebMcpToolStartedAt(toolMessage);\n    this.upsertMessage({\n      ...toolMessage,\n      streaming: true,\n      agentMetadata: {\n        ...toolMessage.agentMetadata,\n        awaitingLocalTool: false,\n      },\n      toolCall: toolMessage.toolCall\n        ? {\n            ...toolMessage.toolCall,\n            status: \"running\",\n            startedAt,\n            completedAt: undefined,\n            duration: undefined,\n            durationMs: undefined,\n          }\n        : toolMessage.toolCall,\n    });\n    return startedAt;\n  }\n\n  private markWebMcpToolComplete(\n    toolMessage: AgentWidgetMessage,\n    result: unknown,\n    startedAt: number,\n    completedAt = Date.now(),\n    extraMetadata?: Partial<\n      NonNullable<AgentWidgetMessage[\"agentMetadata\"]>\n    >,\n  ): void {\n    // A teardown such as clearMessages()/hydrateMessages()/new send can remove\n    // the bubble while an aborted WebMCP promise is settling. Never resurrect a\n    // cleared message just to mark the old resolve complete.\n    if (!this.messages.some((message) => message.id === toolMessage.id)) return;\n    this.upsertMessage({\n      ...toolMessage,\n      streaming: false,\n      agentMetadata: {\n        ...toolMessage.agentMetadata,\n        awaitingLocalTool: false,\n        ...extraMetadata,\n      },\n      toolCall: toolMessage.toolCall\n        ? {\n            ...toolMessage.toolCall,\n            status: \"complete\",\n            result,\n            startedAt,\n            completedAt,\n            duration: undefined,\n            durationMs: Math.max(0, completedAt - startedAt),\n          }\n        : toolMessage.toolCall,\n    });\n  }\n\n  /**\n   * Resolve TWO OR MORE parallel local-tool awaits sharing one paused\n   * executionId with a SINGLE `/resume` (core#3878). Each call is executed\n   * against the page registry concurrently: every gated call renders its own\n   * native approval bubble, and a sibling's confirm Promise never blocks\n   * another's execution. Outputs are keyed by per-call `webMcpToolCallId`\n   * (server prefers it over tool name; name-keying remains the fallback for\n   * legacy single/distinct-tool turns), so two calls to the SAME tool no longer\n   * collide. The server is tolerant: any call we omit (declined-after-abort,\n   * dedupe, exec failure) simply re-pauses and is retried on its re-emit.\n   *\n   * Mirrors `resolveWebMcpToolCall`'s dedupe / abort / streaming machinery, but\n   * shares one resume POST and marks every resolved key on that POST's HTTP OK.\n   */\n  private async resolveWebMcpToolCallBatch(\n    executionId: string,\n    snapshots: AgentWidgetMessage[],\n  ): Promise<void> {\n    type ExecutedWebMcpTool = {\n      dedupeKey: string;\n      resumeKey: string;\n      output: unknown;\n      toolMessage: AgentWidgetMessage;\n      startedAt: number;\n      completedAt: number;\n    };\n    const claimedKeys: string[] = [];\n    const controllers: AbortController[] = [];\n    // Dedicated controller for the shared resume fetch so cancel() can abort it\n    // alongside the per-call ones (all live in webMcpResolveControllers).\n    const resumeController = new AbortController();\n    this.webMcpResolveControllers.add(resumeController);\n    this.setStreaming(true);\n\n    // Phase 1: execute every pending call concurrently. A null result means\n    // the call was deduped, aborted, or threw; it's omitted from the resume and\n    // (per the tolerant server) re-pauses for retry.\n    const executed = await Promise.all(\n      snapshots.map(async (toolMessage) => {\n        const wireToolName = toolMessage.toolCall?.name;\n        const callId = toolMessage.toolCall?.id;\n        if (!wireToolName || !callId) return null;\n\n        const dedupeKey = `${executionId}:${callId}`;\n        if (\n          this.webMcpInflightKeys.has(dedupeKey) ||\n          this.webMcpResolvedKeys.has(dedupeKey) ||\n          this.isSuggestRepliesAlreadyResolved(toolMessage)\n        ) {\n          return null;\n        }\n        this.webMcpInflightKeys.add(dedupeKey);\n        claimedKeys.push(dedupeKey);\n\n        // Clear the awaiting flag and keep the tool bubble running while the\n        // browser-side WebMCP promise is in flight. The initial `step_await`\n        // only means the server paused for a local tool; it is not completion.\n        const startedAt = this.markWebMcpToolRunning(toolMessage);\n\n        // Per-call id wins for resume keying; fall back to the wire tool name\n        // for legacy servers that don't emit `webMcpToolCallId`.\n        const resumeKey =\n          toolMessage.agentMetadata?.webMcpToolCallId ?? wireToolName;\n\n        // Built-in fire-and-forget tool: no bridge, no confirm gate, no\n        // browser-side execution: the chips render from the message list and\n        // the canned output joins the batch's single /resume.\n        if (wireToolName === SUGGEST_REPLIES_TOOL_NAME) {\n          return {\n            dedupeKey,\n            resumeKey,\n            output: suggestRepliesToolResult(),\n            toolMessage,\n            startedAt,\n            completedAt: Date.now(),\n          };\n        }\n\n        const controller = new AbortController();\n        this.webMcpResolveControllers.add(controller);\n        controllers.push(controller);\n\n        const execPromise = this.client.executeWebMcpToolCall(\n          wireToolName,\n          toolMessage.toolCall?.args,\n          controller.signal,\n        );\n\n        let output: unknown;\n        if (!execPromise) {\n          output = {\n            isError: true,\n            content: [\n              { type: \"text\", text: \"WebMCP not enabled on this widget.\" },\n            ],\n          };\n        } else {\n          try {\n            output = await execPromise;\n          } catch (error) {\n            const isAbortError =\n              error instanceof Error &&\n              (error.name === \"AbortError\" ||\n                error.message.includes(\"aborted\") ||\n                error.message.includes(\"abort\"));\n            if (!isAbortError) {\n              this.callbacks.onError?.(\n                error instanceof Error ? error : new Error(String(error)),\n              );\n            }\n            this.markWebMcpToolComplete(\n              toolMessage,\n              buildWebMcpErrorResult(\n                isAbortError\n                  ? \"Aborted by cancel()\"\n                  : getWebMcpErrorMessage(error),\n              ),\n              startedAt,\n            );\n            // Release the dedupe claim so a re-emit can retry this call.\n            this.webMcpInflightKeys.delete(dedupeKey);\n            return null;\n          }\n        }\n        if (controller.signal.aborted) {\n          this.markWebMcpToolComplete(\n            toolMessage,\n            buildWebMcpErrorResult(\"Aborted by cancel()\"),\n            startedAt,\n          );\n          this.webMcpInflightKeys.delete(dedupeKey);\n          return null;\n        }\n        return {\n          dedupeKey,\n          resumeKey,\n          output,\n          toolMessage,\n          startedAt,\n          completedAt: Date.now(),\n        };\n      }),\n    );\n\n    let ready: ExecutedWebMcpTool[] = [];\n    try {\n      ready = executed.filter((r): r is ExecutedWebMcpTool => r !== null);\n      // Everything deduped/aborted/failed: nothing to post.\n      if (ready.length === 0) return;\n\n      const toolOutputs: Record<string, unknown> = {};\n      for (const r of ready) {\n        // Two omitted-on-collision safety: if two calls somehow resolve to the\n        // same key (only possible on a legacy name fallback), last write wins:        // the server re-pauses the unrepresented call for retry.\n        toolOutputs[r.resumeKey] = r.output;\n      }\n\n      const response = await this.client.resumeFlow(executionId, toolOutputs, {\n        signal: resumeController.signal,\n      });\n      if (!response.ok) {\n        const errorData = await response.json().catch(() => null);\n        throw new Error(errorData?.error ?? `Resume failed: ${response.status}`);\n      }\n      // Server accepted the batch: mark every included call resolved so stale\n      // re-emits don't re-execute the page tool, then complete each bubble.\n      // Do this only after /resume HTTP success; if /resume fails, the server\n      // may still be paused and the retry path must not show a final result.\n      for (const r of ready) {\n        this.webMcpResolvedKeys.add(r.dedupeKey);\n        this.markWebMcpToolComplete(\n          r.toolMessage,\n          r.output,\n          r.startedAt,\n          r.completedAt,\n          r.toolMessage.toolCall?.name === SUGGEST_REPLIES_TOOL_NAME\n            ? { suggestRepliesResolved: true }\n            : undefined,\n        );\n      }\n      if (response.body) {\n        await this.connectStream(response.body, { allowReentry: true });\n      }\n    } catch (error) {\n      const isAbortError =\n        error instanceof Error &&\n        (error.name === \"AbortError\" ||\n          error.message.includes(\"aborted\") ||\n          error.message.includes(\"abort\"));\n      if (!isAbortError) {\n        this.callbacks.onError?.(\n          error instanceof Error ? error : new Error(String(error)),\n        );\n      } else {\n        for (const r of ready) {\n          this.markWebMcpToolComplete(\n            r.toolMessage,\n            buildWebMcpErrorResult(\"Aborted by cancel()\"),\n            r.startedAt,\n          );\n        }\n      }\n    } finally {\n      for (const key of claimedKeys) {\n        this.webMcpInflightKeys.delete(key);\n      }\n      for (const controller of controllers) {\n        this.webMcpResolveControllers.delete(controller);\n      }\n      this.webMcpResolveControllers.delete(resumeController);\n      if (this.webMcpResolveControllers.size === 0 && !this.abortController) {\n        this.setStreaming(false);\n      }\n    }\n  }\n\n  /**\n   * Resolve a paused auto-resolving LOCAL tool call and post the result to\n   * `/resume`: `webmcp:*` calls execute against the host page's tool\n   * registry; the built-in `suggest_replies` skips execution entirely and\n   * resumes with a canned \"shown\" result (the chips render from the message\n   * list, not from this resolve).\n   *\n   * Triggered automatically from `handleEvent` when a `step_await`-derived\n   * message arrives for such a tool: the user does not click a pill; the\n   * bridge's confirm-bubble gate (WebMCP only) is the only interactive\n   * surface.\n   *\n   * Idempotent on the message's `toolCall.id`: re-emits of the same step_await\n   * (e.g. from message coalescing) won't double-fire `tool.execute`. Failure\n   * modes, declined, timed out, throw, unknown tool, all resolve into a\n   * `{ isError: true, content: [...] }` payload that resumes the dispatch\n   * cleanly so the agent can recover.\n   */\n  public async resolveWebMcpToolCall(\n    toolMessage: AgentWidgetMessage,\n  ): Promise<void> {\n    const executionId = toolMessage.agentMetadata?.executionId;\n    const wireToolName = toolMessage.toolCall?.name;\n    const toolCallId = toolMessage.toolCall?.id;\n\n    // Malformed step_await wire shapes shouldn't silently strand the\n    // server-side dispatch. Three failure modes:\n    //   - no executionId: no /resume target exists; surface to the host\n    //     via onError so an operator can react. This is a server-side\n    //     wire-shape bug: Persona can't recover it from the client.\n    //   - no wireToolName: defensive guard: handleEvent only calls us\n    //     for an auto-resolving local tool name (`webmcp:*` or\n    //     `suggest_replies`), so this path indicates a direct caller\n    //     misuse. Silent return.\n    //   - no toolCallId: dedupe key falls apart, but the server can still\n    //     advance if we post an isError for the wireToolName. Do that\n    //     and bail before the dedupe path.\n    if (!executionId) {\n      this.callbacks.onError?.(\n        new Error(\n          \"WebMCP step_await missing executionId: dispatch left paused.\",\n        ),\n      );\n      return;\n    }\n    if (!wireToolName) return;\n    if (!toolCallId) {\n      // No toolCall.id → no per-call dedupe key. Fall back to a synthetic\n      // `(executionId):(wireToolName)` so identical malformed re-emits don't\n      // re-POST /resume. Idempotent on duplicate bad payloads.\n      const malformedKey = `${executionId}:__no_tool_id__:${wireToolName}`;\n      if (\n        this.webMcpInflightKeys.has(malformedKey) ||\n        this.webMcpResolvedKeys.has(malformedKey)\n      ) {\n        return;\n      }\n      this.webMcpInflightKeys.add(malformedKey);\n      try {\n        await this.resumeWithToolOutput(executionId, wireToolName, {\n          isError: true,\n          content: [\n            {\n              type: \"text\",\n              text: \"WebMCP step_await missing toolCall.id: cannot execute the page tool.\",\n            },\n          ],\n        });\n        this.webMcpResolvedKeys.add(malformedKey);\n      } catch (error) {\n        this.callbacks.onError?.(\n          error instanceof Error ? error : new Error(String(error)),\n        );\n      } finally {\n        this.webMcpInflightKeys.delete(malformedKey);\n      }\n      return;\n    }\n\n    // Dedupe key scoped by executionId: see `webMcpInflightKeys` doc comment\n    // for the failure-recovery + cross-dispatch rationale. The persisted\n    // `suggestRepliesResolved` guard backs the in-memory sets across\n    // hydrations.\n    const dedupeKey = `${executionId}:${toolCallId}`;\n    if (\n      this.webMcpInflightKeys.has(dedupeKey) ||\n      this.webMcpResolvedKeys.has(dedupeKey) ||\n      this.isSuggestRepliesAlreadyResolved(toolMessage)\n    ) {\n      return;\n    }\n    this.webMcpInflightKeys.add(dedupeKey);\n\n    // Mark resolved on the message so the UI's local-tool sheet (if any\n    // generic one ever lands) does not show: this is a fully-automatic\n    // tool from the user's perspective, modulo the confirm bubble. Keep the\n    // tool bubble running until the browser-side promise resolves; the\n    // initial step_await was only the server pause, not tool completion.\n    const startedAt = this.markWebMcpToolRunning(toolMessage);\n\n    // Per-resolve AbortController, NOT the shared `this.abortController`.\n    // A single turn can produce multiple `webmcp:*` step_await messages:    // both PARALLEL (two awaits in one stream) and, more commonly, CHAINED\n    // (tool A → /resume → tool B, where B's step_await arrives inside A's\n    // resume SSE stream). The old code pre-aborted `this.abortController`\n    // here to mirror the sibling resolve paths; in the chained case that\n    // aborted the stream still delivering B, so B never executed and its\n    // /resume was never POSTed: the dispatch hung forever. Using a dedicated\n    // per-resolve controller leaves the in-flight resume stream untouched.\n    // cancel()/clearMessages()/hydrateMessages()/sendMessage() iterate\n    // `webMcpResolveControllers` to tear these down on a real stop / new turn.\n    const resolveController = new AbortController();\n    this.webMcpResolveControllers.add(resolveController);\n    const { signal } = resolveController;\n    this.setStreaming(true);\n\n    // Built-in fire-and-forget tool: no bridge, no confirm gate, no\n    // browser-side execution: the chips render from the message list and the\n    // canned output resumes the execution immediately. Branch BEFORE any\n    // bridge access so the missing-bridge error path can never fire for it.\n    const isSuggestReplies = wireToolName === SUGGEST_REPLIES_TOOL_NAME;\n\n    const args = toolMessage.toolCall?.args;\n    // Thread the signal INTO the bridge: short-circuits the confirm bubble\n    // and the execute() race on cancel(), so a late confirm-approval after\n    // cancel() cannot fire a host-page side effect with no matching /resume.\n    const execPromise = isSuggestReplies\n      ? null\n      : this.client.executeWebMcpToolCall(wireToolName, args, signal);\n\n    let phase: \"execute\" | \"resume\" = \"execute\";\n    let completedAt = startedAt;\n    try {\n      let resumeOutput: unknown;\n      if (isSuggestReplies) {\n        resumeOutput = suggestRepliesToolResult();\n      } else if (!execPromise) {\n        // Client has no bridge (config.webmcp.enabled !== true). Resume with\n        // an error so the dispatch can advance instead of hanging.\n        resumeOutput = {\n          isError: true,\n          content: [\n            { type: \"text\", text: \"WebMCP not enabled on this widget.\" },\n          ],\n        };\n      } else {\n        resumeOutput = await execPromise;\n      }\n      completedAt = Date.now();\n      // If cancel() fired during execute, the bridge returned an aborted\n      // result: don't post it. The server's SSE has been torn down; a\n      // /resume now would just produce an orphan dispatch on the server.\n      // Streaming/teardown is handled by the shared `finally` below (gated on\n      // the resolve set) so we don't clobber a sibling resolve or a live\n      // dispatch's controller here.\n      if (signal.aborted) {\n        this.markWebMcpToolComplete(\n          toolMessage,\n          buildWebMcpErrorResult(\"Aborted by cancel()\"),\n          startedAt,\n        );\n        return;\n      }\n      // Mark resolved as soon as the HTTP /resume returns OK: not after the\n      // SSE stream finishes. `connectStream` swallows downstream SSE errors\n      // (they surface via onError, not by rethrowing), so awaiting it doesn't\n      // tell us whether the server actually processed the resume. Marking\n      // here pairs with the dedupe semantics: a successful POST means the\n      // server got the answer; later step_await re-emits for the same\n      // toolCall.id are stale and must not re-execute the page tool.\n      // Key the resume by the per-call id (core#3878) when present; the server\n      // prefers it over tool name. Falls back to the wire tool name for legacy\n      // servers: the original name-keyed contract, still correct for a single\n      // call (only same-tool PARALLEL calls could collide on the name).\n      const resumeKey =\n        toolMessage.agentMetadata?.webMcpToolCallId ?? wireToolName;\n      phase = \"resume\";\n      await this.resumeWithToolOutput(executionId, resumeKey, resumeOutput, {\n        onHttpOk: () => {\n          this.webMcpResolvedKeys.add(dedupeKey);\n          this.markWebMcpToolComplete(\n            toolMessage,\n            resumeOutput,\n            startedAt,\n            completedAt,\n            isSuggestReplies ? { suggestRepliesResolved: true } : undefined,\n          );\n        },\n        signal,\n      });\n    } catch (error) {\n      const isAbortError =\n        error instanceof Error &&\n        (error.name === \"AbortError\" ||\n          error.message.includes(\"aborted\") ||\n          error.message.includes(\"abort\"));\n      // Streaming/teardown handled by the shared `finally` (gated on the\n      // resolve set): do NOT null the shared `this.abortController` here; it\n      // may belong to a live dispatch or sibling resolve, not to us.\n      if (phase === \"execute\" || isAbortError || signal.aborted) {\n        this.markWebMcpToolComplete(\n          toolMessage,\n          buildWebMcpErrorResult(\n            isAbortError || signal.aborted\n              ? \"Aborted by cancel()\"\n              : getWebMcpErrorMessage(error),\n          ),\n          startedAt,\n        );\n      }\n      if (!isAbortError) {\n        // The bridge normalizes tool errors into result objects, so reaching\n        // here means a network failure during `/resume` itself, OR a stream\n        // hookup error. Surface to onError, but DO NOT mark resolved: a\n        // later step_await re-emit should be allowed to retry the resume.\n        this.callbacks.onError?.(\n          error instanceof Error ? error : new Error(String(error)),\n        );\n      }\n    } finally {\n      this.webMcpInflightKeys.delete(dedupeKey);\n      this.webMcpResolveControllers.delete(resolveController);\n      // Only flip streaming off when this was the last in-flight resolve AND\n      // no shared dispatch is live. Otherwise a finishing resolve would hide\n      // the typing indicator while a sibling (parallel) or successor (chained)\n      // resolve, or a live dispatch, is still running.\n      if (this.webMcpResolveControllers.size === 0 && !this.abortController) {\n        this.setStreaming(false);\n      }\n    }\n  }\n\n  /**\n   * POST `/resume` with a SINGLE tool's output and pipe the resulting SSE\n   * stream back through `connectStream`. Shared by every single-call local-tool\n   * resolve path (ask_user_question and single WebMCP calls). Parallel WebMCP\n   * calls use `resolveWebMcpToolCallBatch`, which posts one resume for many.\n   *\n   * `resumeKey` is the `toolOutputs` map key: the per-call `webMcpToolCallId`\n   * for WebMCP (core#3878), or the tool name for ask_user_question / legacy\n   * servers. `onHttpOk` runs synchronously between the HTTP-status check and the\n   * stream pipe; it lets the WebMCP resolve path commit the dedupe flag at\n   * \"server accepted the answer\" rather than \"stream finished cleanly\".\n   */\n  private async resumeWithToolOutput(\n    executionId: string,\n    resumeKey: string,\n    output: unknown,\n    options?: { onHttpOk?: () => void; signal?: AbortSignal },\n  ): Promise<void> {\n    const response = await this.client.resumeFlow(\n      executionId,\n      { [resumeKey]: output },\n      { signal: options?.signal },\n    );\n    if (!response.ok) {\n      const errorData = await response.json().catch(() => null);\n      throw new Error(errorData?.error ?? `Resume failed: ${response.status}`);\n    }\n    options?.onHttpOk?.();\n    if (response.body) {\n      await this.connectStream(response.body, { allowReentry: true });\n    } else if (this.webMcpResolveControllers.size === 0) {\n      // No stream to pipe. Clear streaming only when no WebMCP resolve is in\n      // flight: for a WebMCP caller the current resolve's controller is still\n      // in the set, so its own `finally` (gated on the set draining) owns the\n      // teardown. Non-WebMCP callers (ask_user_question) keep the old behavior.\n      this.setStreaming(false);\n      this.abortController = null;\n    }\n  }\n\n  /**\n   * Tear down every in-flight WebMCP resolve and advance the epoch. Each\n   * resolve owns a dedicated AbortController (chained/parallel resolves don't\n   * share one), so we abort them individually; the aborts propagate into the\n   * bridge's execute race and into each `/resume` fetch signal. Bumping\n   * `webMcpEpoch` strands any resolve still deferred in a queued microtask:   * it captured the prior epoch and bails before installing a fresh\n   * controller, so it can't escape this teardown. Called from every stop /\n   * new-turn boundary (cancel, clearMessages, hydrateMessages, sendMessage).\n   */\n  private abortWebMcpResolves(): void {\n    for (const controller of this.webMcpResolveControllers) {\n      controller.abort();\n    }\n    this.webMcpResolveControllers.clear();\n    // Settle every approval bubble still awaiting a click. The bridge parks a\n    // resolve on `await requestConfirm(...)` (→ requestWebMcpApproval) and only\n    // re-checks `signal.aborted` AFTER that await returns: aborting the\n    // controller above does NOT unblock it. Left unsettled, the bridge's\n    // execute(), its `/resume`, and the resolve's `finally` would all hang\n    // forever (and the resolver map would leak across teardowns). Route through\n    // `resolveWebMcpApproval(…, \"denied\")` so each parked Promise resolves\n    // `false` AND its bubble message flips out of `pending` (no stale \"Approve/\n    // Deny\" left clickable). The bridge then returns cleanly and its\n    // post-confirm `signal.aborted` guard bails before any host-page side effect\n    // or stale `/resume`. Snapshot the keys first: resolveWebMcpApproval\n    // mutates the map as it deletes each resolver.\n    for (const approvalMessageId of [...this.webMcpApprovalResolvers.keys()]) {\n      this.resolveWebMcpApproval(approvalMessageId, \"denied\");\n    }\n    // Drop any awaits buffered for a not-yet-flushed batch: their messages are\n    // being torn down, and a microtask-deferred flush must not survive. The\n    // epoch bump below also strands an already-scheduled flush.\n    this.webMcpAwaitBatches.clear();\n    this.webMcpEpoch++;\n  }\n\n  public cancel() {\n    this.abortController?.abort();\n    this.abortController = null;\n    // Tear down every in-flight WebMCP resolve (each owns its own controller,\n    // independent of the shared one above). Clear the inflight set so retries\n    // are possible if the user re-issues the same step_await context.\n    this.abortWebMcpResolves();\n    this.webMcpInflightKeys.clear();\n    // Stop any in-progress audio too: when the user hits \"stop\", they want\n    // the assistant to actually stop talking, not just stop generating tokens.\n    // Both helpers are safe no-ops when audio isn't configured.\n    this.stopSpeaking();\n    this.stopVoicePlayback();\n    this.setStreaming(false);\n    this.setStatus(\"idle\");\n  }\n\n  public clearMessages() {\n    this.stopSpeaking();\n    this.abortController?.abort();\n    this.abortController = null;\n    // Tear down every in-flight WebMCP resolve too: their messages are about\n    // to be wiped, and a microtask-deferred resolve must not survive the clear.\n    this.abortWebMcpResolves();\n    this.messages = [];\n    this.agentExecution = null;\n    this.clearArtifactState();\n    // Clearing messages also wipes the WebMCP dedupe state: a fresh\n    // conversation should not refuse to call a webmcp:* tool just because\n    // a tool with the same key resolved in the prior conversation.\n    this.webMcpInflightKeys.clear();\n    this.webMcpResolvedKeys.clear();\n    // A fresh conversation must resend the full WebMCP tool list on its next\n    // turn: drop the diff-only fingerprint cache (server keys by recordId, so\n    // a new conversation has no stored set to match).\n    this.client.resetClientToolsFingerprint();\n    this.setStreaming(false);\n    this.setStatus(\"idle\");\n    this.callbacks.onMessagesChanged([...this.messages]);\n  }\n\n  public getArtifacts(): PersonaArtifactRecord[] {\n    return [...this.artifacts.values()];\n  }\n\n  public getArtifactById(id: string): PersonaArtifactRecord | undefined {\n    return this.artifacts.get(id);\n  }\n\n  public getSelectedArtifactId(): string | null {\n    return this.selectedArtifactId;\n  }\n\n  public selectArtifact(id: string | null): void {\n    this.selectedArtifactId = id;\n    this.emitArtifactsState();\n  }\n\n  public clearArtifacts(): void {\n    this.clearArtifactState();\n  }\n\n  public upsertArtifact(manual: PersonaArtifactManualUpsert): PersonaArtifactRecord {\n    const id =\n      manual.id ||\n      `art_${Date.now().toString(36)}_${Math.random().toString(36).slice(2, 9)}`;\n    if (manual.artifactType === \"markdown\") {\n      const rec: PersonaArtifactRecord = {\n        id,\n        artifactType: \"markdown\",\n        title: manual.title,\n        status: \"complete\",\n        markdown: manual.content\n      };\n      this.artifacts.set(id, rec);\n      this.selectedArtifactId = id;\n      this.emitArtifactsState();\n      return rec;\n    }\n    const rec: PersonaArtifactRecord = {\n      id,\n      artifactType: \"component\",\n      title: manual.title,\n      status: \"complete\",\n      component: manual.component,\n      props: manual.props ?? {}\n    };\n    this.artifacts.set(id, rec);\n    this.selectedArtifactId = id;\n    this.emitArtifactsState();\n    return rec;\n  }\n\n  private clearArtifactState(): void {\n    if (this.artifacts.size === 0 && this.selectedArtifactId === null) return;\n    this.artifacts.clear();\n    this.selectedArtifactId = null;\n    this.emitArtifactsState();\n  }\n\n  private emitArtifactsState(): void {\n    this.callbacks.onArtifactsState?.({\n      artifacts: [...this.artifacts.values()],\n      selectedId: this.selectedArtifactId\n    });\n  }\n\n  private applyArtifactStreamEvent(ev: AgentWidgetEvent): void {\n    switch (ev.type) {\n      case \"artifact_start\": {\n        if (ev.artifactType === \"markdown\") {\n          this.artifacts.set(ev.id, {\n            id: ev.id,\n            artifactType: \"markdown\",\n            title: ev.title,\n            status: \"streaming\",\n            markdown: \"\"\n          });\n        } else {\n          this.artifacts.set(ev.id, {\n            id: ev.id,\n            artifactType: \"component\",\n            title: ev.title,\n            status: \"streaming\",\n            component: ev.component ?? \"\",\n            props: {}\n          });\n        }\n        this.selectedArtifactId = ev.id;\n        break;\n      }\n      case \"artifact_delta\": {\n        const row = this.artifacts.get(ev.id);\n        if (row?.artifactType === \"markdown\") {\n          row.markdown = (row.markdown ?? \"\") + ev.artDelta;\n        }\n        break;\n      }\n      case \"artifact_update\": {\n        const row = this.artifacts.get(ev.id);\n        if (row?.artifactType === \"component\") {\n          row.props = { ...row.props, ...ev.props };\n          if (ev.component) row.component = ev.component;\n        }\n        break;\n      }\n      case \"artifact_complete\": {\n        const row = this.artifacts.get(ev.id);\n        if (row) row.status = \"complete\";\n        break;\n      }\n      default:\n        return;\n    }\n    this.emitArtifactsState();\n  }\n\n  public hydrateMessages(messages: AgentWidgetMessage[]) {\n    this.abortController?.abort();\n    this.abortController = null;\n    // Hydration replaces the conversation: abort and forget every in-flight\n    // WebMCP resolve; their messages are about to be replaced.\n    this.abortWebMcpResolves();\n    // Wipe the WebMCP dedupe state alongside the message restore: the\n    // incoming snapshot is treated as a fresh conversation context.\n    this.webMcpInflightKeys.clear();\n    this.webMcpResolvedKeys.clear();\n    this.messages = this.sortMessages(\n      messages.map((message) => ({\n        ...message,\n        streaming: false,\n        sequence: message.sequence ?? this.nextSequence()\n      }))\n    );\n    this.setStreaming(false);\n    this.setStatus(\"idle\");\n    this.callbacks.onMessagesChanged([...this.messages]);\n  }\n\n  public hydrateArtifacts(\n    artifacts: PersonaArtifactRecord[],\n    selectedId: string | null = null\n  ) {\n    this.artifacts.clear();\n    for (const rec of artifacts) {\n      this.artifacts.set(rec.id, { ...rec, status: \"complete\" });\n    }\n    this.selectedArtifactId = selectedId;\n    this.emitArtifactsState();\n  }\n\n  private handleEvent = (event: AgentWidgetEvent) => {\n    if (event.type === \"message\") {\n      this.upsertMessage(event.message);\n\n      // Local-tool auto-resolve: when a step_await emits a tool-variant\n      // message for a `webmcp:*` tool, or the built-in fire-and-forget\n      // `suggest_replies`: resolve it and post the result to /resume.\n      // Unlike ask_user_question, no user pill click is required; for WebMCP\n      // the bridge's confirm bubble is the only interactive surface, and\n      // suggest_replies resumes with a canned \"shown\" result while the chips\n      // render above the composer.\n      //\n      // Defer via `queueMicrotask` so handleEvent returns FIRST. The current\n      // SSE consumer is still mid-loop; once we return, the dispatch's\n      // `connectStream` sees end-of-stream (server closes the SSE at\n      // step_await), flips status to \"idle\", and clears `abortController`\n      // before our resolve grabs them. Without this, the original dispatch's\n      // finalizer would clobber the new abort controller and `streaming=true`\n      // set inside `resolveWebMcpToolCall`.\n      //\n      // ALWAYS resolve when the wire name carries the `webmcp:` prefix, even\n      // if the bridge is non-operational. Otherwise the dispatch stays paused\n      // indefinitely: `resolveWebMcpToolCall` translates the missing-bridge\n      // case into an isError result that resumes the flow cleanly.\n      // `suggest_replies`, by contrast, is gated on its feature flag: when\n      // `features.suggestReplies.enabled === false` the widget neither\n      // renders chips nor resumes: the same parked-execution posture as a\n      // server-declared ask_user_question with its sheet disabled.\n      const tc = event.message.toolCall;\n      const autoResolvable =\n        !!tc?.name &&\n        (isWebMcpToolName(tc.name) ||\n          (tc.name === SUGGEST_REPLIES_TOOL_NAME &&\n            this.config.features?.suggestReplies?.enabled !== false));\n      if (\n        event.message.agentMetadata?.awaitingLocalTool === true &&\n        autoResolvable\n      ) {\n        // Collect the await into its executionId's batch instead of resolving\n        // it on the spot. Parallel same-tool calls (core#3878) arrive as\n        // separate `step_await`s in the same stream; batching lets us post ONE\n        // `/resume` keyed by per-call id (see `enqueueWebMcpAwait`).\n        this.enqueueWebMcpAwait(event.message);\n      }\n\n      // Track agent execution state from message metadata\n      if (event.message.agentMetadata?.executionId) {\n        if (!this.agentExecution) {\n          this.agentExecution = {\n            executionId: event.message.agentMetadata.executionId,\n            agentId: '',\n            agentName: event.message.agentMetadata.agentName ?? '',\n            status: 'running',\n            currentIteration: event.message.agentMetadata.iteration ?? 0,\n            maxTurns: 0\n          };\n        } else if (event.message.agentMetadata.iteration !== undefined) {\n          this.agentExecution.currentIteration = event.message.agentMetadata.iteration;\n        }\n      }\n    } else if (event.type === \"status\") {\n      this.setStatus(event.status);\n      if (event.status === \"connecting\") {\n        this.setStreaming(true);\n      } else if (event.status === \"idle\" || event.status === \"error\") {\n        // Keep the typing indicator up while a WebMCP resolve is still in\n        // flight: in a chained turn the intermediate resume stream ends with an\n        // idle status, but the successor tool is still executing. The resolve's\n        // own `finally` flips streaming off once the resolve set drains.\n        if (this.webMcpResolveControllers.size === 0) {\n          this.setStreaming(false);\n          this.abortController = null;\n        }\n        // Mark agent execution as complete when streaming ends: UNLESS local\n        // tools are still outstanding. A batched WebMCP resume is deferred to\n        // the microtask below (so `webMcpResolveControllers` is still empty\n        // here) and a chained resolve may be mid-flight; marking the run\n        // 'complete' now would make isAgentRunning() report a finished run while\n        // page tools are still executing. Stay 'running': the resume stream's\n        // own idle (with batches drained and resolves settled) marks it done.\n        const webMcpPending =\n          this.webMcpAwaitBatches.size > 0 ||\n          this.webMcpResolveControllers.size > 0;\n        if (this.agentExecution?.status === 'running') {\n          if (event.status === \"error\") {\n            this.agentExecution.status = 'error';\n          } else if (!webMcpPending) {\n            this.agentExecution.status = 'complete';\n          }\n        }\n        // The stream that delivered any local-tool `step_await`s has now ended,\n        // so every parallel await it carried is collected. Flush them as ONE\n        // batched `/resume` per executionId (deferred: see\n        // scheduleWebMcpBatchFlush). Runs AFTER the teardown above so a resolve\n        // doesn't fight the end-of-stream streaming/abortController reset.\n        this.scheduleWebMcpBatchFlush();\n      }\n    } else if (event.type === \"error\") {\n      this.setStatus(\"error\");\n      // Mirror the idle/status handler: don't tear down streaming while a\n      // WebMCP resolve is still confirming/executing on another stream: an\n      // error on one chained resume stream must not hide the typing indicator\n      // (or null a controller) for a sibling/successor resolve still in flight.\n      // The resolve's own `finally` flips streaming off once the set drains.\n      if (this.webMcpResolveControllers.size === 0) {\n        this.setStreaming(false);\n        this.abortController = null;\n      }\n      if (this.agentExecution?.status === 'running') {\n        this.agentExecution.status = 'error';\n      }\n      this.callbacks.onError?.(event.error);\n    } else if (\n      event.type === \"artifact_start\" ||\n      event.type === \"artifact_delta\" ||\n      event.type === \"artifact_update\" ||\n      event.type === \"artifact_complete\"\n    ) {\n      this.applyArtifactStreamEvent(event);\n    }\n  };\n\n  private setStatus(status: AgentWidgetSessionStatus) {\n    if (this.status === status) return;\n    this.status = status;\n    this.callbacks.onStatusChanged(status);\n  }\n\n  private setStreaming(streaming: boolean) {\n    if (this.streaming === streaming) return;\n    const wasStreaming = this.streaming;\n    this.streaming = streaming;\n    this.callbacks.onStreamingChanged(streaming);\n\n    // Speak the latest assistant message when streaming completes\n    if (wasStreaming && !streaming) {\n      this.speakLatestAssistantMessage();\n    }\n  }\n\n  /**\n   * Speak the latest assistant message using the Web Speech API\n   * if text-to-speech is enabled in the config.\n   */\n  private speakLatestAssistantMessage() {\n    const ttsConfig = this.config.textToSpeech;\n    if (!ttsConfig?.enabled) return;\n\n    // Determine if browser TTS should fire:\n    // - provider 'browser' (or unset): always use browser TTS\n    // - provider 'runtype': only if browserFallback is enabled\n    const useBrowserTts =\n      !ttsConfig.provider ||\n      ttsConfig.provider === 'browser' ||\n      (ttsConfig.provider === 'runtype' && ttsConfig.browserFallback);\n    if (!useBrowserTts) return;\n\n    // Find the last assistant message with actual content\n    const lastAssistant = [...this.messages]\n      .reverse()\n      .find(m => m.role === 'assistant' && m.content && !m.voiceProcessing);\n\n    if (!lastAssistant) return;\n\n    // Skip if already spoken by Runtype provider's audio playback\n    if (this.ttsSpokenMessageIds.has(lastAssistant.id)) {\n      this.ttsSpokenMessageIds.delete(lastAssistant.id);\n      return;\n    }\n\n    const text = resolveSpeakableText(lastAssistant.content);\n    if (!text.trim()) return;\n\n    // Route auto-speak through the same controller as the \"Read aloud\" button\n    // so there's a single owner of the speech engine and the button reflects\n    // playback state (and a single message can't be spoken by two paths at once).\n    void this.readAloud.play(lastAssistant.id, {\n      text,\n      voice: ttsConfig.voice,\n      rate: ttsConfig.rate,\n      pitch: ttsConfig.pitch,\n    });\n  }\n\n  /**\n   * Pick the best available English voice from a list of SpeechSynthesisVoices.\n   * Prefers high-quality remote/natural voices, then enhanced local voices,\n   * then standard local voices. Retained for backwards compatibility; delegates\n   * to the browser speech engine's picker.\n   */\n  static pickBestVoice(voices: SpeechSynthesisVoice[]): SpeechSynthesisVoice {\n    return pickBestVoice(voices);\n  }\n\n  /**\n   * Toggle the per-message \"Read aloud\" action: play → pause → resume (or\n   * play → stop when the engine can't pause). Speaks the assistant message's\n   * text via the configured speech engine (browser Web Speech API by default,\n   * or a hosted engine from `textToSpeech.createEngine`).\n   */\n  public toggleReadAloud(messageId: string): void {\n    const message = this.messages.find(m => m.id === messageId);\n    if (!message || message.role !== 'assistant') return;\n    const text = resolveSpeakableText(message.content || '');\n    if (!text.trim()) return;\n    const tts = this.config.textToSpeech;\n    this.readAloud.toggle(messageId, {\n      text,\n      voice: tts?.voice,\n      rate: tts?.rate,\n      pitch: tts?.pitch,\n    });\n  }\n\n  /** Current read-aloud playback state for a message (`idle` unless active). */\n  public getReadAloudState(messageId: string): ReadAloudState {\n    return this.readAloud.stateFor(messageId);\n  }\n\n  /** Subscribe to read-aloud state changes. Returns an unsubscribe function. */\n  public onReadAloudChange(listener: ReadAloudListener): () => void {\n    return this.readAloud.onChange(listener);\n  }\n\n  /**\n   * Stop any in-progress text-to-speech / read-aloud playback.\n   */\n  public stopSpeaking() {\n    this.readAloud.stop();\n    if (typeof window !== 'undefined' && 'speechSynthesis' in window) {\n      window.speechSynthesis.cancel();\n    }\n  }\n\n  private appendMessage(message: AgentWidgetMessage) {\n    const withSequence = this.ensureSequence(message);\n    this.messages = this.sortMessages([...this.messages, withSequence]);\n    this.callbacks.onMessagesChanged([...this.messages]);\n  }\n\n  private upsertMessage(message: AgentWidgetMessage) {\n    const withSequence = this.ensureSequence(message);\n    const index = this.messages.findIndex((m) => m.id === withSequence.id);\n    if (index === -1) {\n      this.appendMessage(withSequence);\n      return;\n    }\n\n    this.messages = this.messages.map((existing, idx) => {\n      if (idx !== index) return existing;\n      const merged = { ...existing, ...withSequence };\n      // Preserve `ask_user_question` answered state across re-emissions.\n      // Top-level merge would otherwise replace `agentMetadata` wholesale:      // post-resume events (e.g. `tool_complete` re-emitted from a stale\n      // client-side cache) would wipe `askUserQuestionAnswered` and\n      // `askUserQuestionAnswers`, causing the answered review card to\n      // lose its answers and revert to \"(skipped)\" placeholders.\n      if (\n        existing.agentMetadata?.askUserQuestionAnswered === true &&\n        withSequence.agentMetadata\n      ) {\n        merged.agentMetadata = {\n          ...withSequence.agentMetadata,\n          askUserQuestionAnswered: true,\n          ...(existing.agentMetadata.askUserQuestionAnswers\n            ? {\n                askUserQuestionAnswers:\n                  existing.agentMetadata.askUserQuestionAnswers,\n              }\n            : {}),\n          // Keep awaiting flag false once resolved: never let a stale\n          // re-emit flip us back to awaiting.\n          awaitingLocalTool: false,\n        };\n      }\n      // suggest_replies equivalent: preserve the persisted fire-and-forget\n      // resolution across re-emissions. It is the only dedupe signal that\n      // survives a hydration (the in-memory key sets are cleared), so a\n      // stale step_await re-emit must not wipe it before\n      // `isSuggestRepliesAlreadyResolved` checks it in the resolve path.\n      if (\n        existing.agentMetadata?.suggestRepliesResolved === true &&\n        withSequence.agentMetadata\n      ) {\n        merged.agentMetadata = {\n          ...(merged.agentMetadata ?? withSequence.agentMetadata),\n          suggestRepliesResolved: true,\n          awaitingLocalTool: false,\n        };\n      }\n      // Approval equivalent: `approval_complete` carries only the\n      // resolution (approvalId, decision, resolvedBy): the runtime does not\n      // re-send toolName/description/toolType/reason/parameters, so client.ts\n      // rebuilds the approval with empty required fields and the optional\n      // ones absent. A wholesale `approval` replacement would wipe that\n      // context from the resolved bubble on the next full re-render (morph,\n      // virtual-scroll re-mount, storage restore). Merge field-wise instead:\n      // take the resolution from the incoming event, keep existing context\n      // wherever the event is silent or empty.\n      if (\n        existing.approval &&\n        withSequence.approval &&\n        existing.approval.id === withSequence.approval.id\n      ) {\n        const prior = existing.approval;\n        const incoming = withSequence.approval;\n        merged.approval = {\n          ...prior,\n          ...incoming,\n          executionId: incoming.executionId || prior.executionId,\n          toolName: incoming.toolName || prior.toolName,\n          description: incoming.description || prior.description,\n          toolType: incoming.toolType ?? prior.toolType,\n          reason: incoming.reason ?? prior.reason,\n          parameters: incoming.parameters ?? prior.parameters,\n        };\n      }\n      // Auto-resolved local-tool equivalent (`webmcp:*` and the built-in\n      // `suggest_replies`): once such a tool has started resolving (inflight)\n      // or resolved, a duplicate `step_await` re-emit must not flip\n      // `awaitingLocalTool` back to true and resurrect the \"waiting on\n      // local tool\" UI. It also must not overwrite an existing running or\n      // completed toolCall with the fresh running skeleton emitted by client.ts\n      // for every step_await. resolveWebMcpToolCall's dedupe path returns\n      // without re-touching the message, so correct the merge here (also avoids\n      // a one-frame flash before that microtask runs).\n      const reTcName = withSequence.toolCall?.name;\n      const reExecId = withSequence.agentMetadata?.executionId;\n      const reTcId = withSequence.toolCall?.id;\n      if (\n        reTcName &&\n        isAutoResolvedLocalToolName(reTcName) &&\n        reExecId &&\n        reTcId &&\n        withSequence.agentMetadata?.awaitingLocalTool === true\n      ) {\n        const reKey = `${reExecId}:${reTcId}`;\n        const isInflight = this.webMcpInflightKeys.has(reKey);\n        const isResolved = this.webMcpResolvedKeys.has(reKey);\n        const existingToolName = existing.toolCall?.name;\n        const hasCompletedTool =\n          existing.agentMetadata?.executionId === reExecId &&\n          existing.toolCall?.id === reTcId &&\n          existingToolName !== undefined &&\n          isAutoResolvedLocalToolName(existingToolName) &&\n          existing.toolCall?.status === \"complete\";\n        if (isInflight || isResolved || hasCompletedTool) {\n          merged.agentMetadata = {\n            ...(merged.agentMetadata ?? {}),\n            awaitingLocalTool: false,\n          };\n          // Preserve the in-flight/completed tool state. For in-flight calls,\n          // this keeps the original `startedAt`; for completed calls, it keeps\n          // the measured duration/result even when the call completed with a\n          // local/browser error and therefore was not promoted to resolved.\n          merged.toolCall = existing.toolCall;\n          merged.streaming = existing.streaming;\n        }\n      }\n      return merged;\n    });\n    this.messages = this.sortMessages(this.messages);\n    this.callbacks.onMessagesChanged([...this.messages]);\n  }\n\n  private ensureSequence(message: AgentWidgetMessage): AgentWidgetMessage {\n    if (message.sequence !== undefined) {\n      return { ...message };\n    }\n    return {\n      ...message,\n      sequence: this.nextSequence()\n    };\n  }\n\n  private nextSequence() {\n    return this.sequenceCounter++;\n  }\n\n  private sortMessages(messages: AgentWidgetMessage[]) {\n    return [...messages].sort((a, b) => {\n      // Sort by createdAt timestamp first (chronological order)\n      const timeA = new Date(a.createdAt).getTime();\n      const timeB = new Date(b.createdAt).getTime();\n      if (!Number.isNaN(timeA) && !Number.isNaN(timeB) && timeA !== timeB) {\n        return timeA - timeB;\n      }\n\n      // Fall back to sequence if timestamps are equal or invalid\n      const seqA = a.sequence ?? 0;\n      const seqB = b.sequence ?? 0;\n      if (seqA !== seqB) return seqA - seqB;\n\n      // Final fallback to ID\n      return a.id.localeCompare(b.id);\n    });\n  }\n}\n","import type { IconNode } from \"lucide\";\nimport {\n  // ---------- Mandatory (referenced as string literals in widget source) ----------\n  Activity,\n  ArrowDown,\n  ArrowUp,\n  ArrowUpRight,\n  Bot,\n  ChevronDown,\n  ChevronUp,\n  ChevronRight,\n  ChevronLeft,\n  Check,\n  Clipboard,\n  ClipboardCopy,\n  Copy,\n  File as FileIcon,\n  FileCode,\n  FileSpreadsheet,\n  FileText,\n  ImagePlus,\n  Loader,\n  LoaderCircle,\n  Mic,\n  Paperclip,\n  RefreshCw,\n  Search,\n  Send,\n  ShieldAlert,\n  ShieldCheck,\n  ShieldX,\n  Square,\n  ThumbsDown,\n  ThumbsUp,\n  Upload,\n  Volume2,\n  X,\n  // ---------- Forms / inputs ----------\n  User,\n  Mail,\n  Phone,\n  Calendar,\n  Clock,\n  Building,\n  MapPin,\n  Lock,\n  Key,\n  CreditCard,\n  AtSign,\n  Hash,\n  Globe,\n  Link,\n  // ---------- Status / feedback ----------\n  CircleCheck,\n  CircleX,\n  TriangleAlert,\n  Info,\n  Ban,\n  Shield,\n  // ---------- Navigation ----------\n  ArrowLeft,\n  ArrowRight,\n  ExternalLink,\n  Ellipsis,\n  EllipsisVertical,\n  Menu,\n  House,\n  // ---------- Actions ----------\n  Plus,\n  Minus,\n  Pencil,\n  Trash,\n  Trash2,\n  Save,\n  Download,\n  Share,\n  Funnel,\n  Settings,\n  RotateCw,\n  Maximize,\n  Minimize,\n  // ---------- Commerce ----------\n  ShoppingCart,\n  ShoppingBag,\n  Package,\n  Truck,\n  Tag,\n  Gift,\n  Receipt,\n  Wallet,\n  Store,\n  DollarSign,\n  Percent,\n  // ---------- Media ----------\n  Play,\n  Pause,\n  VolumeX,\n  Camera,\n  Image as ImageIcon,\n  Film,\n  Headphones,\n  // ---------- Social / Comms ----------\n  MessageCircle,\n  MessageSquare,\n  Bell,\n  Heart,\n  Star,\n  Eye,\n  EyeOff,\n  Bookmark,\n  // ---------- Time ----------\n  CalendarDays,\n  History,\n  Timer,\n  // ---------- Files ----------\n  Folder,\n  FolderOpen,\n  Files,\n  // ---------- Decorative ----------\n  Sparkles,\n  Zap,\n  Sun,\n  Moon,\n  Flag,\n  // ---------- Devices ----------\n  Monitor,\n  Smartphone,\n} from \"lucide\";\n\n/**\n * Curated registry of lucide icons available to `renderLucideIcon`.\n *\n * The widget used to do `import * as icons from \"lucide\"` and look up\n * icons dynamically by string. That defeated tree-shaking, so the IIFE\n * (CDN/script-tag) bundle shipped all 1640 lucide icons (~400KB of icon\n * data) regardless of which we actually used. This explicit registry\n * lets the bundler drop any icon not listed here.\n *\n * Trade-off: `renderLucideIcon(name)` is now a *closed set*. Names not\n * in this map return `null` and log a warning, exactly as a typo did\n * before. The registry is intentionally generous (~110 icons) so that\n * custom `ComponentRenderer` authors rarely hit a missing-icon dead end.\n *\n * To add icons: add a named import above and a row in `LUCIDE_ICONS`,\n * keyed by the lucide kebab-case name (matches their filename and\n * https://lucide.dev/icons).\n *\n * See `packages/widget/docs/icon-registry-shortlist.md` for the full\n * curation rationale and which icons were considered but excluded.\n */\nconst LUCIDE_ICONS = {\n  // Mandatory\n  \"activity\": Activity,\n  \"arrow-down\": ArrowDown,\n  \"arrow-up\": ArrowUp,\n  \"arrow-up-right\": ArrowUpRight,\n  \"bot\": Bot,\n  \"chevron-down\": ChevronDown,\n  \"chevron-up\": ChevronUp,\n  \"chevron-right\": ChevronRight,\n  \"chevron-left\": ChevronLeft,\n  \"check\": Check,\n  \"clipboard\": Clipboard,\n  \"clipboard-copy\": ClipboardCopy,\n  \"copy\": Copy,\n  \"file\": FileIcon,\n  \"file-code\": FileCode,\n  \"file-spreadsheet\": FileSpreadsheet,\n  \"file-text\": FileText,\n  \"image-plus\": ImagePlus,\n  \"loader\": Loader,\n  \"loader-circle\": LoaderCircle,\n  \"mic\": Mic,\n  \"paperclip\": Paperclip,\n  \"refresh-cw\": RefreshCw,\n  \"search\": Search,\n  \"send\": Send,\n  \"shield-alert\": ShieldAlert,\n  \"shield-check\": ShieldCheck,\n  \"shield-x\": ShieldX,\n  \"square\": Square,\n  \"thumbs-down\": ThumbsDown,\n  \"thumbs-up\": ThumbsUp,\n  \"upload\": Upload,\n  \"volume-2\": Volume2,\n  \"x\": X,\n  // Forms / inputs\n  \"user\": User,\n  \"mail\": Mail,\n  \"phone\": Phone,\n  \"calendar\": Calendar,\n  \"clock\": Clock,\n  \"building\": Building,\n  \"map-pin\": MapPin,\n  \"lock\": Lock,\n  \"key\": Key,\n  \"credit-card\": CreditCard,\n  \"at-sign\": AtSign,\n  \"hash\": Hash,\n  \"globe\": Globe,\n  \"link\": Link,\n  // Status / feedback\n  \"circle-check\": CircleCheck,\n  \"circle-x\": CircleX,\n  \"triangle-alert\": TriangleAlert,\n  \"info\": Info,\n  \"ban\": Ban,\n  \"shield\": Shield,\n  // Navigation\n  \"arrow-left\": ArrowLeft,\n  \"arrow-right\": ArrowRight,\n  \"external-link\": ExternalLink,\n  \"ellipsis\": Ellipsis,\n  \"ellipsis-vertical\": EllipsisVertical,\n  \"menu\": Menu,\n  \"house\": House,\n  // Actions\n  \"plus\": Plus,\n  \"minus\": Minus,\n  \"pencil\": Pencil,\n  \"trash\": Trash,\n  \"trash-2\": Trash2,\n  \"save\": Save,\n  \"download\": Download,\n  \"share\": Share,\n  \"funnel\": Funnel,\n  \"settings\": Settings,\n  \"rotate-cw\": RotateCw,\n  \"maximize\": Maximize,\n  \"minimize\": Minimize,\n  // Commerce\n  \"shopping-cart\": ShoppingCart,\n  \"shopping-bag\": ShoppingBag,\n  \"package\": Package,\n  \"truck\": Truck,\n  \"tag\": Tag,\n  \"gift\": Gift,\n  \"receipt\": Receipt,\n  \"wallet\": Wallet,\n  \"store\": Store,\n  \"dollar-sign\": DollarSign,\n  \"percent\": Percent,\n  // Media\n  \"play\": Play,\n  \"pause\": Pause,\n  \"volume-x\": VolumeX,\n  \"camera\": Camera,\n  \"image\": ImageIcon,\n  \"film\": Film,\n  \"headphones\": Headphones,\n  // Social / Comms\n  \"message-circle\": MessageCircle,\n  \"message-square\": MessageSquare,\n  \"bell\": Bell,\n  \"heart\": Heart,\n  \"star\": Star,\n  \"eye\": Eye,\n  \"eye-off\": EyeOff,\n  \"bookmark\": Bookmark,\n  // Time\n  \"calendar-days\": CalendarDays,\n  \"history\": History,\n  \"timer\": Timer,\n  // Files\n  \"folder\": Folder,\n  \"folder-open\": FolderOpen,\n  \"files\": Files,\n  // Decorative\n  \"sparkles\": Sparkles,\n  \"zap\": Zap,\n  \"sun\": Sun,\n  \"moon\": Moon,\n  \"flag\": Flag,\n  // Devices\n  \"monitor\": Monitor,\n  \"smartphone\": Smartphone,\n} as const satisfies Record<string, IconNode>;\n\n/**\n * Names of lucide icons that ship with the widget. Names not in this\n * union return `null` from `renderLucideIcon` (with a console warning).\n */\nexport type IconName = keyof typeof LUCIDE_ICONS;\n\n/**\n * Renders a lucide icon as an inline SVG element. Works inside Shadow\n * DOM and requires no CSS.\n *\n * @param iconName - A lucide kebab-case name from the registry. See\n *   `IconName` for the full list, or `docs/icon-registry-shortlist.md`\n *   for rationale.\n * @param size - The size in pixels (number) or any CSS length string.\n * @param color - Stroke color (default: \"currentColor\").\n * @param strokeWidth - Stroke width (default: 2).\n * @returns SVGElement, or null if the name is not in the registry.\n */\nexport const renderLucideIcon = (\n  iconName: IconName | (string & {}),\n  size: number | string = 24,\n  color: string = \"currentColor\",\n  strokeWidth: number = 2\n): SVGElement | null => {\n  const iconData = (LUCIDE_ICONS as Record<string, IconNode | undefined>)[iconName];\n  if (!iconData) {\n    console.warn(\n      `Lucide icon \"${iconName}\" is not in the Persona registry. ` +\n      `Add it to packages/widget/src/utils/icons.ts (see docs/icon-registry-shortlist.md).`\n    );\n    return null;\n  }\n  return createSvgFromIconData(iconData, size, color, strokeWidth);\n};\n\nfunction createSvgFromIconData(\n  iconData: IconNode,\n  size: number | string,\n  color: string,\n  strokeWidth: number\n): SVGElement | null {\n  if (!Array.isArray(iconData)) return null;\n\n  const svg = document.createElementNS(\"http://www.w3.org/2000/svg\", \"svg\");\n  svg.setAttribute(\"width\", String(size));\n  svg.setAttribute(\"height\", String(size));\n  svg.setAttribute(\"viewBox\", \"0 0 24 24\");\n  svg.setAttribute(\"fill\", \"none\");\n  svg.setAttribute(\"stroke\", color);\n  svg.setAttribute(\"stroke-width\", String(strokeWidth));\n  svg.setAttribute(\"stroke-linecap\", \"round\");\n  svg.setAttribute(\"stroke-linejoin\", \"round\");\n  svg.setAttribute(\"aria-hidden\", \"true\");\n\n  // IconNode shape: [[\"path\", {\"d\": \"...\"}], [\"circle\", {\"cx\": \"...\"}], ...]\n  iconData.forEach((elementData) => {\n    if (!Array.isArray(elementData) || elementData.length < 2) return;\n    const tagName = elementData[0] as string;\n    const attrs = elementData[1] as Record<string, string> | undefined;\n    if (!attrs) return;\n    const element = document.createElementNS(\"http://www.w3.org/2000/svg\", tagName);\n    Object.entries(attrs).forEach(([key, value]) => {\n      // Skip 'stroke' so the parent SVG's stroke attribute drives color uniformly\n      if (key !== \"stroke\") element.setAttribute(key, String(value));\n    });\n    svg.appendChild(element);\n  });\n\n  return svg;\n}\n","/**\n * Attachment Manager\n *\n * Handles file selection, validation, preview generation, and content part creation\n * for the composer attachment feature. Supports both images and documents.\n */\n\nimport { createElement } from \"./dom\";\nimport { renderLucideIcon } from \"./icons\";\nimport type {\n  AgentWidgetAttachmentsConfig,\n  ContentPart,\n  ImageContentPart,\n  FileContentPart\n} from \"../types\";\nimport {\n  fileToContentPart,\n  validateFile,\n  isImageFile,\n  getFileTypeName,\n  ALL_SUPPORTED_MIME_TYPES\n} from \"./content\";\n\n/**\n * Pending attachment with preview\n */\nexport interface PendingAttachment {\n  id: string;\n  file: File;\n  previewUrl: string | null; // null for non-image files\n  contentPart: ImageContentPart | FileContentPart;\n}\n\n/**\n * Attachment manager configuration\n */\nexport interface AttachmentManagerConfig {\n  allowedTypes?: string[];\n  maxFileSize?: number;\n  maxFiles?: number;\n  onFileRejected?: (file: File, reason: \"type\" | \"size\" | \"count\") => void;\n  onAttachmentsChange?: (attachments: PendingAttachment[]) => void;\n}\n\n/**\n * Default configuration values\n */\nconst DEFAULTS = {\n  allowedTypes: ALL_SUPPORTED_MIME_TYPES,\n  maxFileSize: 10 * 1024 * 1024, // 10MB\n  maxFiles: 4\n};\n\n/**\n * Generate a unique ID for attachments\n */\nfunction generateAttachmentId(): string {\n  return `attach_${Date.now()}_${Math.random().toString(36).substring(2, 9)}`;\n}\n\n/**\n * Get the appropriate Lucide icon name for a file type\n */\nfunction getFileIconName(mimeType: string): string {\n  if (mimeType === 'application/pdf') return 'file-text';\n  if (mimeType.startsWith('text/')) return 'file-text';\n  if (mimeType.includes('word')) return 'file-text';\n  if (mimeType.includes('excel') || mimeType.includes('spreadsheet')) return 'file-spreadsheet';\n  if (mimeType === 'application/json') return 'file-code';\n  return 'file';\n}\n\n/**\n * Creates and manages attachments for the composer\n */\nexport class AttachmentManager {\n  private attachments: PendingAttachment[] = [];\n  private config: Required<\n    Pick<AttachmentManagerConfig, \"allowedTypes\" | \"maxFileSize\" | \"maxFiles\">\n  > &\n    Pick<AttachmentManagerConfig, \"onFileRejected\" | \"onAttachmentsChange\">;\n  private previewsContainer: HTMLElement | null = null;\n\n  constructor(config: AttachmentManagerConfig = {}) {\n    this.config = {\n      allowedTypes: config.allowedTypes ?? DEFAULTS.allowedTypes,\n      maxFileSize: config.maxFileSize ?? DEFAULTS.maxFileSize,\n      maxFiles: config.maxFiles ?? DEFAULTS.maxFiles,\n      onFileRejected: config.onFileRejected,\n      onAttachmentsChange: config.onAttachmentsChange\n    };\n  }\n\n  /**\n   * Set the previews container element\n   */\n  setPreviewsContainer(container: HTMLElement | null): void {\n    this.previewsContainer = container;\n  }\n\n  /**\n   * Update the configuration (e.g., when allowed types change)\n   */\n  updateConfig(config: Partial<AttachmentManagerConfig>): void {\n    if (config.allowedTypes !== undefined) {\n      this.config.allowedTypes = config.allowedTypes.length > 0 ? config.allowedTypes : DEFAULTS.allowedTypes;\n    }\n    if (config.maxFileSize !== undefined) {\n      this.config.maxFileSize = config.maxFileSize;\n    }\n    if (config.maxFiles !== undefined) {\n      this.config.maxFiles = config.maxFiles;\n    }\n    if (config.onFileRejected !== undefined) {\n      this.config.onFileRejected = config.onFileRejected;\n    }\n    if (config.onAttachmentsChange !== undefined) {\n      this.config.onAttachmentsChange = config.onAttachmentsChange;\n    }\n  }\n\n  /**\n   * Get current attachments\n   */\n  getAttachments(): PendingAttachment[] {\n    return [...this.attachments];\n  }\n\n  /**\n   * Get content parts for all attachments\n   */\n  getContentParts(): ContentPart[] {\n    return this.attachments.map((a) => a.contentPart);\n  }\n\n  /**\n   * Check if there are any attachments\n   */\n  hasAttachments(): boolean {\n    return this.attachments.length > 0;\n  }\n\n  /**\n   * Get the number of attachments\n   */\n  count(): number {\n    return this.attachments.length;\n  }\n\n  /**\n   * Handle file input change event\n   */\n  async handleFileSelect(files: FileList | null): Promise<void> {\n    if (!files || files.length === 0) return;\n    await this.handleFiles(Array.from(files));\n  }\n\n  /**\n   * Handle an array of files (e.g., clipboard image paste)\n   */\n  async handleFiles(files: readonly File[]): Promise<void> {\n    if (!files.length) return;\n\n    for (const file of files) {\n      // Check if we've hit the max files limit\n      if (this.attachments.length >= this.config.maxFiles) {\n        this.config.onFileRejected?.(file, \"count\");\n        continue;\n      }\n\n      // Validate the file\n      const validation = validateFile(\n        file,\n        this.config.allowedTypes,\n        this.config.maxFileSize\n      );\n\n      if (!validation.valid) {\n        const reason = validation.error?.includes(\"type\") ? \"type\" : \"size\";\n        this.config.onFileRejected?.(file, reason);\n        continue;\n      }\n\n      try {\n        // Convert to content part (handles both images and files)\n        const contentPart = await fileToContentPart(file);\n\n        // Create preview URL only for images\n        const previewUrl = isImageFile(file) ? URL.createObjectURL(file) : null;\n\n        const attachment: PendingAttachment = {\n          id: generateAttachmentId(),\n          file,\n          previewUrl,\n          contentPart\n        };\n\n        this.attachments.push(attachment);\n        this.renderPreview(attachment);\n      } catch (error) {\n        console.error(\"[AttachmentManager] Failed to process file:\", error);\n      }\n    }\n\n    this.updatePreviewsVisibility();\n    this.config.onAttachmentsChange?.(this.getAttachments());\n  }\n\n  /**\n   * Remove an attachment by ID\n   */\n  removeAttachment(id: string): void {\n    const index = this.attachments.findIndex((a) => a.id === id);\n    if (index === -1) return;\n\n    const attachment = this.attachments[index];\n\n    // Revoke the object URL to free memory (only for images)\n    if (attachment.previewUrl) {\n      URL.revokeObjectURL(attachment.previewUrl);\n    }\n\n    // Remove from array\n    this.attachments.splice(index, 1);\n\n    // Remove from DOM\n    const previewEl = this.previewsContainer?.querySelector(\n      `[data-attachment-id=\"${id}\"]`\n    );\n    if (previewEl) {\n      previewEl.remove();\n    }\n\n    this.updatePreviewsVisibility();\n    this.config.onAttachmentsChange?.(this.getAttachments());\n  }\n\n  /**\n   * Clear all attachments\n   */\n  clearAttachments(): void {\n    // Revoke all object URLs\n    for (const attachment of this.attachments) {\n      if (attachment.previewUrl) {\n        URL.revokeObjectURL(attachment.previewUrl);\n      }\n    }\n\n    this.attachments = [];\n\n    // Clear the previews container\n    if (this.previewsContainer) {\n      this.previewsContainer.innerHTML = \"\";\n    }\n\n    this.updatePreviewsVisibility();\n    this.config.onAttachmentsChange?.(this.getAttachments());\n  }\n\n  /**\n   * Render a preview for an attachment (image thumbnail or file icon)\n   */\n  private renderPreview(attachment: PendingAttachment): void {\n    if (!this.previewsContainer) return;\n\n    const isImage = isImageFile(attachment.file);\n\n    const previewWrapper = createElement(\n      \"div\",\n      \"persona-attachment-preview persona-relative persona-inline-block\"\n    );\n    previewWrapper.setAttribute(\"data-attachment-id\", attachment.id);\n    previewWrapper.style.width = \"48px\";\n    previewWrapper.style.height = \"48px\";\n\n    if (isImage && attachment.previewUrl) {\n      // Render image thumbnail\n      const img = createElement(\"img\") as HTMLImageElement;\n      img.src = attachment.previewUrl;\n      img.alt = attachment.file.name;\n      img.className =\n        \"persona-w-full persona-h-full persona-object-cover persona-rounded-lg persona-border persona-border-gray-200\";\n      img.style.width = \"48px\";\n      img.style.height = \"48px\";\n      img.style.objectFit = \"cover\";\n      img.style.borderRadius = \"8px\";\n      previewWrapper.appendChild(img);\n    } else {\n      // Render file icon with type label\n      const filePreview = createElement(\"div\");\n      filePreview.style.width = \"48px\";\n      filePreview.style.height = \"48px\";\n      filePreview.style.borderRadius = \"8px\";\n      filePreview.style.backgroundColor = \"var(--persona-container, #f3f4f6)\";\n      filePreview.style.border = \"1px solid var(--persona-border, #e5e7eb)\";\n      filePreview.style.display = \"flex\";\n      filePreview.style.flexDirection = \"column\";\n      filePreview.style.alignItems = \"center\";\n      filePreview.style.justifyContent = \"center\";\n      filePreview.style.gap = \"2px\";\n      filePreview.style.overflow = \"hidden\";\n\n      // File icon\n      const iconName = getFileIconName(attachment.file.type);\n      const fileIcon = renderLucideIcon(iconName, 20, \"var(--persona-muted, #6b7280)\", 1.5);\n      if (fileIcon) {\n        filePreview.appendChild(fileIcon);\n      }\n\n      // File type label\n      const typeLabel = createElement(\"span\");\n      typeLabel.textContent = getFileTypeName(attachment.file.type, attachment.file.name);\n      typeLabel.style.fontSize = \"8px\";\n      typeLabel.style.fontWeight = \"600\";\n      typeLabel.style.color = \"var(--persona-muted, #6b7280)\";\n      typeLabel.style.textTransform = \"uppercase\";\n      typeLabel.style.lineHeight = \"1\";\n      filePreview.appendChild(typeLabel);\n\n      previewWrapper.appendChild(filePreview);\n    }\n\n    // Create remove button\n    const removeBtn = createElement(\n      \"button\",\n      \"persona-attachment-remove persona-absolute persona-flex persona-items-center persona-justify-center\"\n    ) as HTMLButtonElement;\n    removeBtn.type = \"button\";\n    removeBtn.setAttribute(\"aria-label\", \"Remove attachment\");\n    removeBtn.style.position = \"absolute\";\n    removeBtn.style.top = \"-4px\";\n    removeBtn.style.right = \"-4px\";\n    removeBtn.style.width = \"18px\";\n    removeBtn.style.height = \"18px\";\n    removeBtn.style.borderRadius = \"50%\";\n    removeBtn.style.backgroundColor = \"var(--persona-palette-colors-black-alpha-60, rgba(0, 0, 0, 0.6))\";\n    removeBtn.style.border = \"none\";\n    removeBtn.style.cursor = \"pointer\";\n    removeBtn.style.display = \"flex\";\n    removeBtn.style.alignItems = \"center\";\n    removeBtn.style.justifyContent = \"center\";\n    removeBtn.style.padding = \"0\";\n\n    // Add X icon\n    const xIcon = renderLucideIcon(\"x\", 10, \"var(--persona-text-inverse, #ffffff)\", 2);\n    if (xIcon) {\n      removeBtn.appendChild(xIcon);\n    } else {\n      removeBtn.textContent = \"×\";\n      removeBtn.style.color = \"var(--persona-text-inverse, #ffffff)\";\n      removeBtn.style.fontSize = \"14px\";\n      removeBtn.style.lineHeight = \"1\";\n    }\n\n    // Remove on click\n    removeBtn.addEventListener(\"click\", (e) => {\n      e.preventDefault();\n      e.stopPropagation();\n      this.removeAttachment(attachment.id);\n    });\n\n    previewWrapper.appendChild(removeBtn);\n    this.previewsContainer.appendChild(previewWrapper);\n  }\n\n  /**\n   * Update the visibility of the previews container\n   */\n  private updatePreviewsVisibility(): void {\n    if (!this.previewsContainer) return;\n    this.previewsContainer.style.display =\n      this.attachments.length > 0 ? \"flex\" : \"none\";\n  }\n\n  /**\n   * Create an AttachmentManager from widget config\n   */\n  static fromConfig(\n    config?: AgentWidgetAttachmentsConfig,\n    onAttachmentsChange?: (attachments: PendingAttachment[]) => void\n  ): AttachmentManager {\n    return new AttachmentManager({\n      allowedTypes: config?.allowedTypes,\n      maxFileSize: config?.maxFileSize,\n      maxFiles: config?.maxFiles,\n      onFileRejected: config?.onFileRejected,\n      onAttachmentsChange\n    });\n  }\n}\n","const isObject = (value: unknown): value is Record<string, unknown> =>\n  typeof value === \"object\" && value !== null && !Array.isArray(value);\n\n/**\n * Deep-merge plain objects. Arrays and non-objects are replaced by override.\n */\nexport function deepMerge<T extends Record<string, unknown>>(\n  base: T | undefined,\n  override: Record<string, unknown> | undefined\n): T | Record<string, unknown> | undefined {\n  if (!base) return override;\n  if (!override) return base;\n\n  const merged: Record<string, unknown> = { ...base };\n\n  for (const [key, value] of Object.entries(override)) {\n    const existing = merged[key];\n    if (isObject(existing) && isObject(value)) {\n      merged[key] = deepMerge(existing, value);\n    } else {\n      merged[key] = value;\n    }\n  }\n\n  return merged;\n}\n","import type { AgentWidgetConfig, AgentWidgetLauncherConfig } from \"./types\";\nimport type { DeepPartial, PersonaTheme } from \"./types/theme\";\nimport { deepMerge } from \"./utils/deep-merge\";\n\n/**\n * Default width for the floating launcher panel (when not overridden).\n * Benchmarks: many chat products use ~300–400px; 400px is a frequent “standard” default.\n * We use 440px to better fit code/JSON and structured replies while staying responsive via `min(..., 100vw)`.\n */\nexport const DEFAULT_FLOATING_LAUNCHER_WIDTH = \"min(440px, calc(100vw - 24px))\";\n\n/** Max width cap paired with {@link DEFAULT_FLOATING_LAUNCHER_WIDTH} for theme defaults. */\nexport const DEFAULT_FLOATING_LAUNCHER_MAX_WIDTH = \"440px\";\n\nexport const DEFAULT_LAUNCHER_CONFIG: AgentWidgetLauncherConfig = {\n  enabled: true,\n  mountMode: \"floating\",\n  dock: {\n    side: \"right\",\n    width: \"420px\",\n  },\n  title: \"Chat Assistant\",\n  subtitle: \"Here to help you get answers fast\",\n  agentIconText: \"💬\",\n  agentIconName: \"bot\",\n  headerIconName: \"bot\",\n  position: \"bottom-right\",\n  width: DEFAULT_FLOATING_LAUNCHER_WIDTH,\n  heightOffset: 0,\n  autoExpand: false,\n  callToActionIconHidden: false,\n  agentIconSize: \"40px\",\n  headerIconSize: \"40px\",\n  closeButtonSize: \"32px\",\n  // Zero out browser-default <button> padding so the icon gets the full\n  // 32x32 content box, matching clearChat.paddingX/Y below. Without this,\n  // UA stylesheets add ~1-2px vertical and ~6px horizontal padding that\n  // eats into the border-box width and shrinks the rendered icon.\n  closeButtonPaddingX: \"0px\",\n  closeButtonPaddingY: \"0px\",\n  callToActionIconName: \"arrow-up-right\",\n  callToActionIconText: \"\",\n  callToActionIconSize: \"32px\",\n  callToActionIconPadding: \"5px\",\n  callToActionIconColor: undefined,\n  callToActionIconBackgroundColor: undefined,\n  // closeButtonColor / clearChat.iconColor omitted so theme.components.header.actionIconForeground applies.\n  closeButtonBackgroundColor: \"transparent\",\n  clearChat: {\n    backgroundColor: \"transparent\",\n    borderColor: \"transparent\",\n    enabled: true,\n    placement: \"inline\",\n    iconName: \"refresh-cw\",\n    size: \"32px\",\n    showTooltip: true,\n    tooltipText: \"Clear chat\",\n    paddingX: \"0px\",\n    paddingY: \"0px\",\n  },\n  headerIconHidden: false,\n  border: undefined,\n  shadow:\n    \"0 10px 15px -3px rgba(0, 0, 0, 0.1), 0 4px 6px -4px rgba(0, 0, 0, 0.1)\",\n};\n\n/**\n * Default widget configuration\n * Single source of truth for all default values\n */\nexport const DEFAULT_WIDGET_CONFIG: Partial<AgentWidgetConfig> = {\n  apiUrl: \"https://api.runtype.com/api/chat/dispatch\",\n  // Client token mode defaults (optional, only used when clientToken is set)\n  clientToken: undefined,\n  agentId: undefined,\n  target: undefined,\n  theme: undefined,\n  darkTheme: undefined,\n  colorScheme: \"light\",\n  launcher: DEFAULT_LAUNCHER_CONFIG,\n  copy: {\n    welcomeTitle: \"Hello 👋\",\n    welcomeSubtitle: \"Ask anything about your account or products.\",\n    inputPlaceholder: \"How can I help...\",\n    sendButtonLabel: \"Send\",\n  },\n  sendButton: {\n    borderWidth: \"0px\",\n    paddingX: \"12px\",\n    paddingY: \"10px\",\n    borderColor: undefined,\n    useIcon: true,\n    iconText: \"↑\",\n    size: \"40px\",\n    showTooltip: true,\n    tooltipText: \"Send message\",\n    iconName: \"send\",\n  },\n  statusIndicator: {\n    visible: true,\n    idleText: \"Online\",\n    connectingText: \"Connecting…\",\n    connectedText: \"Streaming…\",\n    errorText: \"Offline\",\n  },\n  voiceRecognition: {\n    enabled: true,\n    pauseDuration: 2000,\n    iconName: \"mic\",\n    iconSize: \"39px\",\n    borderWidth: \"0px\",\n    paddingX: \"9px\",\n    paddingY: \"14px\",\n    iconColor: undefined,\n    backgroundColor: \"transparent\",\n    borderColor: \"transparent\",\n    recordingIconColor: undefined,\n    recordingBackgroundColor: undefined,\n    recordingBorderColor: \"transparent\",\n    showTooltip: true,\n    tooltipText: \"Start voice recognition\",\n  },\n  features: {\n    showReasoning: true,\n    showToolCalls: true,\n    scrollToBottom: {\n      enabled: true,\n      iconName: \"arrow-down\",\n      label: \"\",\n    },\n    scrollBehavior: {\n      // ChatGPT-style default: pin the just-sent message near the top and let\n      // the reply stream into the space below. Restore the old \"stick to the\n      // bottom\" behavior with `mode: \"follow\"`.\n      mode: \"anchor-top\",\n      anchorTopOffset: 16,\n      // Surface the unread count + \"streaming below\" hint while pinned, so the\n      // reader still sees activity arriving off-screen under the pinned turn.\n      // (Suppressed by default historically; opted on alongside the anchor-top\n      // default so the default UX keeps the affordance.)\n      showActivityWhilePinned: true,\n    },\n    toolCallDisplay: {\n      collapsedMode: \"tool-call\",\n      activePreview: false,\n      grouped: false,\n      previewMaxLines: 3,\n      expandable: true,\n      loadingAnimation: \"none\",\n    },\n    reasoningDisplay: {\n      activePreview: false,\n      previewMaxLines: 3,\n      expandable: true,\n      loadingAnimation: \"none\",\n    },\n    streamAnimation: {\n      type: \"none\",\n      placeholder: \"none\",\n      speed: 120,\n      duration: 1800,\n    },\n    askUserQuestion: {\n      enabled: true,\n      slideInMs: 180,\n      freeTextLabel: \"Other…\",\n      freeTextPlaceholder: \"Type your answer…\",\n      submitLabel: \"Send\",\n    },\n  },\n  suggestionChips: [\n    \"What can you help me with?\",\n    \"Tell me about your features\",\n    \"How does this work?\",\n  ],\n  suggestionChipsConfig: {\n    fontFamily: \"sans-serif\",\n    fontWeight: \"500\",\n    paddingX: \"12px\",\n    paddingY: \"6px\",\n  },\n  layout: {\n    header: {\n      layout: \"default\",\n      showIcon: true,\n      showTitle: true,\n      showSubtitle: true,\n      showCloseButton: true,\n      showClearChat: true,\n    },\n    messages: {\n      layout: \"bubble\",\n      avatar: {\n        show: false,\n        position: \"left\",\n      },\n      timestamp: {\n        show: false,\n        position: \"below\",\n      },\n      groupConsecutive: false,\n    },\n    slots: {},\n  },\n  markdown: {\n    options: {\n      gfm: true,\n      breaks: true,\n    },\n    disableDefaultStyles: false,\n  },\n  messageActions: {\n    enabled: true,\n    showCopy: true,\n    showUpvote: false, // Requires backend - disabled by default\n    showDownvote: false, // Requires backend - disabled by default\n    visibility: \"hover\",\n    align: \"right\",\n    layout: \"pill-inside\",\n  },\n  debug: false,\n};\n\nfunction mergeThemePartials(\n  base: DeepPartial<PersonaTheme> | undefined,\n  override: DeepPartial<PersonaTheme> | undefined\n): DeepPartial<PersonaTheme> | undefined {\n  if (!base && !override) return undefined;\n  if (!base) return override;\n  if (!override) return base;\n  return deepMerge(\n    base as Record<string, unknown>,\n    override as Record<string, unknown>\n  ) as DeepPartial<PersonaTheme>;\n}\n\n/**\n * Helper to deep merge user config with defaults\n * This ensures all default values are present while allowing selective overrides\n */\nexport function mergeWithDefaults(\n  config?: Partial<AgentWidgetConfig>\n): Partial<AgentWidgetConfig> {\n  if (!config) return DEFAULT_WIDGET_CONFIG;\n\n  return {\n    ...DEFAULT_WIDGET_CONFIG,\n    ...config,\n    theme: mergeThemePartials(DEFAULT_WIDGET_CONFIG.theme, config.theme),\n    darkTheme: mergeThemePartials(DEFAULT_WIDGET_CONFIG.darkTheme, config.darkTheme),\n    launcher: {\n      ...DEFAULT_WIDGET_CONFIG.launcher,\n      ...config.launcher,\n      dock: {\n        ...DEFAULT_WIDGET_CONFIG.launcher?.dock,\n        ...config.launcher?.dock,\n      },\n      clearChat: {\n        ...DEFAULT_WIDGET_CONFIG.launcher?.clearChat,\n        ...config.launcher?.clearChat,\n      },\n    },\n    copy: {\n      ...DEFAULT_WIDGET_CONFIG.copy,\n      ...config.copy,\n    },\n    sendButton: {\n      ...DEFAULT_WIDGET_CONFIG.sendButton,\n      ...config.sendButton,\n    },\n    statusIndicator: {\n      ...DEFAULT_WIDGET_CONFIG.statusIndicator,\n      ...config.statusIndicator,\n    },\n    voiceRecognition: {\n      ...DEFAULT_WIDGET_CONFIG.voiceRecognition,\n      ...config.voiceRecognition,\n    },\n    features: (() => {\n      const da = DEFAULT_WIDGET_CONFIG.features?.artifacts;\n      const ca = config.features?.artifacts;\n      const dsb = DEFAULT_WIDGET_CONFIG.features?.scrollToBottom;\n      const csb = config.features?.scrollToBottom;\n      const dsc = DEFAULT_WIDGET_CONFIG.features?.scrollBehavior;\n      const csc = config.features?.scrollBehavior;\n      const dsa = DEFAULT_WIDGET_CONFIG.features?.streamAnimation;\n      const csa = config.features?.streamAnimation;\n      const dau = DEFAULT_WIDGET_CONFIG.features?.askUserQuestion;\n      const cau = config.features?.askUserQuestion;\n      const mergedArtifacts =\n        da === undefined && ca === undefined\n          ? undefined\n          : {\n              ...da,\n              ...ca,\n              layout: {\n                ...da?.layout,\n                ...ca?.layout,\n              },\n            };\n      const mergedScrollToBottom =\n        dsb === undefined && csb === undefined\n          ? undefined\n          : {\n              ...dsb,\n              ...csb,\n            };\n      const mergedScrollBehavior =\n        dsc === undefined && csc === undefined\n          ? undefined\n          : {\n              ...dsc,\n              ...csc,\n            };\n      const mergedStreamAnimation =\n        dsa === undefined && csa === undefined\n          ? undefined\n          : {\n              ...dsa,\n              ...csa,\n            };\n      const mergedAskUserQuestion =\n        dau === undefined && cau === undefined\n          ? undefined\n          : {\n              ...dau,\n              ...cau,\n              styles: {\n                ...dau?.styles,\n                ...cau?.styles,\n              },\n            };\n      return {\n        ...DEFAULT_WIDGET_CONFIG.features,\n        ...config.features,\n        ...(mergedScrollToBottom !== undefined ? { scrollToBottom: mergedScrollToBottom } : {}),\n        ...(mergedScrollBehavior !== undefined ? { scrollBehavior: mergedScrollBehavior } : {}),\n        ...(mergedArtifacts !== undefined ? { artifacts: mergedArtifacts } : {}),\n        ...(mergedStreamAnimation !== undefined ? { streamAnimation: mergedStreamAnimation } : {}),\n        ...(mergedAskUserQuestion !== undefined ? { askUserQuestion: mergedAskUserQuestion } : {}),\n      };\n    })(),\n    suggestionChips: config.suggestionChips ?? DEFAULT_WIDGET_CONFIG.suggestionChips,\n    suggestionChipsConfig: {\n      ...DEFAULT_WIDGET_CONFIG.suggestionChipsConfig,\n      ...config.suggestionChipsConfig,\n    },\n    layout: {\n      ...DEFAULT_WIDGET_CONFIG.layout,\n      ...config.layout,\n      header: {\n        ...DEFAULT_WIDGET_CONFIG.layout?.header,\n        ...config.layout?.header,\n      },\n      messages: {\n        ...DEFAULT_WIDGET_CONFIG.layout?.messages,\n        ...config.layout?.messages,\n        avatar: {\n          ...DEFAULT_WIDGET_CONFIG.layout?.messages?.avatar,\n          ...config.layout?.messages?.avatar,\n        },\n        timestamp: {\n          ...DEFAULT_WIDGET_CONFIG.layout?.messages?.timestamp,\n          ...config.layout?.messages?.timestamp,\n        },\n      },\n      slots: {\n        ...DEFAULT_WIDGET_CONFIG.layout?.slots,\n        ...config.layout?.slots,\n      },\n    },\n    markdown: {\n      ...DEFAULT_WIDGET_CONFIG.markdown,\n      ...config.markdown,\n      options: {\n        ...DEFAULT_WIDGET_CONFIG.markdown?.options,\n        ...config.markdown?.options,\n      },\n    },\n    messageActions: {\n      ...DEFAULT_WIDGET_CONFIG.messageActions,\n      ...config.messageActions,\n    },\n  };\n}\n","import type {\n  DeepPartial,\n  PersonaTheme,\n  ResolvedToken,\n  ThemeValidationResult,\n  ThemeValidationError,\n  CreateThemeOptions,\n  ComponentTokens,\n  SemanticTokens,\n} from '../types/theme';\nimport {\n  DEFAULT_FLOATING_LAUNCHER_MAX_WIDTH,\n  DEFAULT_FLOATING_LAUNCHER_WIDTH,\n} from '../defaults';\n\nexport const DEFAULT_PALETTE = {\n  colors: {\n    primary: {\n      50: '#ffffff',\n      100: '#f5f5f5',\n      200: '#d4d4d4',\n      300: '#a3a3a3',\n      400: '#737373',\n      500: '#171717',\n      600: '#0f0f0f',\n      700: '#0a0a0a',\n      800: '#050505',\n      900: '#030303',\n      950: '#000000',\n    },\n    secondary: {\n      50: '#f5f3ff',\n      100: '#ede9fe',\n      200: '#ddd6fe',\n      300: '#c4b5fd',\n      400: '#a78bfa',\n      500: '#8b5cf6',\n      600: '#7c3aed',\n      700: '#6d28d9',\n      800: '#5b21b6',\n      900: '#4c1d95',\n      950: '#2e1065',\n    },\n    accent: {\n      50: '#ecfeff',\n      100: '#cffafe',\n      200: '#a5f3fc',\n      300: '#67e8f9',\n      400: '#22d3ee',\n      500: '#06b6d4',\n      600: '#0891b2',\n      700: '#0e7490',\n      800: '#155e75',\n      900: '#164e63',\n      950: '#083344',\n    },\n    gray: {\n      50: '#f9fafb',\n      100: '#f3f4f6',\n      200: '#e5e7eb',\n      300: '#d1d5db',\n      400: '#9ca3af',\n      500: '#6b7280',\n      600: '#4b5563',\n      700: '#374151',\n      800: '#1f2937',\n      900: '#111827',\n      950: '#030712',\n    },\n    success: {\n      50: '#f0fdf4',\n      100: '#dcfce7',\n      200: '#bbf7d0',\n      300: '#86efac',\n      400: '#4ade80',\n      500: '#22c55e',\n      600: '#16a34a',\n      700: '#15803d',\n      800: '#166534',\n      900: '#14532d',\n    },\n    warning: {\n      50: '#fefce8',\n      100: '#fef9c3',\n      200: '#fef08a',\n      300: '#fde047',\n      400: '#facc15',\n      500: '#eab308',\n      600: '#ca8a04',\n      700: '#a16207',\n      800: '#854d0e',\n      900: '#713f12',\n    },\n    error: {\n      50: '#fef2f2',\n      100: '#fee2e2',\n      200: '#fecaca',\n      300: '#fca5a5',\n      400: '#f87171',\n      500: '#ef4444',\n      600: '#dc2626',\n      700: '#b91c1c',\n      800: '#991b1b',\n      900: '#7f1d1d',\n    },\n    info: {\n      50: '#eff6ff',\n      100: '#dbeafe',\n      200: '#bfdbfe',\n      300: '#93c5fd',\n      400: '#60a5fa',\n      500: '#3b82f6',\n      600: '#2563eb',\n      700: '#1d4ed8',\n      800: '#1e40af',\n      900: '#1e3a8a',\n      950: '#172554',\n    },\n  },\n  spacing: {\n    0: '0px',\n    1: '0.25rem',\n    2: '0.5rem',\n    3: '0.75rem',\n    4: '1rem',\n    5: '1.25rem',\n    6: '1.5rem',\n    8: '2rem',\n    10: '2.5rem',\n    12: '3rem',\n    16: '4rem',\n    20: '5rem',\n    24: '6rem',\n    32: '8rem',\n    40: '10rem',\n    48: '12rem',\n    56: '14rem',\n    64: '16rem',\n  },\n  typography: {\n    fontFamily: {\n      sans: 'system-ui, -apple-system, BlinkMacSystemFont, \"Segoe UI\", Roboto, sans-serif',\n      serif: 'Georgia, Cambria, \"Times New Roman\", Times, serif',\n      mono: 'ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, monospace',\n    },\n    fontSize: {\n      xs: '0.75rem',\n      sm: '0.875rem',\n      base: '1rem',\n      lg: '1.125rem',\n      xl: '1.25rem',\n      '2xl': '1.5rem',\n      '3xl': '1.875rem',\n      '4xl': '2.25rem',\n    },\n    fontWeight: {\n      normal: '400',\n      medium: '500',\n      semibold: '600',\n      bold: '700',\n    },\n    lineHeight: {\n      tight: '1.25',\n      normal: '1.5',\n      relaxed: '1.625',\n    },\n  },\n  shadows: {\n    none: 'none',\n    sm: '0 1px 2px 0 rgb(0 0 0 / 0.05)',\n    md: '0 4px 6px -1px rgb(0 0 0 / 0.1), 0 2px 4px -2px rgb(0 0 0 / 0.1)',\n    lg: '0 10px 15px -3px rgb(0 0 0 / 0.1), 0 4px 6px -4px rgb(0 0 0 / 0.1)',\n    xl: '0 20px 25px -5px rgb(0 0 0 / 0.1), 0 8px 10px -6px rgb(0 0 0 / 0.1)',\n    '2xl': '0 25px 50px -12px rgb(0 0 0 / 0.25)',\n  },\n  borders: {\n    none: 'none',\n    sm: '1px solid',\n    md: '2px solid',\n    lg: '4px solid',\n  },\n  radius: {\n    none: '0px',\n    sm: '0.125rem',\n    md: '0.375rem',\n    lg: '0.5rem',\n    xl: '0.75rem',\n    '2xl': '1rem',\n    full: '9999px',\n  },\n};\n\nexport const DEFAULT_SEMANTIC: SemanticTokens = {\n  colors: {\n    primary: 'palette.colors.primary.500',\n    secondary: 'palette.colors.secondary.500',\n    // Links/Focus role: solid primary\n    accent: 'palette.colors.primary.600',\n    // Surfaces role: soft gray\n    surface: 'palette.colors.gray.50',\n    background: 'palette.colors.gray.50',\n    container: 'palette.colors.gray.50',\n    text: 'palette.colors.gray.900',\n    textMuted: 'palette.colors.gray.500',\n    textInverse: 'palette.colors.gray.50',\n    // Borders role: soft gray\n    border: 'palette.colors.gray.200',\n    divider: 'palette.colors.gray.200',\n    interactive: {\n      // Primary Actions role: solid primary\n      default: 'palette.colors.primary.600',\n      hover: 'palette.colors.primary.700',\n      // Links/Focus role: solid primary\n      focus: 'palette.colors.primary.600',\n      active: 'palette.colors.primary.600',\n      disabled: 'palette.colors.gray.300',\n    },\n    feedback: {\n      success: 'palette.colors.success.500',\n      warning: 'palette.colors.warning.500',\n      error: 'palette.colors.error.500',\n      info: 'palette.colors.info.500',\n    },\n  },\n  spacing: {\n    xs: 'palette.spacing.1',\n    sm: 'palette.spacing.2',\n    md: 'palette.spacing.4',\n    lg: 'palette.spacing.6',\n    xl: 'palette.spacing.8',\n    '2xl': 'palette.spacing.10',\n  },\n  typography: {\n    fontFamily: 'palette.typography.fontFamily.sans',\n    fontSize: 'palette.typography.fontSize.base',\n    fontWeight: 'palette.typography.fontWeight.normal',\n    lineHeight: 'palette.typography.lineHeight.normal',\n  },\n};\n\nexport const DEFAULT_COMPONENTS: ComponentTokens = {\n  button: {\n    primary: {\n      // Primary Actions role: solid primary\n      background: 'palette.colors.primary.500',\n      foreground: 'palette.colors.primary.50',\n      borderRadius: 'palette.radius.lg',\n      padding: 'semantic.spacing.md',\n    },\n    secondary: {\n      background: 'semantic.colors.surface',\n      foreground: 'semantic.colors.secondary',\n      borderRadius: 'palette.radius.lg',\n      padding: 'semantic.spacing.md',\n    },\n    ghost: {\n      background: 'transparent',\n      foreground: 'semantic.colors.text',\n      borderRadius: 'palette.radius.md',\n      padding: 'semantic.spacing.sm',\n    },\n  },\n  input: {\n    // Input role: soft gray\n    background: 'palette.colors.gray.50',\n    placeholder: 'palette.colors.gray.400',\n    borderRadius: 'palette.radius.lg',\n    padding: 'semantic.spacing.md',\n    focus: {\n      border: 'palette.colors.gray.400',\n      ring: 'palette.colors.gray.400',\n    },\n  },\n  launcher: {\n    background: 'palette.colors.primary.500',\n    foreground: 'palette.colors.primary.50',\n    border: 'palette.colors.gray.200',\n    size: '60px',\n    iconSize: '28px',\n    borderRadius: 'palette.radius.full',\n    shadow: 'palette.shadows.lg',\n  },\n  panel: {\n    width: DEFAULT_FLOATING_LAUNCHER_WIDTH,\n    maxWidth: DEFAULT_FLOATING_LAUNCHER_MAX_WIDTH,\n    height: '600px',\n    maxHeight: 'calc(100vh - 80px)',\n    borderRadius: 'palette.radius.xl',\n    shadow: 'palette.shadows.xl',\n  },\n  header: {\n    // Header role: solid primary\n    background: 'palette.colors.primary.500',\n    border: 'palette.colors.primary.600',\n    borderRadius: 'palette.radius.xl palette.radius.xl 0 0',\n    padding: 'semantic.spacing.md',\n    iconBackground: 'palette.colors.primary.600',\n    iconForeground: 'palette.colors.primary.50',\n    titleForeground: 'palette.colors.primary.50',\n    subtitleForeground: 'palette.colors.primary.200',\n    actionIconForeground: 'palette.colors.primary.200',\n  },\n  message: {\n    user: {\n      // User Messages role: solid primary\n      background: 'palette.colors.primary.500',\n      text: 'palette.colors.primary.50',\n      borderRadius: 'palette.radius.lg',\n      shadow: 'palette.shadows.sm',\n    },\n    assistant: {\n      // Assistant Messages role: soft gray\n      background: 'palette.colors.gray.50',\n      text: 'palette.colors.gray.900',\n      borderRadius: 'palette.radius.lg',\n      border: 'palette.colors.gray.200',\n      shadow: 'palette.shadows.sm',\n    },\n    border: 'semantic.colors.border',\n  },\n  introCard: {\n    // Defaults preserve the legacy `persona-shadow-sm` look exactly so existing\n    // pages render unchanged when no token is set.\n    background: 'semantic.colors.surface',\n    borderRadius: 'palette.radius.2xl',\n    padding: 'semantic.spacing.lg',\n    shadow: '0 5px 15px rgba(15, 23, 42, 0.08)',\n  },\n  toolBubble: {\n    shadow: 'palette.shadows.sm',\n  },\n  reasoningBubble: {\n    shadow: 'palette.shadows.sm',\n  },\n  composer: {\n    shadow: 'palette.shadows.none',\n  },\n  markdown: {\n    inlineCode: {\n      background: 'palette.colors.gray.50',\n      foreground: 'palette.colors.gray.900',\n    },\n    link: {\n      // Links/Focus role: solid primary\n      foreground: 'palette.colors.primary.600',\n    },\n    prose: {\n      fontFamily: 'inherit',\n    },\n    codeBlock: {\n      background: 'semantic.colors.container',\n      borderColor: 'semantic.colors.border',\n      textColor: 'inherit',\n    },\n    table: {\n      headerBackground: 'semantic.colors.container',\n      borderColor: 'semantic.colors.border',\n    },\n    hr: {\n      color: 'semantic.colors.divider',\n    },\n    blockquote: {\n      borderColor: 'palette.colors.gray.900',\n      background: 'transparent',\n      textColor: 'palette.colors.gray.500',\n    },\n  },\n  collapsibleWidget: {\n    container: 'palette.colors.gray.50',\n    surface: 'semantic.colors.surface',\n    border: 'semantic.colors.border',\n  },\n  voice: {\n    recording: {\n      indicator: 'palette.colors.error.500',\n      background: 'palette.colors.error.50',\n      border: 'palette.colors.error.200',\n    },\n    processing: {\n      icon: 'palette.colors.primary.500',\n      background: 'palette.colors.primary.50',\n    },\n    speaking: {\n      icon: 'palette.colors.success.500',\n    },\n  },\n  // Neutral surface card (components/approval-actions.ts). The primary action\n  // anchors to the brand primary; deny is a neutral tinted button. Consumers who\n  // themed these (or set config.approval.* color overrides) still win.\n  approval: {\n    requested: {\n      background: 'semantic.colors.surface',\n      border: 'semantic.colors.border',\n      text: 'palette.colors.gray.900',\n      shadow: '0 1px 2px 0 rgba(11, 11, 11, 0.06), 0 2px 8px 0 rgba(11, 11, 11, 0.04)',\n    },\n    approve: {\n      background: 'semantic.colors.primary',\n      foreground: 'semantic.colors.textInverse',\n      borderRadius: 'palette.radius.md',\n      padding: 'semantic.spacing.sm',\n    },\n    deny: {\n      background: 'semantic.colors.container',\n      foreground: 'semantic.colors.text',\n      borderRadius: 'palette.radius.md',\n      padding: 'semantic.spacing.sm',\n    },\n  },\n  attachment: {\n    image: {\n      background: 'palette.colors.gray.100',\n      border: 'palette.colors.gray.200',\n    },\n  },\n  scrollToBottom: {\n    background: 'components.button.primary.background',\n    foreground: 'components.button.primary.foreground',\n    border: 'semantic.colors.primary',\n    size: '40px',\n    borderRadius: 'palette.radius.full',\n    shadow: 'palette.shadows.sm',\n    padding: '0.5rem 0.875rem',\n    gap: '0.5rem',\n    fontSize: '0.875rem',\n    iconSize: '14px',\n  },\n  artifact: {\n    pane: {\n      background: 'semantic.colors.container',\n      toolbarBackground: 'semantic.colors.container',\n    },\n  },\n};\n\nexport function resolveTokenValue(theme: PersonaTheme, path: string): string | undefined {\n  if (\n    !path.startsWith('palette.') &&\n    !path.startsWith('semantic.') &&\n    !path.startsWith('components.')\n  ) {\n    return path;\n  }\n\n  const parts = path.split('.');\n  let current: any = theme;\n\n  for (const part of parts) {\n    if (current === undefined || current === null) {\n      return undefined;\n    }\n    current = current[part];\n  }\n\n  if (\n    typeof current === 'string' &&\n    (current.startsWith('palette.') ||\n      current.startsWith('semantic.') ||\n      current.startsWith('components.'))\n  ) {\n    return resolveTokenValue(theme, current);\n  }\n\n  return current;\n}\n\nexport function resolveTokens(theme: PersonaTheme): Record<string, ResolvedToken> {\n  const resolved: Record<string, ResolvedToken> = {};\n\n  function resolveObject(obj: any, prefix: string) {\n    for (const [key, value] of Object.entries(obj)) {\n      const path = `${prefix}.${key}`;\n\n      if (typeof value === 'string') {\n        const resolvedValue = resolveTokenValue(theme, value);\n        if (resolvedValue !== undefined) {\n          resolved[path] = {\n            path,\n            value: resolvedValue,\n            type:\n              prefix.includes('color')\n                ? 'color'\n                : prefix.includes('spacing')\n                  ? 'spacing'\n                  : prefix.includes('typography')\n                    ? 'typography'\n                    : prefix.includes('shadow')\n                      ? 'shadow'\n                      : prefix.includes('border')\n                        ? 'border'\n                        : 'color',\n          };\n        }\n      } else if (typeof value === 'object' && value !== null) {\n        resolveObject(value, path);\n      }\n    }\n  }\n\n  resolveObject(theme.palette, 'palette');\n  resolveObject(theme.semantic, 'semantic');\n  resolveObject(theme.components, 'components');\n\n  return resolved;\n}\n\nexport function validateTheme(theme: Partial<PersonaTheme>): ThemeValidationResult {\n  const errors: ThemeValidationError[] = [];\n  const warnings: ThemeValidationError[] = [];\n\n  if (!theme.palette) {\n    errors.push({\n      path: 'palette',\n      message: 'Theme must include a palette',\n      severity: 'error',\n    });\n  }\n\n  if (!theme.semantic) {\n    warnings.push({\n      path: 'semantic',\n      message: 'No semantic tokens defined - defaults will be used',\n      severity: 'warning',\n    });\n  }\n\n  if (!theme.components) {\n    warnings.push({\n      path: 'components',\n      message: 'No component tokens defined - defaults will be used',\n      severity: 'warning',\n    });\n  }\n\n  return {\n    valid: errors.length === 0,\n    errors,\n    warnings,\n  };\n}\n\nfunction mergeRecords(\n  base: Record<string, unknown>,\n  override: Record<string, unknown>\n): Record<string, unknown> {\n  const result = { ...base };\n  for (const [key, value] of Object.entries(override)) {\n    const existing = result[key];\n    if (existing && typeof existing === 'object' && !Array.isArray(existing) &&\n        value && typeof value === 'object' && !Array.isArray(value)) {\n      result[key] = mergeRecords(\n        existing as Record<string, unknown>,\n        value as Record<string, unknown>\n      );\n    } else {\n      result[key] = value;\n    }\n  }\n  return result;\n}\n\nfunction deepMergeComponents(\n  base: ComponentTokens,\n  override?: Partial<ComponentTokens>\n): ComponentTokens {\n  if (!override) return base;\n  return mergeRecords(\n    base as unknown as Record<string, unknown>,\n    override as unknown as Record<string, unknown>\n  ) as unknown as ComponentTokens;\n}\n\nexport function createTheme(\n  userConfig?: DeepPartial<PersonaTheme>,\n  options: CreateThemeOptions = {}\n): PersonaTheme {\n  const baseTheme: PersonaTheme = {\n    palette: DEFAULT_PALETTE as PersonaTheme['palette'],\n    semantic: DEFAULT_SEMANTIC as PersonaTheme['semantic'],\n    components: DEFAULT_COMPONENTS as PersonaTheme['components'],\n  };\n\n  let theme: PersonaTheme = {\n    palette: {\n      ...baseTheme.palette,\n      ...userConfig?.palette,\n      colors: {\n        ...baseTheme.palette.colors,\n        ...userConfig?.palette?.colors,\n      },\n      spacing: {\n        ...baseTheme.palette.spacing,\n        ...userConfig?.palette?.spacing,\n      },\n      typography: {\n        ...baseTheme.palette.typography,\n        ...userConfig?.palette?.typography,\n      },\n      shadows: {\n        ...baseTheme.palette.shadows,\n        ...userConfig?.palette?.shadows,\n      },\n      borders: {\n        ...baseTheme.palette.borders,\n        ...userConfig?.palette?.borders,\n      },\n      radius: {\n        ...baseTheme.palette.radius,\n        ...userConfig?.palette?.radius,\n      },\n    },\n    semantic: {\n      ...baseTheme.semantic,\n      ...userConfig?.semantic,\n      colors: {\n        ...baseTheme.semantic.colors,\n        ...userConfig?.semantic?.colors,\n        interactive: {\n          ...baseTheme.semantic.colors.interactive,\n          ...userConfig?.semantic?.colors?.interactive,\n        },\n        feedback: {\n          ...baseTheme.semantic.colors.feedback,\n          ...userConfig?.semantic?.colors?.feedback,\n        },\n      },\n      spacing: {\n        ...baseTheme.semantic.spacing,\n        ...userConfig?.semantic?.spacing,\n      },\n      typography: {\n        ...baseTheme.semantic.typography,\n        ...userConfig?.semantic?.typography,\n      },\n    },\n    components: deepMergeComponents(\n      baseTheme.components,\n      userConfig?.components as Partial<ComponentTokens> | undefined\n    ),\n  } as PersonaTheme;\n\n  if (options.validate !== false) {\n    const validation = validateTheme(theme);\n    if (!validation.valid) {\n      throw new Error(\n        `Theme validation failed: ${validation.errors.map((e) => e.message).join(', ')}`\n      );\n    }\n  }\n\n  if (options.plugins) {\n    for (const plugin of options.plugins) {\n      theme = plugin.transform(theme);\n    }\n  }\n\n  return theme;\n}\n\nexport function themeToCssVariables(theme: PersonaTheme): Record<string, string> {\n  const resolved = resolveTokens(theme);\n  const cssVars: Record<string, string> = {};\n\n  for (const [path, token] of Object.entries(resolved)) {\n    const varName = path.replace(/\\./g, '-');\n    cssVars[`--persona-${varName}`] = token.value;\n  }\n\n  cssVars['--persona-primary'] = cssVars['--persona-semantic-colors-primary'] ?? cssVars['--persona-palette-colors-primary-500'];\n  cssVars['--persona-secondary'] = cssVars['--persona-semantic-colors-secondary'] ?? cssVars['--persona-palette-colors-secondary-500'];\n  cssVars['--persona-accent'] = cssVars['--persona-semantic-colors-accent'] ?? cssVars['--persona-palette-colors-accent-500'];\n  cssVars['--persona-surface'] = cssVars['--persona-semantic-colors-surface'] ?? cssVars['--persona-palette-colors-gray-50'];\n  cssVars['--persona-background'] = cssVars['--persona-semantic-colors-background'] ?? cssVars['--persona-palette-colors-gray-50'];\n  cssVars['--persona-container'] = cssVars['--persona-semantic-colors-container'] ?? cssVars['--persona-palette-colors-gray-100'];\n  cssVars['--persona-text'] = cssVars['--persona-semantic-colors-text'] ?? cssVars['--persona-palette-colors-gray-900'];\n  cssVars['--persona-text-muted'] = cssVars['--persona-semantic-colors-text-muted'] ?? cssVars['--persona-palette-colors-gray-500'];\n  cssVars['--persona-text-inverse'] = cssVars['--persona-semantic-colors-text-inverse'] ?? cssVars['--persona-palette-colors-gray-50'];\n  cssVars['--persona-border'] = cssVars['--persona-semantic-colors-border'] ?? cssVars['--persona-palette-colors-gray-200'];\n  cssVars['--persona-divider'] = cssVars['--persona-semantic-colors-divider'] ?? cssVars['--persona-palette-colors-gray-200'];\n  cssVars['--persona-muted'] = cssVars['--persona-text-muted'];\n\n  cssVars['--persona-voice-recording-indicator'] = cssVars['--persona-components-voice-recording-indicator'] ?? cssVars['--persona-palette-colors-error-500'];\n  cssVars['--persona-voice-recording-bg'] = cssVars['--persona-components-voice-recording-background'] ?? cssVars['--persona-palette-colors-error-50'];\n  cssVars['--persona-voice-processing-icon'] = cssVars['--persona-components-voice-processing-icon'] ?? cssVars['--persona-palette-colors-primary-500'];\n  cssVars['--persona-voice-speaking-icon'] = cssVars['--persona-components-voice-speaking-icon'] ?? cssVars['--persona-palette-colors-success-500'];\n\n  cssVars['--persona-approval-bg'] = cssVars['--persona-components-approval-requested-background'] ?? cssVars['--persona-surface'];\n  cssVars['--persona-approval-border'] = cssVars['--persona-components-approval-requested-border'] ?? cssVars['--persona-border'];\n  cssVars['--persona-approval-text'] = cssVars['--persona-components-approval-requested-text'] ?? cssVars['--persona-palette-colors-gray-900'];\n  cssVars['--persona-approval-shadow'] = cssVars['--persona-components-approval-requested-shadow'] ?? '0 1px 2px 0 rgba(11, 11, 11, 0.06), 0 2px 8px 0 rgba(11, 11, 11, 0.04)';\n  cssVars['--persona-approval-approve-bg'] = cssVars['--persona-components-approval-approve-background'] ?? cssVars['--persona-button-primary-bg'];\n  cssVars['--persona-approval-deny-bg'] = cssVars['--persona-components-approval-deny-background'] ?? cssVars['--persona-container'];\n\n  cssVars['--persona-attachment-image-bg'] = cssVars['--persona-components-attachment-image-background'] ?? cssVars['--persona-palette-colors-gray-100'];\n  cssVars['--persona-attachment-image-border'] = cssVars['--persona-components-attachment-image-border'] ?? cssVars['--persona-palette-colors-gray-200'];\n\n  // Typography shorthand aliases\n  cssVars['--persona-font-family'] = cssVars['--persona-semantic-typography-fontFamily'] ?? cssVars['--persona-palette-typography-fontFamily-sans'];\n  cssVars['--persona-font-size'] = cssVars['--persona-semantic-typography-fontSize'] ?? cssVars['--persona-palette-typography-fontSize-base'];\n  cssVars['--persona-font-weight'] = cssVars['--persona-semantic-typography-fontWeight'] ?? cssVars['--persona-palette-typography-fontWeight-normal'];\n  cssVars['--persona-line-height'] = cssVars['--persona-semantic-typography-lineHeight'] ?? cssVars['--persona-palette-typography-lineHeight-normal'];\n\n  cssVars['--persona-input-font-family'] = cssVars['--persona-font-family'];\n  cssVars['--persona-input-font-weight'] = cssVars['--persona-font-weight'];\n\n  // Radius aliases used throughout the existing widget CSS.\n  cssVars['--persona-radius-sm'] = cssVars['--persona-palette-radius-sm'] ?? '0.125rem';\n  cssVars['--persona-radius-md'] = cssVars['--persona-palette-radius-md'] ?? '0.375rem';\n  cssVars['--persona-radius-lg'] = cssVars['--persona-palette-radius-lg'] ?? '0.5rem';\n  cssVars['--persona-radius-xl'] = cssVars['--persona-palette-radius-xl'] ?? '0.75rem';\n  cssVars['--persona-radius-full'] = cssVars['--persona-palette-radius-full'] ?? '9999px';\n  cssVars['--persona-launcher-radius'] =\n    cssVars['--persona-components-launcher-borderRadius'] ??\n    cssVars['--persona-palette-radius-full'] ??\n    '9999px';\n  cssVars['--persona-launcher-bg'] =\n    cssVars['--persona-components-launcher-background'] ??\n    cssVars['--persona-primary'];\n  cssVars['--persona-launcher-fg'] =\n    cssVars['--persona-components-launcher-foreground'] ??\n    cssVars['--persona-text-inverse'];\n  cssVars['--persona-launcher-border'] =\n    cssVars['--persona-components-launcher-border'] ??\n    cssVars['--persona-border'];\n  cssVars['--persona-button-primary-bg'] =\n    cssVars['--persona-components-button-primary-background'] ??\n    cssVars['--persona-primary'];\n  cssVars['--persona-button-primary-fg'] =\n    cssVars['--persona-components-button-primary-foreground'] ??\n    cssVars['--persona-text-inverse'];\n  cssVars['--persona-button-radius'] =\n    cssVars['--persona-components-button-primary-borderRadius'] ??\n    cssVars['--persona-palette-radius-full'] ??\n    '9999px';\n  cssVars['--persona-panel-radius'] =\n    cssVars['--persona-components-panel-borderRadius'] ??\n    cssVars['--persona-radius-xl'] ??\n    '0.75rem';\n  cssVars['--persona-panel-border'] =\n    cssVars['--persona-components-panel-border'] ?? `1px solid ${cssVars['--persona-border']}`;\n  cssVars['--persona-panel-shadow'] =\n    cssVars['--persona-components-panel-shadow'] ??\n    cssVars['--persona-palette-shadows-xl'] ??\n    '0 25px 50px -12px rgba(0, 0, 0, 0.25)';\n  cssVars['--persona-launcher-shadow'] =\n    cssVars['--persona-components-launcher-shadow'] ??\n    '0 10px 15px -3px rgba(0, 0, 0, 0.1), 0 4px 6px -4px rgba(0, 0, 0, 0.1)';\n  cssVars['--persona-input-radius'] =\n    cssVars['--persona-components-input-borderRadius'] ??\n    cssVars['--persona-radius-lg'] ??\n    '0.5rem';\n  cssVars['--persona-message-user-radius'] =\n    cssVars['--persona-components-message-user-borderRadius'] ??\n    cssVars['--persona-radius-lg'] ??\n    '0.5rem';\n  cssVars['--persona-message-assistant-radius'] =\n    cssVars['--persona-components-message-assistant-borderRadius'] ??\n    cssVars['--persona-radius-lg'] ??\n    '0.5rem';\n\n  // Component-level color overrides: these map component tokens to\n  // dedicated CSS variables that the widget CSS reads for individual elements.\n  cssVars['--persona-header-bg'] =\n    cssVars['--persona-components-header-background'] ?? cssVars['--persona-surface'];\n  cssVars['--persona-header-border'] =\n    cssVars['--persona-components-header-border'] ?? cssVars['--persona-divider'];\n  cssVars['--persona-header-icon-bg'] =\n    cssVars['--persona-components-header-iconBackground'] ?? cssVars['--persona-primary'];\n  cssVars['--persona-header-icon-fg'] =\n    cssVars['--persona-components-header-iconForeground'] ?? cssVars['--persona-text-inverse'];\n  cssVars['--persona-header-title-fg'] =\n    cssVars['--persona-components-header-titleForeground'] ?? cssVars['--persona-primary'];\n  cssVars['--persona-header-subtitle-fg'] =\n    cssVars['--persona-components-header-subtitleForeground'] ?? cssVars['--persona-text-muted'];\n  cssVars['--persona-header-action-icon-fg'] =\n    cssVars['--persona-components-header-actionIconForeground'] ?? cssVars['--persona-muted'];\n\n  const headerTokens = theme.components?.header;\n  if (headerTokens?.shadow) cssVars['--persona-header-shadow'] = headerTokens.shadow;\n  if (headerTokens?.borderBottom) cssVars['--persona-header-border-bottom'] = headerTokens.borderBottom;\n\n  // Intro card aliases: short names the panel inline-styles read directly.\n  // The full-path `--persona-components-introCard-*` variables auto-emit above;\n  // these mirror them with sensible fallbacks so existing pages keep their look.\n  const introCardTokens = theme.components?.introCard;\n  cssVars['--persona-intro-card-bg'] =\n    cssVars['--persona-components-introCard-background'] ?? cssVars['--persona-surface'];\n  cssVars['--persona-intro-card-radius'] =\n    cssVars['--persona-components-introCard-borderRadius'] ?? '1rem';\n  cssVars['--persona-intro-card-padding'] =\n    cssVars['--persona-components-introCard-padding'] ?? '1.5rem';\n  cssVars['--persona-intro-card-shadow'] =\n    introCardTokens?.shadow\n      ?? cssVars['--persona-components-introCard-shadow']\n      ?? '0 5px 15px rgba(15, 23, 42, 0.08)';\n\n  cssVars['--persona-input-background'] =\n    cssVars['--persona-components-input-background'] ?? cssVars['--persona-surface'];\n  cssVars['--persona-input-placeholder'] =\n    cssVars['--persona-components-input-placeholder'] ?? cssVars['--persona-text-muted'];\n\n  cssVars['--persona-message-user-bg'] =\n    cssVars['--persona-components-message-user-background'] ?? cssVars['--persona-accent'];\n  cssVars['--persona-message-user-text'] =\n    cssVars['--persona-components-message-user-text'] ?? cssVars['--persona-text-inverse'];\n  cssVars['--persona-message-user-shadow'] =\n    cssVars['--persona-components-message-user-shadow'] ?? '0 5px 15px rgba(15, 23, 42, 0.08)';\n  cssVars['--persona-message-assistant-bg'] =\n    cssVars['--persona-components-message-assistant-background'] ?? cssVars['--persona-surface'];\n  cssVars['--persona-message-assistant-text'] =\n    cssVars['--persona-components-message-assistant-text'] ?? cssVars['--persona-text'];\n  cssVars['--persona-message-assistant-border'] =\n    cssVars['--persona-components-message-assistant-border'] ?? cssVars['--persona-border'];\n  cssVars['--persona-message-assistant-shadow'] =\n    cssVars['--persona-components-message-assistant-shadow'] ?? '0 1px 2px 0 rgb(0 0 0 / 0.05)';\n  cssVars['--persona-scroll-to-bottom-bg'] =\n    cssVars['--persona-components-scrollToBottom-background'] ??\n    cssVars['--persona-button-primary-bg'] ??\n    cssVars['--persona-accent'];\n  cssVars['--persona-scroll-to-bottom-fg'] =\n    cssVars['--persona-components-scrollToBottom-foreground'] ??\n    cssVars['--persona-button-primary-fg'] ??\n    cssVars['--persona-text-inverse'];\n  cssVars['--persona-scroll-to-bottom-border'] =\n    cssVars['--persona-components-scrollToBottom-border'] ??\n    cssVars['--persona-primary'];\n  cssVars['--persona-scroll-to-bottom-size'] =\n    cssVars['--persona-components-scrollToBottom-size'] ??\n    '40px';\n  cssVars['--persona-scroll-to-bottom-radius'] =\n    cssVars['--persona-components-scrollToBottom-borderRadius'] ??\n    cssVars['--persona-button-radius'] ??\n    cssVars['--persona-radius-full'] ??\n    '9999px';\n  cssVars['--persona-scroll-to-bottom-shadow'] =\n    cssVars['--persona-components-scrollToBottom-shadow'] ??\n    cssVars['--persona-palette-shadows-sm'] ??\n    '0 1px 2px 0 rgb(0 0 0 / 0.05)';\n  cssVars['--persona-scroll-to-bottom-padding'] =\n    cssVars['--persona-components-scrollToBottom-padding'] ??\n    '0.5rem 0.875rem';\n  cssVars['--persona-scroll-to-bottom-gap'] =\n    cssVars['--persona-components-scrollToBottom-gap'] ??\n    '0.5rem';\n  cssVars['--persona-scroll-to-bottom-font-size'] =\n    cssVars['--persona-components-scrollToBottom-fontSize'] ??\n    cssVars['--persona-palette-typography-fontSize-sm'] ??\n    '0.875rem';\n  cssVars['--persona-scroll-to-bottom-icon-size'] =\n    cssVars['--persona-components-scrollToBottom-iconSize'] ??\n    '14px';\n\n  cssVars['--persona-tool-bubble-shadow'] =\n    cssVars['--persona-components-toolBubble-shadow'] ?? '0 5px 15px rgba(15, 23, 42, 0.08)';\n  cssVars['--persona-reasoning-bubble-shadow'] =\n    cssVars['--persona-components-reasoningBubble-shadow'] ?? '0 5px 15px rgba(15, 23, 42, 0.08)';\n  cssVars['--persona-composer-shadow'] =\n    cssVars['--persona-components-composer-shadow'] ?? 'none';\n\n  cssVars['--persona-md-inline-code-bg'] =\n    cssVars['--persona-components-markdown-inlineCode-background'] ?? cssVars['--persona-container'];\n  cssVars['--persona-md-inline-code-color'] =\n    cssVars['--persona-components-markdown-inlineCode-foreground'] ?? cssVars['--persona-text'];\n\n  cssVars['--persona-md-link-color'] =\n    cssVars['--persona-components-markdown-link-foreground'] ??\n    cssVars['--persona-accent'] ??\n    '#0f0f0f';\n\n  const mdH1Size = cssVars['--persona-components-markdown-heading-h1-fontSize'];\n  if (mdH1Size) cssVars['--persona-md-h1-size'] = mdH1Size;\n  const mdH1Weight = cssVars['--persona-components-markdown-heading-h1-fontWeight'];\n  if (mdH1Weight) cssVars['--persona-md-h1-weight'] = mdH1Weight;\n  const mdH2Size = cssVars['--persona-components-markdown-heading-h2-fontSize'];\n  if (mdH2Size) cssVars['--persona-md-h2-size'] = mdH2Size;\n  const mdH2Weight = cssVars['--persona-components-markdown-heading-h2-fontWeight'];\n  if (mdH2Weight) cssVars['--persona-md-h2-weight'] = mdH2Weight;\n\n  const mdProseFont = cssVars['--persona-components-markdown-prose-fontFamily'];\n  if (mdProseFont && mdProseFont !== 'inherit') {\n    cssVars['--persona-md-prose-font-family'] = mdProseFont;\n  }\n\n  // Markdown code block\n  cssVars['--persona-md-code-block-bg'] =\n    cssVars['--persona-components-markdown-codeBlock-background'] ?? cssVars['--persona-container'];\n  cssVars['--persona-md-code-block-border-color'] =\n    cssVars['--persona-components-markdown-codeBlock-borderColor'] ?? cssVars['--persona-border'];\n  cssVars['--persona-md-code-block-text-color'] =\n    cssVars['--persona-components-markdown-codeBlock-textColor'] ?? 'inherit';\n\n  // Markdown table\n  cssVars['--persona-md-table-header-bg'] =\n    cssVars['--persona-components-markdown-table-headerBackground'] ?? cssVars['--persona-container'];\n  cssVars['--persona-md-table-border-color'] =\n    cssVars['--persona-components-markdown-table-borderColor'] ?? cssVars['--persona-border'];\n\n  // Markdown HR\n  cssVars['--persona-md-hr-color'] =\n    cssVars['--persona-components-markdown-hr-color'] ?? cssVars['--persona-divider'];\n\n  // Markdown blockquote\n  cssVars['--persona-md-blockquote-border-color'] =\n    cssVars['--persona-components-markdown-blockquote-borderColor'] ??\n    cssVars['--persona-palette-colors-gray-900'];\n  cssVars['--persona-md-blockquote-bg'] =\n    cssVars['--persona-components-markdown-blockquote-background'] ?? 'transparent';\n  cssVars['--persona-md-blockquote-text-color'] =\n    cssVars['--persona-components-markdown-blockquote-textColor'] ??\n    cssVars['--persona-palette-colors-gray-500'];\n\n  // Collapsible widget chrome (tool/reasoning/approval bubbles)\n  cssVars['--cw-container'] =\n    cssVars['--persona-components-collapsibleWidget-container'] ?? cssVars['--persona-surface'];\n  cssVars['--cw-surface'] =\n    cssVars['--persona-components-collapsibleWidget-surface'] ?? cssVars['--persona-surface'];\n  cssVars['--cw-border'] =\n    cssVars['--persona-components-collapsibleWidget-border'] ?? cssVars['--persona-border'];\n\n  // Message border\n  cssVars['--persona-message-border'] =\n    cssVars['--persona-components-message-border'] ?? cssVars['--persona-border'];\n\n  // Icon button tokens\n  const components = theme.components;\n  const iconBtn = components?.iconButton;\n  if (iconBtn) {\n    if (iconBtn.background) cssVars['--persona-icon-btn-bg'] = iconBtn.background;\n    if (iconBtn.border) cssVars['--persona-icon-btn-border'] = iconBtn.border;\n    if (iconBtn.color) cssVars['--persona-icon-btn-color'] = iconBtn.color;\n    if (iconBtn.padding) cssVars['--persona-icon-btn-padding'] = iconBtn.padding;\n    if (iconBtn.borderRadius) cssVars['--persona-icon-btn-radius'] = iconBtn.borderRadius;\n    if (iconBtn.hoverBackground) cssVars['--persona-icon-btn-hover-bg'] = iconBtn.hoverBackground;\n    if (iconBtn.hoverColor) cssVars['--persona-icon-btn-hover-color'] = iconBtn.hoverColor;\n    if (iconBtn.activeBackground) cssVars['--persona-icon-btn-active-bg'] = iconBtn.activeBackground;\n    if (iconBtn.activeBorder) cssVars['--persona-icon-btn-active-border'] = iconBtn.activeBorder;\n  }\n\n  // Label button tokens\n  const labelBtn = components?.labelButton;\n  if (labelBtn) {\n    if (labelBtn.background) cssVars['--persona-label-btn-bg'] = labelBtn.background;\n    if (labelBtn.border) cssVars['--persona-label-btn-border'] = labelBtn.border;\n    if (labelBtn.color) cssVars['--persona-label-btn-color'] = labelBtn.color;\n    if (labelBtn.padding) cssVars['--persona-label-btn-padding'] = labelBtn.padding;\n    if (labelBtn.borderRadius) cssVars['--persona-label-btn-radius'] = labelBtn.borderRadius;\n    if (labelBtn.hoverBackground) cssVars['--persona-label-btn-hover-bg'] = labelBtn.hoverBackground;\n    if (labelBtn.fontSize) cssVars['--persona-label-btn-font-size'] = labelBtn.fontSize;\n    if (labelBtn.gap) cssVars['--persona-label-btn-gap'] = labelBtn.gap;\n  }\n\n  // Toggle group tokens\n  const toggleGrp = components?.toggleGroup;\n  if (toggleGrp) {\n    if (toggleGrp.gap) cssVars['--persona-toggle-group-gap'] = toggleGrp.gap;\n    if (toggleGrp.borderRadius) cssVars['--persona-toggle-group-radius'] = toggleGrp.borderRadius;\n  }\n\n  // Artifact tokens\n  const artifact = components?.artifact;\n  if (artifact?.toolbar) {\n    const t = artifact.toolbar;\n    if (t.iconHoverColor) cssVars['--persona-artifact-toolbar-icon-hover-color'] = t.iconHoverColor;\n    if (t.iconHoverBackground) cssVars['--persona-artifact-toolbar-icon-hover-bg'] = t.iconHoverBackground;\n    if (t.iconPadding) cssVars['--persona-artifact-toolbar-icon-padding'] = t.iconPadding;\n    if (t.iconBorderRadius) cssVars['--persona-artifact-toolbar-icon-radius'] = t.iconBorderRadius;\n    if (t.iconBorder) cssVars['--persona-artifact-toolbar-icon-border'] = t.iconBorder;\n    if (t.toggleGroupGap) cssVars['--persona-artifact-toolbar-toggle-group-gap'] = t.toggleGroupGap;\n    if (t.toggleBorderRadius) cssVars['--persona-artifact-toolbar-toggle-radius'] = t.toggleBorderRadius;\n    if (t.copyBackground) cssVars['--persona-artifact-toolbar-copy-bg'] = t.copyBackground;\n    if (t.copyBorder) cssVars['--persona-artifact-toolbar-copy-border'] = t.copyBorder;\n    if (t.copyColor) cssVars['--persona-artifact-toolbar-copy-color'] = t.copyColor;\n    if (t.copyBorderRadius) cssVars['--persona-artifact-toolbar-copy-radius'] = t.copyBorderRadius;\n    if (t.copyPadding) cssVars['--persona-artifact-toolbar-copy-padding'] = t.copyPadding;\n    if (t.copyMenuBackground) {\n      cssVars['--persona-artifact-toolbar-copy-menu-bg'] = t.copyMenuBackground;\n      cssVars['--persona-dropdown-bg'] = cssVars['--persona-dropdown-bg'] ?? t.copyMenuBackground;\n    }\n    if (t.copyMenuBorder) {\n      cssVars['--persona-artifact-toolbar-copy-menu-border'] = t.copyMenuBorder;\n      cssVars['--persona-dropdown-border'] = cssVars['--persona-dropdown-border'] ?? t.copyMenuBorder;\n    }\n    if (t.copyMenuShadow) {\n      cssVars['--persona-artifact-toolbar-copy-menu-shadow'] = t.copyMenuShadow;\n      cssVars['--persona-dropdown-shadow'] = cssVars['--persona-dropdown-shadow'] ?? t.copyMenuShadow;\n    }\n    if (t.copyMenuBorderRadius) {\n      cssVars['--persona-artifact-toolbar-copy-menu-radius'] = t.copyMenuBorderRadius;\n      cssVars['--persona-dropdown-radius'] = cssVars['--persona-dropdown-radius'] ?? t.copyMenuBorderRadius;\n    }\n    if (t.copyMenuItemHoverBackground) {\n      cssVars['--persona-artifact-toolbar-copy-menu-item-hover-bg'] = t.copyMenuItemHoverBackground;\n      cssVars['--persona-dropdown-item-hover-bg'] = cssVars['--persona-dropdown-item-hover-bg'] ?? t.copyMenuItemHoverBackground;\n    }\n    if (t.iconBackground) cssVars['--persona-artifact-toolbar-icon-bg'] = t.iconBackground;\n    if (t.toolbarBorder) cssVars['--persona-artifact-toolbar-border'] = t.toolbarBorder;\n  }\n  if (artifact?.tab) {\n    const t = artifact.tab;\n    if (t.background) cssVars['--persona-artifact-tab-bg'] = t.background;\n    if (t.activeBackground) cssVars['--persona-artifact-tab-active-bg'] = t.activeBackground;\n    if (t.activeBorder) cssVars['--persona-artifact-tab-active-border'] = t.activeBorder;\n    if (t.borderRadius) cssVars['--persona-artifact-tab-radius'] = t.borderRadius;\n    if (t.textColor) cssVars['--persona-artifact-tab-color'] = t.textColor;\n    if (t.hoverBackground) cssVars['--persona-artifact-tab-hover-bg'] = t.hoverBackground;\n    if (t.listBackground) cssVars['--persona-artifact-tab-list-bg'] = t.listBackground;\n    if (t.listBorderColor) cssVars['--persona-artifact-tab-list-border-color'] = t.listBorderColor;\n    if (t.listPadding) cssVars['--persona-artifact-tab-list-padding'] = t.listPadding;\n  }\n  if (artifact?.pane) {\n    const t = artifact.pane;\n    if (t.toolbarBackground) {\n      const toolbarBg =\n        resolveTokenValue(theme, t.toolbarBackground) ?? t.toolbarBackground;\n      cssVars['--persona-artifact-toolbar-bg'] = toolbarBg;\n    }\n  }\n\n  return cssVars;\n}\n\nexport function applyThemeVariables(element: HTMLElement, theme: PersonaTheme): void {\n  const cssVars = themeToCssVariables(theme);\n\n  for (const [name, value] of Object.entries(cssVars)) {\n    element.style.setProperty(name, value);\n  }\n}\n\n/**\n * Stable `data-persona-theme-zone` values applied to key widget regions.\n * Visual editors should use `[data-persona-theme-zone=\"header\"]` selectors\n * rather than internal class names.\n */\nexport const THEME_ZONES = {\n  header: 'Widget header bar',\n  messages: 'Message list area',\n  'user-message': 'User message bubble',\n  'assistant-message': 'Assistant message bubble',\n  composer: 'Footer / composer area',\n  container: 'Main widget container',\n  'artifact-pane': 'Artifact sidebar',\n  'artifact-toolbar': 'Artifact toolbar',\n} as const;\n\nexport type ThemeZone = keyof typeof THEME_ZONES;\n","import type { DeepPartial, PersonaTheme } from '../types/theme';\nimport type { AgentWidgetConfig } from '../types';\nimport { createTheme, resolveTokens, themeToCssVariables } from './tokens';\nimport { deepMerge } from './deep-merge';\n\nexport type ColorScheme = 'light' | 'dark' | 'auto';\n\nexport interface PersonaWidgetConfig {\n  theme?: DeepPartial<PersonaTheme>;\n  darkTheme?: DeepPartial<PersonaTheme>;\n  colorScheme?: ColorScheme;\n}\n\ntype WidgetConfig = PersonaWidgetConfig | AgentWidgetConfig;\n\nconst DARK_PALETTE = {\n  colors: {\n    primary: {\n      50: '#ffffff',\n      100: '#f5f5f5',\n      200: '#d4d4d4',\n      300: '#a3a3a3',\n      400: '#737373',\n      500: '#171717',\n      600: '#0f0f0f',\n      700: '#0a0a0a',\n      800: '#050505',\n      900: '#030303',\n      950: '#000000',\n    },\n    secondary: {\n      50: '#f5f3ff',\n      100: '#ede9fe',\n      200: '#ddd6fe',\n      300: '#c4b5fd',\n      400: '#a78bfa',\n      500: '#8b5cf6',\n      600: '#7c3aed',\n      700: '#6d28d9',\n      800: '#5b21b6',\n      900: '#4c1d95',\n      950: '#2e1065',\n    },\n    accent: {\n      50: '#ecfeff',\n      100: '#cffafe',\n      200: '#a5f3fc',\n      300: '#67e8f9',\n      400: '#22d3ee',\n      500: '#06b6d4',\n      600: '#0891b2',\n      700: '#0e7490',\n      800: '#155e75',\n      900: '#164e63',\n      950: '#083344',\n    },\n    gray: {\n      50: '#f9fafb',\n      100: '#f3f4f6',\n      200: '#e5e7eb',\n      300: '#d1d5db',\n      400: '#9ca3af',\n      500: '#6b7280',\n      600: '#4b5563',\n      700: '#374151',\n      800: '#1f2937',\n      900: '#111827',\n      950: '#030712',\n    },\n    success: {\n      50: '#f0fdf4',\n      100: '#dcfce7',\n      200: '#bbf7d0',\n      300: '#86efac',\n      400: '#4ade80',\n      500: '#22c55e',\n      600: '#16a34a',\n      700: '#15803d',\n      800: '#166534',\n      900: '#14532d',\n    },\n    warning: {\n      50: '#fefce8',\n      100: '#fef9c3',\n      200: '#fef08a',\n      300: '#fde047',\n      400: '#facc15',\n      500: '#eab308',\n      600: '#ca8a04',\n      700: '#a16207',\n      800: '#854d0e',\n      900: '#713f12',\n    },\n    error: {\n      50: '#fef2f2',\n      100: '#fee2e2',\n      200: '#fecaca',\n      300: '#fca5a5',\n      400: '#f87171',\n      500: '#ef4444',\n      600: '#dc2626',\n      700: '#b91c1c',\n      800: '#991b1b',\n      900: '#7f1d1d',\n    },\n  },\n};\n\n/**\n * Normalize theme config for merging; rejects non-objects.\n */\nconst normalizeThemeConfig = (\n  theme: DeepPartial<PersonaTheme> | Record<string, unknown> | undefined\n): DeepPartial<PersonaTheme> | undefined => {\n  if (!theme || typeof theme !== 'object' || Array.isArray(theme)) return undefined;\n  return theme as DeepPartial<PersonaTheme>;\n};\n\nexport const detectColorScheme = (): 'light' | 'dark' => {\n  if (typeof document !== 'undefined' && document.documentElement.classList.contains('dark')) {\n    return 'dark';\n  }\n\n  if (typeof window !== 'undefined' && window.matchMedia?.('(prefers-color-scheme: dark)').matches) {\n    return 'dark';\n  }\n\n  return 'light';\n};\n\nconst getColorSchemeFromConfig = (config?: WidgetConfig): 'light' | 'dark' => {\n  const colorScheme = config?.colorScheme ?? 'light';\n\n  if (colorScheme === 'light') return 'light';\n  if (colorScheme === 'dark') return 'dark';\n\n  return detectColorScheme();\n};\n\nexport const getColorScheme = (config?: WidgetConfig): 'light' | 'dark' => {\n  return getColorSchemeFromConfig(config);\n};\n\nexport const createLightTheme = (userConfig?: DeepPartial<PersonaTheme>): PersonaTheme => {\n  return createTheme(userConfig);\n};\n\nexport const createDarkTheme = (userConfig?: DeepPartial<PersonaTheme>): PersonaTheme => {\n  const baseTheme = createTheme(undefined, { validate: false });\n  \n  return createTheme(\n    {\n      ...userConfig,\n      palette: {\n        ...baseTheme.palette,\n        colors: {\n          ...DARK_PALETTE.colors,\n          ...userConfig?.palette?.colors,\n        },\n      },\n    },\n    { validate: false }\n  );\n};\n\nexport const getActiveTheme = (config?: WidgetConfig): PersonaTheme => {\n  const scheme = getColorScheme(config);\n  const lightThemeConfig = normalizeThemeConfig(config?.theme);\n  const darkThemeConfig = normalizeThemeConfig(config?.darkTheme);\n\n  if (scheme === 'dark') {\n    return createDarkTheme(\n      deepMerge(\n        (lightThemeConfig ?? {}) as Record<string, unknown>,\n        (darkThemeConfig ?? {}) as Record<string, unknown>\n      ) as DeepPartial<PersonaTheme>\n    );\n  }\n\n  return createLightTheme(lightThemeConfig);\n};\n\nexport const getCssVariables = (theme: PersonaTheme): Record<string, string> => {\n  return themeToCssVariables(theme);\n};\n\nexport const applyThemeVariables = (\n  element: HTMLElement,\n  config?: WidgetConfig\n): void => {\n  const theme = getActiveTheme(config);\n  const cssVars = getCssVariables(theme);\n\n  for (const [name, value] of Object.entries(cssVars)) {\n    element.style.setProperty(name, value);\n  }\n};\n\nexport const createThemeObserver = (\n  callback: (scheme: 'light' | 'dark') => void\n): (() => void) => {\n  const cleanupFns: Array<() => void> = [];\n\n  if (typeof document !== 'undefined' && typeof MutationObserver !== 'undefined') {\n    const observer = new MutationObserver(() => {\n      callback(detectColorScheme());\n    });\n\n    observer.observe(document.documentElement, {\n      attributes: true,\n      attributeFilter: ['class'],\n    });\n\n    cleanupFns.push(() => observer.disconnect());\n  }\n\n  if (typeof window !== 'undefined' && window.matchMedia) {\n    const mediaQuery = window.matchMedia('(prefers-color-scheme: dark)');\n    const handleChange = () => callback(detectColorScheme());\n\n    if (mediaQuery.addEventListener) {\n      mediaQuery.addEventListener('change', handleChange);\n      cleanupFns.push(() => mediaQuery.removeEventListener('change', handleChange));\n    } else if (mediaQuery.addListener) {\n      mediaQuery.addListener(handleChange);\n      cleanupFns.push(() => mediaQuery.removeListener(handleChange));\n    }\n  }\n\n  return () => {\n    cleanupFns.forEach((fn) => fn());\n  };\n};\n\nexport { createTheme, resolveTokens, themeToCssVariables };\n","import { Idiomorph } from \"idiomorph\";\n\nexport type MorphOptions = {\n  preserveTypingAnimation?: boolean;\n};\n\n/**\n * Morph a container's contents using idiomorph with chat-widget-specific\n * preservation rules for typing indicators.\n *\n * Action buttons are matched by their `id` attribute (set to `actions-{messageId}`)\n * so idiomorph updates them in place rather than recreating them.\n */\nexport const morphMessages = (\n  container: HTMLElement,\n  newContent: HTMLElement,\n  options: MorphOptions = {}\n): void => {\n  const { preserveTypingAnimation = true } = options;\n\n  Idiomorph.morph(container, newContent.innerHTML, {\n    morphStyle: \"innerHTML\",\n    callbacks: {\n      beforeNodeMorphed(oldNode: Node, newNode: Node): boolean | void {\n        if (!(oldNode instanceof HTMLElement)) return;\n\n        // Preserve typing indicator dots to maintain animation continuity\n        // Also preserve elements with data-preserve-animation attribute for custom loading indicators\n        if (preserveTypingAnimation) {\n          if (oldNode.classList.contains(\"persona-animate-typing\")) {\n            return false;\n          }\n          // Plugins actively mutating a node (e.g. glyph-cycle's tick loop)\n          // opt out of morph entirely via this attribute. Unlike\n          // `data-preserve-animation`, this is honored regardless of whether\n          // the new DOM carries the attribute: it's a runtime-only marker.\n          if (oldNode.hasAttribute(\"data-preserve-runtime\")) {\n            return false;\n          }\n          if (oldNode.hasAttribute(\"data-preserve-animation\")) {\n            // Allow morph when the new node drops the attribute (e.g. tool completed)\n            if (newNode instanceof HTMLElement && !newNode.hasAttribute(\"data-preserve-animation\")) {\n              return;\n            }\n            // Allow morph when content has meaningfully changed (e.g. tool name arrived)\n            if (newNode instanceof HTMLElement && newNode.hasAttribute(\"data-preserve-animation\")) {\n              const oldText = oldNode.textContent ?? \"\";\n              const newText = newNode.textContent ?? \"\";\n              if (oldText !== newText) {\n                return;\n              }\n            }\n            return false;\n          }\n        }\n      },\n    },\n  });\n};\n","/**\n * Normalize text pulled from a native browser selection before it is written to\n * the clipboard.\n *\n * When a user triple-clicks a chat bubble and presses Ctrl/Cmd-C, the browser\n * serializes the DOM selection itself. Markdown is rendered into block-level\n * elements (`<p>`, `<li>`, `<pre>`, …), and browsers emit surrounding newlines\n * for those blocks — so copying a single message drags along stray leading\n * blank lines and a trailing newline that the user never visually selected.\n *\n * This trims the outer whitespace so the clipboard matches the visible\n * selection, while preserving:\n *   - interior newlines (a multi-paragraph or multi-message selection keeps its\n *     line breaks), and\n *   - leading indentation on the first line (e.g. copied code keeps its indent;\n *     only fully-blank leading lines are dropped).\n */\nexport const normalizeCopiedSelectionText = (text: string): string =>\n  text.replace(/^\\n+/, \"\").replace(/\\s+$/, \"\");\n","/**\n * Pure state machine for composer message-history navigation (Up/Down arrows).\n *\n * Mirrors the shell / Slack convention: pressing Up recalls previously sent\n * user messages for re-entry or editing; Down walks back toward the present\n * and restores the in-progress draft once you page past the newest entry.\n *\n * Kept free of DOM access so it can be unit-tested in the Node test env. The\n * UI layer (`ui.ts`) supplies caret information and applies the returned value\n * to the textarea.\n */\n\nexport interface ComposerHistoryState {\n  /** Index into the history list, or -1 when not navigating. */\n  index: number;\n  /** The user's in-progress text, saved when navigation begins. */\n  draft: string;\n}\n\nexport const INITIAL_HISTORY_STATE: ComposerHistoryState = {\n  index: -1,\n  draft: \"\"\n};\n\nexport interface ComposerHistoryInput {\n  direction: \"up\" | \"down\";\n  /** Previously sent user-message texts, oldest first. */\n  history: string[];\n  /** Current textarea value (saved as the draft when navigation begins). */\n  currentValue: string;\n  /** True when the caret sits at the very start of the textarea. */\n  atStart: boolean;\n  state: ComposerHistoryState;\n}\n\nexport interface ComposerHistoryResult {\n  /** Whether the key was consumed (caller should preventDefault). */\n  handled: boolean;\n  /** New textarea value to apply: only present when it should change. */\n  value?: string;\n  /** Next navigation state. */\n  state: ComposerHistoryState;\n}\n\n/**\n * Compute the next navigation state for an Up/Down key press.\n *\n * - **Up** enters history only from the top boundary (`atStart`), then keeps\n *   stepping toward older messages on each subsequent press while navigating.\n * - **Down** only acts while already navigating, stepping toward newer messages\n *   and finally restoring the saved draft once it walks past the newest entry.\n */\nexport function navigateComposerHistory(\n  input: ComposerHistoryInput\n): ComposerHistoryResult {\n  const { direction, history, currentValue, atStart, state } = input;\n  const inHistory = state.index !== -1;\n\n  if (history.length === 0) {\n    return { handled: false, state };\n  }\n\n  if (direction === \"up\") {\n    // Only hijack Up from the top boundary so normal multi-line cursor\n    // movement keeps working until the user is actually cycling history.\n    if (!inHistory && !atStart) {\n      return { handled: false, state };\n    }\n\n    if (!inHistory) {\n      // First step: stash the draft and jump to the newest entry.\n      const index = history.length - 1;\n      return {\n        handled: true,\n        value: history[index],\n        state: { index, draft: currentValue }\n      };\n    }\n\n    if (state.index > 0) {\n      const index = state.index - 1;\n      return {\n        handled: true,\n        value: history[index],\n        state: { index, draft: state.draft }\n      };\n    }\n\n    // Already at the oldest entry: consume the key but don't change.\n    return { handled: true, state };\n  }\n\n  // direction === \"down\": only meaningful while navigating history.\n  if (!inHistory) {\n    return { handled: false, state };\n  }\n\n  if (state.index < history.length - 1) {\n    const index = state.index + 1;\n    return {\n      handled: true,\n      value: history[index],\n      state: { index, draft: state.draft }\n    };\n  }\n\n  // Stepped past the newest entry: restore the saved draft and exit.\n  return {\n    handled: true,\n    value: state.draft,\n    state: { ...INITIAL_HISTORY_STATE }\n  };\n}\n","/**\n * Message-level fingerprint cache for skipping re-renders of unchanged messages.\n *\n * During streaming, every SSE chunk triggers renderMessagesWithPluginsImpl which\n * rebuilds ALL message bubbles. The fingerprint cache lets us skip rebuilding\n * messages whose content hasn't changed, so only the actively streaming message\n * is re-rendered each cycle.\n */\n\nexport type FingerprintableMessage = {\n  id: string;\n  role: string;\n  content: string;\n  streaming?: boolean;\n  voiceProcessing?: boolean;\n  variant?: string;\n  rawContent?: string;\n  llmContent?: string;\n  approval?: { status?: string; [key: string]: unknown };\n  toolCall?: {\n    status?: string;\n    chunks?: string[];\n    args?: unknown;\n    [key: string]: unknown;\n  };\n  reasoning?: { chunks?: string[]; status?: string; [key: string]: unknown };\n  contentParts?: unknown[];\n  stopReason?: string;\n};\n\nexport type MessageCacheEntry = {\n  fingerprint: string;\n  wrapper: HTMLElement;\n};\n\nexport type MessageCache = Map<string, MessageCacheEntry>;\n\n/**\n * Compute a fast fingerprint for a message to detect changes.\n * Uses string concatenation with a delimiter rather than JSON.stringify for performance.\n * The configVersion parameter ensures cache invalidation when widget config changes.\n */\nexport function computeMessageFingerprint(\n  message: FingerprintableMessage,\n  configVersion: number\n): string {\n  return [\n    message.id,\n    message.role,\n    message.content?.length ?? 0,\n    message.content?.slice(-32) ?? \"\",\n    message.streaming ? \"1\" : \"0\",\n    // voiceProcessing flips true→false on transcript finalize, usually with the\n    // SAME text — so without it the cache would reuse the live transcribing/\n    // thinking bubble (waveform/dots) for the finalized message.\n    message.voiceProcessing ? \"1\" : \"0\",\n    message.variant ?? \"\",\n    message.rawContent?.length ?? 0,\n    message.llmContent?.length ?? 0,\n    message.approval?.status ?? \"\",\n    message.toolCall?.status ?? \"\",\n    message.toolCall?.name ?? \"\",\n    message.toolCall?.chunks?.length ?? 0,\n    message.toolCall?.chunks?.[message.toolCall.chunks.length - 1]?.slice(-32) ?? \"\",\n    typeof message.toolCall?.args === \"string\"\n      ? message.toolCall.args.length\n      : message.toolCall?.args\n        ? JSON.stringify(message.toolCall.args).length\n        : 0,\n    message.reasoning?.chunks?.length ?? 0,\n    message.reasoning?.chunks?.[message.reasoning.chunks.length - 1]?.length ?? 0,\n    message.reasoning?.chunks?.[message.reasoning.chunks.length - 1]?.slice(-32) ?? \"\",\n    message.contentParts?.length ?? 0,\n    message.stopReason ?? \"\",\n    configVersion,\n  ].join(\"\\x00\");\n}\n\n/**\n * Create a new message cache instance.\n */\nexport function createMessageCache(): MessageCache {\n  return new Map();\n}\n\n/**\n * Look up a cached wrapper for a message. Returns the cached wrapper\n * if the fingerprint matches, or null if the message needs re-rendering.\n */\nexport function getCachedWrapper(\n  cache: MessageCache,\n  messageId: string,\n  fingerprint: string\n): HTMLElement | null {\n  const entry = cache.get(messageId);\n  if (entry && entry.fingerprint === fingerprint) {\n    return entry.wrapper;\n  }\n  return null;\n}\n\n/**\n * Store a rendered wrapper in the cache.\n */\nexport function setCachedWrapper(\n  cache: MessageCache,\n  messageId: string,\n  fingerprint: string,\n  wrapper: HTMLElement\n): void {\n  cache.set(messageId, { fingerprint, wrapper });\n}\n\n/**\n * Remove cache entries for messages that no longer exist.\n * Call after each render pass with the current message IDs.\n */\nexport function pruneCache(\n  cache: MessageCache,\n  activeMessageIds: Set<string>\n): void {\n  for (const key of cache.keys()) {\n    if (!activeMessageIds.has(key)) {\n      cache.delete(key);\n    }\n  }\n}\n","export type FollowStateAction = \"none\" | \"pause\" | \"resume\";\n\nexport type FollowStateController = {\n  isFollowing: () => boolean;\n  pause: () => boolean;\n  resume: () => boolean;\n};\n\nexport type FollowStateScrollInput = {\n  following: boolean;\n  currentScrollTop: number;\n  lastScrollTop: number;\n  nearBottom: boolean;\n  userScrollThreshold: number;\n  isAutoScrolling?: boolean;\n  pauseOnUpwardScroll?: boolean;\n  pauseWhenAwayFromBottom?: boolean;\n  resumeRequiresDownwardScroll?: boolean;\n};\n\nexport type FollowStateWheelInput = {\n  following: boolean;\n  deltaY: number;\n  nearBottom?: boolean;\n  resumeWhenNearBottom?: boolean;\n};\n\nexport function createFollowStateController(initiallyFollowing = true): FollowStateController {\n  let following = initiallyFollowing;\n\n  return {\n    isFollowing: () => following,\n    pause: () => {\n      if (!following) return false;\n      following = false;\n      return true;\n    },\n    resume: () => {\n      if (following) return false;\n      following = true;\n      return true;\n    }\n  };\n}\n\nexport function getScrollBottomOffset(element: Pick<HTMLElement, \"scrollHeight\" | \"clientHeight\">): number {\n  return Math.max(0, element.scrollHeight - element.clientHeight);\n}\n\nexport function isElementNearBottom(\n  element: Pick<HTMLElement, \"scrollTop\" | \"scrollHeight\" | \"clientHeight\">,\n  threshold: number\n): boolean {\n  return getScrollBottomOffset(element) - element.scrollTop <= threshold;\n}\n\nexport function resolveFollowStateFromScroll(\n  input: FollowStateScrollInput\n): { action: FollowStateAction; delta: number; nextLastScrollTop: number } {\n  const {\n    following,\n    currentScrollTop,\n    lastScrollTop,\n    nearBottom,\n    userScrollThreshold,\n    isAutoScrolling = false,\n    pauseOnUpwardScroll = false,\n    pauseWhenAwayFromBottom = true,\n    resumeRequiresDownwardScroll = false\n  } = input;\n\n  const delta = currentScrollTop - lastScrollTop;\n\n  if (isAutoScrolling || Math.abs(delta) < userScrollThreshold) {\n    return { action: \"none\", delta, nextLastScrollTop: currentScrollTop };\n  }\n\n  if (!following && nearBottom && (!resumeRequiresDownwardScroll || delta > 0)) {\n    return { action: \"resume\", delta, nextLastScrollTop: currentScrollTop };\n  }\n\n  if (following && pauseOnUpwardScroll && delta < 0) {\n    return { action: \"pause\", delta, nextLastScrollTop: currentScrollTop };\n  }\n\n  if (following && pauseWhenAwayFromBottom && !nearBottom) {\n    return { action: \"pause\", delta, nextLastScrollTop: currentScrollTop };\n  }\n\n  return { action: \"none\", delta, nextLastScrollTop: currentScrollTop };\n}\n\nexport function resolveFollowStateFromWheel(\n  input: FollowStateWheelInput\n): FollowStateAction {\n  const {\n    following,\n    deltaY,\n    nearBottom = false,\n    resumeWhenNearBottom = false\n  } = input;\n\n  if (following && deltaY < 0) {\n    return \"pause\";\n  }\n\n  if (!following && resumeWhenNearBottom && deltaY > 0 && nearBottom) {\n    return \"resume\";\n  }\n\n  return \"none\";\n}\n\nexport type SelectionLike = Pick<Selection, \"isCollapsed\"> & {\n  anchorNode: Node | null;\n  focusNode: Node | null;\n};\n\n/**\n * True when the user has a non-collapsed text selection that touches the\n * given container. Used to pause auto-follow while the user is selecting\n * transcript text, so the streaming scroll doesn't drag content out from\n * under an in-progress drag-selection.\n */\nexport function hasSelectionWithin(\n  selection: SelectionLike | null,\n  container: Pick<Node, \"contains\">\n): boolean {\n  if (!selection || selection.isCollapsed) return false;\n  return (\n    container.contains(selection.anchorNode) ||\n    container.contains(selection.focusNode)\n  );\n}\n\nexport type AnchorScrollInput = {\n  /** Top of the anchored user message, relative to the scroll content. */\n  anchorOffsetTop: number;\n  /** Desired gap kept between the anchored message and the viewport top. */\n  topOffset: number;\n  /** clientHeight of the scroll container. */\n  viewportHeight: number;\n  /** scrollHeight of the scroll container, excluding any anchor spacer. */\n  contentHeight: number;\n};\n\n/**\n * Geometry for anchor-top (\"pin the sent user message near the viewport\n * top\") scrolling. The spacer height is the extra scrollable room needed so\n * the target scroll position is reachable before the streamed response has\n * grown tall enough to provide it naturally.\n */\nexport function computeAnchorScrollState(input: AnchorScrollInput): {\n  targetScrollTop: number;\n  spacerHeight: number;\n} {\n  const targetScrollTop = Math.max(0, input.anchorOffsetTop - input.topOffset);\n  const spacerHeight = Math.max(\n    0,\n    targetScrollTop + input.viewportHeight - input.contentHeight\n  );\n  return { targetScrollTop, spacerHeight };\n}\n\n/**\n * Shrink-only spacer update: as streamed content grows beneath the anchored\n * message, the spacer gives back exactly that much room so total scroll\n * height stays constant and nothing jumps. Never grows after anchoring.\n */\nexport function computeShrunkSpacerHeight(input: {\n  initialSpacerHeight: number;\n  contentHeightAtAnchor: number;\n  currentContentHeight: number;\n}): number {\n  const growth = Math.max(\n    0,\n    input.currentContentHeight - input.contentHeightAtAnchor\n  );\n  return Math.max(0, input.initialSpacerHeight - growth);\n}\n","import { AgentWidgetSessionStatus } from \"../session\";\n\nexport const statusCopy: Record<AgentWidgetSessionStatus, string> = {\n  idle: \"Online\",\n  connecting: \"Connecting…\",\n  connected: \"Streaming…\",\n  error: \"Offline\"\n};\n\n/**\n * Default z-index for widget overlays. Used for the floating panel, launcher\n * button, sidebar, mobile fullscreen, and docked mobile fullscreen modes.\n * Integrators can override via `launcher.zIndex`.\n */\nexport const DEFAULT_OVERLAY_Z_INDEX = 100000;\n\n/**\n * Z-index for elements portaled to document.body (tooltips, dropdowns).\n * Must be above the widget overlay so portaled UI is not clipped.\n */\nexport const PORTALED_OVERLAY_Z_INDEX = DEFAULT_OVERLAY_Z_INDEX + 1;\n\n\n\n\n\n\n\n\n","import type {\n  AgentWidgetMessage,\n  AgentWidgetStreamAnimationBuffer,\n  AgentWidgetStreamAnimationBuiltinType,\n  AgentWidgetStreamAnimationFeature,\n  AgentWidgetStreamAnimationPlaceholder,\n  AgentWidgetStreamAnimationType,\n  StreamAnimationPlugin,\n} from \"../types\";\n\nexport type ResolvedStreamAnimation = {\n  type: AgentWidgetStreamAnimationType;\n  placeholder: AgentWidgetStreamAnimationPlaceholder;\n  speed: number;\n  duration: number;\n  buffer: AgentWidgetStreamAnimationBuffer;\n};\n\nconst DEFAULT_STREAM_ANIMATION: ResolvedStreamAnimation = {\n  type: \"none\",\n  placeholder: \"none\",\n  speed: 120,\n  duration: 1800,\n  buffer: \"none\",\n};\n\n/** Default tags whose text descendants are not wrapped. Plugins can override. */\nconst DEFAULT_SKIP_TAGS = [\"pre\", \"code\", \"a\", \"script\", \"style\"];\n\nexport const resolveStreamAnimation = (\n  feature: AgentWidgetStreamAnimationFeature | undefined\n): ResolvedStreamAnimation => ({\n  type: feature?.type ?? DEFAULT_STREAM_ANIMATION.type,\n  placeholder: feature?.placeholder ?? DEFAULT_STREAM_ANIMATION.placeholder,\n  speed: feature?.speed ?? DEFAULT_STREAM_ANIMATION.speed,\n  duration: feature?.duration ?? DEFAULT_STREAM_ANIMATION.duration,\n  buffer: feature?.buffer ?? DEFAULT_STREAM_ANIMATION.buffer,\n});\n\n/* ============================================================\n   Plugin registry\n   ============================================================ */\n\n/**\n * Built-in animations ship with the core widget: CSS lives in widget.css\n * and no subpath import is required. They register automatically.\n *\n * Other animations (`letter-rise`, `word-fade`, `wipe`, `glyph-cycle`) are\n * tree-shakeable subpath plugins: consumers import them from\n * `@runtypelabs/persona/animations/<name>` and they auto-register on load.\n */\nconst BUILTIN_PLUGINS: StreamAnimationPlugin[] = [\n  {\n    name: \"typewriter\",\n    containerClass: \"persona-stream-typewriter\",\n    wrap: \"char\",\n    useCaret: true,\n  },\n  {\n    name: \"pop-bubble\",\n    bubbleClass: \"persona-stream-pop\",\n    wrap: \"none\",\n  },\n  {\n    name: \"letter-rise\",\n    containerClass: \"persona-stream-letter-rise\",\n    wrap: \"char\",\n  },\n  {\n    name: \"word-fade\",\n    containerClass: \"persona-stream-word-fade\",\n    wrap: \"word\",\n  },\n];\n\n/**\n * Global registry populated by:\n * - the core built-ins below (always available)\n * - `registerStreamAnimationPlugin()` calls from subpath animation modules\n *   (invoked automatically when consumers `import` them)\n * - IIFE bundle's bootstrap code (pre-registers all built-ins for script-tag\n *   consumers)\n */\nconst globalRegistry = new Map<string, StreamAnimationPlugin>();\nfor (const plugin of BUILTIN_PLUGINS) globalRegistry.set(plugin.name, plugin);\n\n/**\n * Register a custom stream animation plugin globally. Subsequent widget\n * instances can reference the plugin by `name` in `features.streamAnimation.type`.\n * Per-widget plugin overrides via `features.streamAnimation.plugins` take\n * precedence over the global registry.\n */\nexport const registerStreamAnimationPlugin = (plugin: StreamAnimationPlugin): void => {\n  globalRegistry.set(plugin.name, plugin);\n};\n\nexport const unregisterStreamAnimationPlugin = (name: string): void => {\n  // Built-ins are preserved; only external plugins can be unregistered.\n  if (BUILTIN_PLUGINS.some((p) => p.name === name)) return;\n  globalRegistry.delete(name);\n};\n\nexport const listRegisteredStreamAnimations = (): string[] =>\n  Array.from(globalRegistry.keys());\n\n/**\n * Resolve the plugin for a given type. Per-instance overrides take precedence\n * over the global registry. Returns null for `\"none\"` or unknown types.\n */\nexport const resolveStreamAnimationPlugin = (\n  type: AgentWidgetStreamAnimationType,\n  overrides?: Record<string, StreamAnimationPlugin>\n): StreamAnimationPlugin | null => {\n  if (type === \"none\") return null;\n  if (overrides && Object.prototype.hasOwnProperty.call(overrides, type)) {\n    return overrides[type] ?? null;\n  }\n  return globalRegistry.get(type) ?? null;\n};\n\n/* ============================================================\n   Buffering\n   ============================================================ */\n\n/**\n * Apply content buffering to hide in-progress words or lines during streaming.\n * Custom strategies via `plugin.bufferContent` take precedence over `buffer`.\n */\nexport const applyStreamBuffer = (\n  content: string,\n  buffer: AgentWidgetStreamAnimationBuffer,\n  plugin: StreamAnimationPlugin | null,\n  message: AgentWidgetMessage,\n  streaming: boolean\n): string => {\n  if (!streaming) return content;\n  if (plugin?.bufferContent) return plugin.bufferContent(content, message);\n  if (!content) return content;\n  if (buffer === \"word\") {\n    const lastSpace = content.search(/\\s(?=\\S*$)/);\n    if (lastSpace < 0) return \"\";\n    return content.slice(0, lastSpace);\n  }\n  if (buffer === \"line\") {\n    const lastNewline = content.lastIndexOf(\"\\n\");\n    if (lastNewline < 0) return \"\";\n    return content.slice(0, lastNewline);\n  }\n  return content;\n};\n\n/* ============================================================\n   Wrapping\n   ============================================================ */\n\nconst makeCharSpan = (\n  doc: Document,\n  ch: string,\n  messageId: string,\n  index: number\n): HTMLElement => {\n  const span = doc.createElement(\"span\");\n  span.className = \"persona-stream-char\";\n  span.id = `stream-c-${messageId}-${index}`;\n  span.style.setProperty(\"--char-index\", String(index));\n  span.textContent = ch;\n  return span;\n};\n\nconst makeWordSpan = (\n  doc: Document,\n  word: string,\n  messageId: string,\n  index: number\n): HTMLElement => {\n  const span = doc.createElement(\"span\");\n  span.className = \"persona-stream-word\";\n  span.id = `stream-w-${messageId}-${index}`;\n  span.style.setProperty(\"--word-index\", String(index));\n  span.textContent = word;\n  return span;\n};\n\nconst WHITESPACE_RE = /\\s/;\n\nconst shouldSkipSubtree = (node: Node, skipTags: Set<string>): boolean => {\n  let current: Node | null = node.parentNode;\n  while (current) {\n    if (current.nodeType === 1) {\n      const el = current as Element;\n      if (skipTags.has(el.tagName.toLowerCase())) return true;\n    }\n    current = current.parentNode;\n  }\n  return false;\n};\n\nconst wrapTextNodeChars = (\n  textNode: Text,\n  messageId: string,\n  counterRef: { value: number }\n): void => {\n  const doc = textNode.ownerDocument;\n  const parent = textNode.parentNode;\n  if (!doc || !parent) return;\n  const text = textNode.nodeValue ?? \"\";\n  if (!text) return;\n  const fragment = doc.createDocumentFragment();\n  let i = 0;\n  while (i < text.length) {\n    if (WHITESPACE_RE.test(text[i])) {\n      // Keep whitespace as a plain text node so the browser preserves natural\n      // word-break opportunities between words. `display: inline-block` spans\n      // swallow single-space content, so wrapping whitespace would collapse\n      // the spaces and break line wrapping.\n      let j = i;\n      while (j < text.length && WHITESPACE_RE.test(text[j])) j += 1;\n      fragment.appendChild(doc.createTextNode(text.slice(i, j)));\n      i = j;\n    } else {\n      // Wrap each run of non-whitespace chars in a `white-space: nowrap`\n      // group so the browser doesn't break lines between individual char\n      // spans mid-word. Word boundaries (whitespace) stay as plain text\n      // nodes between groups, preserving natural line-break opportunities.\n      const group = doc.createElement(\"span\");\n      group.className = \"persona-stream-word-group\";\n      let j = i;\n      while (j < text.length && !WHITESPACE_RE.test(text[j])) {\n        group.appendChild(makeCharSpan(doc, text[j], messageId, counterRef.value));\n        counterRef.value += 1;\n        j += 1;\n      }\n      fragment.appendChild(group);\n      i = j;\n    }\n  }\n  parent.replaceChild(fragment, textNode);\n};\n\nconst wrapTextNodeWords = (\n  textNode: Text,\n  messageId: string,\n  counterRef: { value: number }\n): void => {\n  const doc = textNode.ownerDocument;\n  const parent = textNode.parentNode;\n  if (!doc || !parent) return;\n  const text = textNode.nodeValue ?? \"\";\n  if (!text) return;\n  const fragment = doc.createDocumentFragment();\n  const tokens = text.split(/(\\s+)/);\n  for (const token of tokens) {\n    if (!token) continue;\n    if (/^\\s+$/.test(token)) {\n      fragment.appendChild(doc.createTextNode(token));\n    } else {\n      fragment.appendChild(makeWordSpan(doc, token, messageId, counterRef.value));\n      counterRef.value += 1;\n    }\n  }\n  parent.replaceChild(fragment, textNode);\n};\n\n/**\n * Wrap plain-text nodes in the sanitized markdown HTML with per-char or per-word\n * spans suitable for staggered CSS animations. Skips descendants of `<pre>`,\n * `<code>`, and `<a>` so code blocks stay legible and link click targets stay intact.\n *\n * Each wrapped span carries a stable `id` (`stream-c-{messageId}-{N}` or\n * `stream-w-{messageId}-{N}`) so idiomorph preserves existing spans across\n * token-by-token re-renders: animations on already-streamed characters never\n * restart.\n */\nexport const wrapStreamAnimation = (\n  html: string,\n  mode: \"char\" | \"word\",\n  messageId: string,\n  options?: { skipTags?: string[]; startIndex?: number }\n): string => {\n  if (!html) return html;\n  if (typeof document === \"undefined\") return html;\n\n  const scratch = document.createElement(\"div\");\n  scratch.innerHTML = html;\n\n  const skipTags = new Set((options?.skipTags ?? DEFAULT_SKIP_TAGS).map((t) => t.toLowerCase()));\n  const walker = document.createTreeWalker(scratch, NodeFilter.SHOW_TEXT, null);\n  const textNodes: Text[] = [];\n  let node = walker.nextNode();\n  while (node) {\n    if (!shouldSkipSubtree(node, skipTags)) {\n      textNodes.push(node as Text);\n    }\n    node = walker.nextNode();\n  }\n\n  // `startIndex` lets callers number spans by their absolute position in a\n  // larger string, even when only a slice is being wrapped. The peek banner\n  // uses this so per-char span IDs stay stable as the trailing-100-char\n  // window shifts each chunk: idiomorph then preserves animations on\n  // already-revealed chars instead of restarting them.\n  const counterRef = { value: options?.startIndex ?? 0 };\n  const wrap = mode === \"char\" ? wrapTextNodeChars : wrapTextNodeWords;\n  for (const textNode of textNodes) {\n    wrap(textNode, messageId, counterRef);\n  }\n\n  return scratch.innerHTML;\n};\n\n/* ============================================================\n   Supporting helpers\n   ============================================================ */\n\n/**\n * Build the caret element for `typewriter` mode. Carries\n * `data-preserve-animation` so idiomorph keeps the blink running across\n * token re-renders.\n */\nexport const createStreamCaret = (doc: Document = document): HTMLElement => {\n  const caret = doc.createElement(\"span\");\n  caret.className = \"persona-stream-caret\";\n  caret.setAttribute(\"aria-hidden\", \"true\");\n  caret.setAttribute(\"data-preserve-animation\", \"stream-caret\");\n  return caret;\n};\n\n/**\n * Shimmer placeholder shown before the first token arrives, and, when the\n * `\"line\"` buffer strategy is active, reshown between lines. A single\n * full-width bar; we don't know ahead of time how wide the next line will be,\n * so committing to one width avoids implying structure the stream won't match.\n */\nexport const createSkeletonPlaceholder = (doc: Document = document): HTMLElement => {\n  const wrapper = doc.createElement(\"div\");\n  wrapper.className = \"persona-stream-skeleton\";\n  wrapper.setAttribute(\"data-preserve-animation\", \"stream-skeleton\");\n  wrapper.setAttribute(\"aria-hidden\", \"true\");\n  const line = doc.createElement(\"div\");\n  line.className = \"persona-stream-skeleton-line\";\n  wrapper.appendChild(line);\n  return wrapper;\n};\n\n/* ============================================================\n   Plugin style + attach lifecycle\n   ============================================================ */\n\n/**\n * Track which plugins have injected their CSS into a given root already.\n * Prevents duplicate <style> tags across widget instances or re-renders.\n */\nconst injectedStyleRoots = new WeakMap<HTMLElement | ShadowRoot, Set<string>>();\n\nexport const injectPluginStyles = (\n  plugin: StreamAnimationPlugin,\n  root: HTMLElement | ShadowRoot\n): void => {\n  if (!plugin.styles) return;\n  let names = injectedStyleRoots.get(root);\n  if (!names) {\n    names = new Set();\n    injectedStyleRoots.set(root, names);\n  }\n  if (names.has(plugin.name)) {\n    // The tracking Set says we injected this plugin's styles, but the actual\n    // <style> node may have been removed (e.g. host cleared via `innerHTML = \"\"`\n    // during widget re-init). Fall through and re-inject if the tag is gone.\n    const escaped = plugin.name.replace(/[\"\\\\]/g, \"\\\\$&\");\n    const existing = root.querySelector(\n      `style[data-persona-animation=\"${escaped}\"]`\n    );\n    if (existing) return;\n    names.delete(plugin.name);\n  }\n  names.add(plugin.name);\n  const doc = root instanceof ShadowRoot ? root.ownerDocument : root.ownerDocument ?? document;\n  const style = doc.createElement(\"style\");\n  style.setAttribute(\"data-persona-animation\", plugin.name);\n  style.textContent = plugin.styles;\n  root.appendChild(style);\n};\n\n/**\n * Attach detach-tracking state for plugins registered to a widget root.\n */\nconst attachedCleanups = new WeakMap<\n  HTMLElement | ShadowRoot,\n  Map<string, (() => void) | void>\n>();\n\nexport const attachPlugin = (\n  plugin: StreamAnimationPlugin,\n  root: HTMLElement | ShadowRoot\n): void => {\n  if (!plugin.onAttach) return;\n  let cleanups = attachedCleanups.get(root);\n  if (!cleanups) {\n    cleanups = new Map();\n    attachedCleanups.set(root, cleanups);\n  }\n  if (cleanups.has(plugin.name)) return;\n  const cleanup = plugin.onAttach(root);\n  cleanups.set(plugin.name, cleanup);\n};\n\nexport const detachAllPlugins = (root: HTMLElement | ShadowRoot): void => {\n  const cleanups = attachedCleanups.get(root);\n  if (!cleanups) return;\n  for (const cleanup of cleanups.values()) {\n    if (typeof cleanup === \"function\") cleanup();\n  }\n  cleanups.clear();\n};\n\n/**\n * Ensure the plugin's one-time side effects (style injection, onAttach) have\n * run for this widget root. Idempotent: safe to call on every render.\n */\nexport const ensurePluginActive = (\n  plugin: StreamAnimationPlugin,\n  root: HTMLElement | ShadowRoot\n): void => {\n  injectPluginStyles(plugin, root);\n  attachPlugin(plugin, root);\n};\n\n/* ============================================================\n   Back-compat helpers (used by existing tests)\n   ============================================================ */\n\nexport const isPerCharAnimation = (type: AgentWidgetStreamAnimationType): boolean => {\n  const plugin = resolveStreamAnimationPlugin(type);\n  return plugin?.wrap === \"char\";\n};\n\nexport const isPerWordAnimation = (type: AgentWidgetStreamAnimationType): boolean => {\n  const plugin = resolveStreamAnimationPlugin(type);\n  return plugin?.wrap === \"word\";\n};\n\nexport const isWrappingAnimation = (type: AgentWidgetStreamAnimationType): boolean =>\n  isPerCharAnimation(type) || isPerWordAnimation(type);\n\nexport const streamAnimationContainerClass = (\n  type: AgentWidgetStreamAnimationType\n): string | null => resolveStreamAnimationPlugin(type)?.containerClass ?? null;\n\nexport const streamAnimationBubbleClass = (\n  type: AgentWidgetStreamAnimationType\n): string | null => resolveStreamAnimationPlugin(type)?.bubbleClass ?? null;\n\n// Re-export the builtin type literal so tests and consumers can reference it.\nexport type { AgentWidgetStreamAnimationBuiltinType };\n","import { DEFAULT_OVERLAY_Z_INDEX } from \"./constants\";\n\n/**\n * Elevates the light-DOM host element's stacking context so viewport-covering\n * overlays (sidebar, fullscreen) can escape parent stacking traps.\n *\n * - If the host has `position: static`, sets it to `relative` (required for\n *   `z-index` to take effect).\n * - Applies `z-index` matching the overlay default.\n * - Applies `isolation: isolate` to create a predictable stacking context.\n *\n * @returns A teardown function that restores only the properties that were changed.\n */\nexport function syncOverlayHostStacking(\n  host: HTMLElement,\n  zIndex: number = DEFAULT_OVERLAY_Z_INDEX\n): () => void {\n  const originalPosition = host.style.position;\n  const originalZIndex = host.style.zIndex;\n  const originalIsolation = host.style.isolation;\n\n  const computed = getComputedStyle(host);\n  const positionWasSet = computed.position === \"static\" || computed.position === \"\";\n  if (positionWasSet) {\n    host.style.position = \"relative\";\n  }\n\n  host.style.zIndex = String(zIndex);\n  host.style.isolation = \"isolate\";\n\n  return () => {\n    if (positionWasSet) {\n      host.style.position = originalPosition;\n    }\n    host.style.zIndex = originalZIndex;\n    host.style.isolation = originalIsolation;\n  };\n}\n","interface ScrollLockState {\n  originalOverflow: string;\n  originalPosition: string;\n  originalTop: string;\n  originalWidth: string;\n  scrollY: number;\n}\n\nlet lockCount = 0;\nlet savedState: ScrollLockState | null = null;\n\n/**\n * Acquire a document-level scroll lock. The page body becomes non-scrollable\n * via `overflow: hidden` with an iOS-safe `position: fixed` pattern that\n * preserves the visual scroll position.\n *\n * Ref-counted: multiple callers can acquire; the lock is only released when\n * all callers have released. Each release call is idempotent.\n *\n * @returns A release function. Call it exactly once per acquisition.\n */\nexport function acquireScrollLock(doc: Document = document): () => void {\n  lockCount++;\n\n  if (lockCount === 1) {\n    const body = doc.body;\n    const win = doc.defaultView ?? window;\n    const scrollY = win.scrollY || doc.documentElement.scrollTop;\n\n    savedState = {\n      originalOverflow: body.style.overflow,\n      originalPosition: body.style.position,\n      originalTop: body.style.top,\n      originalWidth: body.style.width,\n      scrollY,\n    };\n\n    body.style.overflow = \"hidden\";\n    body.style.position = \"fixed\";\n    body.style.top = `-${scrollY}px`;\n    body.style.width = \"100%\";\n  }\n\n  let released = false;\n\n  return () => {\n    if (released) return;\n    released = true;\n    lockCount = Math.max(0, lockCount - 1);\n\n    if (lockCount === 0 && savedState) {\n      const body = doc.body;\n      const win = doc.defaultView ?? window;\n      body.style.overflow = savedState.originalOverflow;\n      body.style.position = savedState.originalPosition;\n      body.style.top = savedState.originalTop;\n      body.style.width = savedState.originalWidth;\n      win.scrollTo(0, savedState.scrollY);\n      savedState = null;\n    }\n  };\n}\n","import type { AgentWidgetConfig, AgentWidgetDockConfig } from \"../types\";\n\nconst DEFAULT_DOCK_CONFIG: Required<AgentWidgetDockConfig> = {\n  side: \"right\",\n  width: \"420px\",\n  animate: true,\n  reveal: \"resize\",\n  maxHeight: \"100dvh\",\n};\n\nexport const isDockedMountMode = (config?: AgentWidgetConfig): boolean =>\n  (config?.launcher?.mountMode ?? \"floating\") === \"docked\";\n\nexport const isComposerBarMountMode = (config?: AgentWidgetConfig): boolean =>\n  (config?.launcher?.mountMode ?? \"floating\") === \"composer-bar\";\n\n/**\n * Resolved dock layout. For `reveal: \"resize\"`, when the panel is closed the dock column is `0px`.\n * For `reveal: \"overlay\"`, the panel overlays with `transform`. For `reveal: \"push\"`, a sliding track\n * moves content and panel together without width animation on the main column. For `emerge`,\n * the dock column still animates like `resize` but the widget stays `dock.width` wide inside the slot.\n * Unknown keys on `launcher.dock` (e.g. legacy `collapsedWidth`) are ignored.\n */\nexport const resolveDockConfig = (\n  config?: AgentWidgetConfig\n): Required<AgentWidgetDockConfig> => {\n  const dock = config?.launcher?.dock;\n  return {\n    side: dock?.side ?? DEFAULT_DOCK_CONFIG.side,\n    width: dock?.width ?? DEFAULT_DOCK_CONFIG.width,\n    animate: dock?.animate ?? DEFAULT_DOCK_CONFIG.animate,\n    reveal: dock?.reveal ?? DEFAULT_DOCK_CONFIG.reveal,\n    maxHeight: dock?.maxHeight ?? DEFAULT_DOCK_CONFIG.maxHeight,\n  };\n};\n","export const positionMap: Record<\n  \"bottom-right\" | \"bottom-left\" | \"top-right\" | \"top-left\",\n  string\n> = {\n  \"bottom-right\": \"persona-bottom-6 persona-right-6\",\n  \"bottom-left\": \"persona-bottom-6 persona-left-6\",\n  \"top-right\": \"persona-top-6 persona-right-6\",\n  \"top-left\": \"persona-top-6 persona-left-6\"\n};\n\n\n\n\n\n\n\n\n","import { createElement, createElementInDocument, createNode, cx } from \"../utils/dom\";\nimport { renderLucideIcon } from \"../utils/icons\";\nimport { AgentWidgetConfig } from \"../types\";\nimport { PORTALED_OVERLAY_Z_INDEX } from \"../utils/constants\";\nimport { HEADER_THEME_CSS } from \"./header-builder\";\n\nexport interface CloseButtonParts {\n  button: HTMLButtonElement;\n  wrapper: HTMLElement;\n}\n\nexport interface ClearChatButtonParts {\n  button: HTMLButtonElement;\n  wrapper: HTMLElement;\n}\n\nexport interface CreateCloseButtonOptions {\n  showClose?: boolean;\n  /**\n   * Override the wrapper className. The full header passes its own\n   * placement-aware class string; composer-bar mode passes a class that\n   * positions the wrapper absolutely in the top-right of the panel chrome.\n   */\n  wrapperClassName?: string;\n  /**\n   * Explicit button-size override that wins over `launcher.closeButtonSize`.\n   * Use when the call site has its own opinion about the size that should\n   * take precedence over the global launcher config: e.g. composer-bar's\n   * minimal close icon, where size is part of the mode's UX, not something\n   * that should inherit from the floating launcher's button size.\n   */\n  buttonSize?: string;\n  /**\n   * Override the rendered icon size (default: \"28px\"). Pair with\n   * `buttonSize` when scaling the whole control down: otherwise the\n   * 28px icon will overflow a smaller button.\n   */\n  iconSize?: string;\n}\n\nexport interface CreateClearChatButtonOptions {\n  /**\n   * Override the wrapper className. Header builder passes its own\n   * placement-aware class string; composer-bar mode passes a class that\n   * positions the wrapper absolutely (next to the close button).\n   */\n  wrapperClassName?: string;\n  /**\n   * Explicit button-size override that wins over `launcher.clearChat.size`.\n   * Composer-bar mode uses this so the clear icon visually matches the\n   * shrunken close button (16px) and doesn't render at the floating\n   * launcher's 32px default.\n   */\n  buttonSize?: string;\n  /** Override the rendered icon size (default: \"20px\"). */\n  iconSize?: string;\n}\n\nconst DEFAULT_WRAPPER_CLASS =\n  \"persona-relative persona-ml-auto persona-inline-flex persona-items-center persona-justify-center\";\n\n/**\n * Build the close (×) button + tooltip used in the panel header. Lifted\n * verbatim from header-builder.ts so composer-bar mode can render just a\n * close button (no full header strip) without duplicating the tooltip\n * + config-driven styling logic.\n */\nexport const createCloseButton = (\n  config: AgentWidgetConfig | undefined,\n  options: CreateCloseButtonOptions = {},\n): CloseButtonParts => {\n  const {\n    showClose = true,\n    wrapperClassName = DEFAULT_WRAPPER_CLASS,\n    buttonSize,\n    iconSize = \"28px\",\n  } = options;\n  const launcher = config?.launcher ?? {};\n  // Call-site `buttonSize` (if provided) wins over launcher config. The\n  // launcher's `closeButtonSize` is set in DEFAULT_WIDGET_CONFIG so it's\n  // never undefined, which means the call-site override is the only way\n  // to opt a specific render path (like composer-bar's minimal close) into\n  // a different size.\n  const closeButtonSize = buttonSize ?? launcher.closeButtonSize ?? \"32px\";\n\n  const wrapper = createElement(\"div\", wrapperClassName);\n\n  const closeButtonTooltipText = launcher.closeButtonTooltipText ?? \"Close chat\";\n  const closeButtonShowTooltip = launcher.closeButtonShowTooltip ?? true;\n  const closeButtonIconName = launcher.closeButtonIconName ?? \"x\";\n  const closeButtonIconText = launcher.closeButtonIconText ?? \"×\";\n  // hover-bg / border-none / rounded-full are default utility classes that\n  // apply only when the matching style override is absent; an override sets\n  // the inline style instead (cx omits the class).\n  const hasCloseBorder = Boolean(\n    launcher.closeButtonBorderWidth || launcher.closeButtonBorderColor\n  );\n\n  const button = createNode(\"button\", {\n    className: cx(\n      \"persona-inline-flex persona-items-center persona-justify-center persona-cursor-pointer\",\n      !launcher.closeButtonBackgroundColor && \"hover:persona-bg-gray-100\",\n      !hasCloseBorder && \"persona-border-none\",\n      !launcher.closeButtonBorderRadius && \"persona-rounded-full\"\n    ),\n    attrs: { type: \"button\", \"aria-label\": closeButtonTooltipText },\n    style: {\n      height: closeButtonSize,\n      width: closeButtonSize,\n      display: showClose ? undefined : \"none\",\n      color: launcher.closeButtonColor || HEADER_THEME_CSS.actionIconColor,\n      backgroundColor: launcher.closeButtonBackgroundColor || undefined,\n      border: hasCloseBorder\n        ? `${launcher.closeButtonBorderWidth || \"0px\"} solid ${launcher.closeButtonBorderColor || \"transparent\"}`\n        : undefined,\n      borderRadius: launcher.closeButtonBorderRadius || undefined,\n      paddingLeft: launcher.closeButtonPaddingX || undefined,\n      paddingRight: launcher.closeButtonPaddingX || undefined,\n      paddingTop: launcher.closeButtonPaddingY || undefined,\n      paddingBottom: launcher.closeButtonPaddingY || undefined,\n    },\n  });\n\n  // The X glyph's paths occupy only the middle 50% of its 24x24 viewBox\n  // (from 6,6 to 18,18), while other header icons (e.g. refresh-cw) span\n  // ~75% of the viewBox. Rendering X at a larger intrinsic size brings\n  // its visible extent into parity with sibling icons in the header.\n  // display:block eliminates inline-baseline spacing that can push the\n  // icon a fractional pixel off-center inside the button.\n  const closeIconSvg = renderLucideIcon(closeButtonIconName, iconSize, \"currentColor\", 1);\n  if (closeIconSvg) {\n    closeIconSvg.style.display = \"block\";\n    button.appendChild(closeIconSvg);\n  } else {\n    button.textContent = closeButtonIconText;\n  }\n\n  wrapper.appendChild(button);\n\n  if (closeButtonShowTooltip && closeButtonTooltipText) {\n    let portaledTooltip: HTMLElement | null = null;\n\n    const showTooltip = () => {\n      if (portaledTooltip) return;\n\n      const tooltipDocument = button.ownerDocument;\n      const tooltipContainer = tooltipDocument.body;\n      if (!tooltipContainer) return;\n\n      portaledTooltip = createElementInDocument(\n        tooltipDocument,\n        \"div\",\n        \"persona-clear-chat-tooltip\"\n      );\n      portaledTooltip.textContent = closeButtonTooltipText;\n\n      const arrow = createElementInDocument(tooltipDocument, \"div\");\n      arrow.className = \"persona-clear-chat-tooltip-arrow\";\n      portaledTooltip.appendChild(arrow);\n\n      const buttonRect = button.getBoundingClientRect();\n\n      portaledTooltip.style.position = \"fixed\";\n      portaledTooltip.style.zIndex = String(PORTALED_OVERLAY_Z_INDEX);\n      portaledTooltip.style.left = `${buttonRect.left + buttonRect.width / 2}px`;\n      portaledTooltip.style.top = `${buttonRect.top - 8}px`;\n      portaledTooltip.style.transform = \"translate(-50%, -100%)\";\n\n      tooltipContainer.appendChild(portaledTooltip);\n    };\n\n    const hideTooltip = () => {\n      if (portaledTooltip && portaledTooltip.parentNode) {\n        portaledTooltip.parentNode.removeChild(portaledTooltip);\n        portaledTooltip = null;\n      }\n    };\n\n    wrapper.addEventListener(\"mouseenter\", showTooltip);\n    wrapper.addEventListener(\"mouseleave\", hideTooltip);\n    button.addEventListener(\"focus\", showTooltip);\n    button.addEventListener(\"blur\", hideTooltip);\n\n    (wrapper as any)._cleanupTooltip = () => {\n      hideTooltip();\n      wrapper.removeEventListener(\"mouseenter\", showTooltip);\n      wrapper.removeEventListener(\"mouseleave\", hideTooltip);\n      button.removeEventListener(\"focus\", showTooltip);\n      button.removeEventListener(\"blur\", hideTooltip);\n    };\n  }\n\n  return { button, wrapper };\n};\n\nconst DEFAULT_CLEAR_CHAT_WRAPPER_CLASS =\n  \"persona-relative persona-ml-auto persona-clear-chat-button-wrapper\";\n\n/**\n * Build the clear-chat (refresh) button + tooltip used in the panel header.\n * Extracted from `header-builder.ts` so composer-bar mode can render a\n * \"start over\" button alongside its close icon without duplicating the\n * tooltip + config-driven styling logic.\n *\n * The factory only handles construction. Wiring the click to the\n * clear-history handler is owned by `setupClearChatButton()` in `ui.ts`,\n * which keys off `panelElements.clearChatButton`.\n */\nexport const createClearChatButton = (\n  config: AgentWidgetConfig | undefined,\n  options: CreateClearChatButtonOptions = {},\n): ClearChatButtonParts => {\n  const {\n    wrapperClassName = DEFAULT_CLEAR_CHAT_WRAPPER_CLASS,\n    buttonSize,\n    iconSize = \"20px\",\n  } = options;\n\n  const launcher = config?.launcher ?? {};\n  const clearChatConfig = launcher.clearChat ?? {};\n  // Call-site `buttonSize` (when provided) wins over launcher.clearChat.size.\n  // Same precedence rule as createCloseButton: callers like composer-bar\n  // intentionally override the inherited launcher default to fit their UX.\n  const clearChatSize = buttonSize ?? clearChatConfig.size ?? \"32px\";\n  const clearChatIconName = clearChatConfig.iconName ?? \"refresh-cw\";\n  const clearChatIconColor = clearChatConfig.iconColor ?? \"\";\n  const clearChatBgColor = clearChatConfig.backgroundColor ?? \"\";\n  const clearChatBorderWidth = clearChatConfig.borderWidth ?? \"\";\n  const clearChatBorderColor = clearChatConfig.borderColor ?? \"\";\n  const clearChatBorderRadius = clearChatConfig.borderRadius ?? \"\";\n  const clearChatPaddingX = clearChatConfig.paddingX ?? \"\";\n  const clearChatPaddingY = clearChatConfig.paddingY ?? \"\";\n  const clearChatTooltipText = clearChatConfig.tooltipText ?? \"Clear chat\";\n  const clearChatShowTooltip = clearChatConfig.showTooltip ?? true;\n\n  const wrapper = createElement(\"div\", wrapperClassName);\n\n  // hover-bg / border-none / rounded-full are default utility classes that\n  // apply only when the matching style override is absent; an override sets\n  // the inline style instead (cx omits the class).\n  const hasClearChatBorder = Boolean(clearChatBorderWidth || clearChatBorderColor);\n\n  const button = createNode(\"button\", {\n    className: cx(\n      \"persona-inline-flex persona-items-center persona-justify-center persona-cursor-pointer\",\n      !clearChatBgColor && \"hover:persona-bg-gray-100\",\n      !hasClearChatBorder && \"persona-border-none\",\n      !clearChatBorderRadius && \"persona-rounded-full\"\n    ),\n    attrs: { type: \"button\", \"aria-label\": clearChatTooltipText },\n    style: {\n      height: clearChatSize,\n      width: clearChatSize,\n      color: clearChatIconColor || HEADER_THEME_CSS.actionIconColor,\n      backgroundColor: clearChatBgColor || undefined,\n      border: hasClearChatBorder\n        ? `${clearChatBorderWidth || \"0px\"} solid ${clearChatBorderColor || \"transparent\"}`\n        : undefined,\n      borderRadius: clearChatBorderRadius || undefined,\n      paddingLeft: clearChatPaddingX || undefined,\n      paddingRight: clearChatPaddingX || undefined,\n      paddingTop: clearChatPaddingY || undefined,\n      paddingBottom: clearChatPaddingY || undefined,\n    },\n  });\n\n  const iconSvg = renderLucideIcon(clearChatIconName, iconSize, \"currentColor\", 1);\n  if (iconSvg) {\n    iconSvg.style.display = \"block\";\n    button.appendChild(iconSvg);\n  }\n\n  wrapper.appendChild(button);\n\n  if (clearChatShowTooltip && clearChatTooltipText) {\n    let portaledTooltip: HTMLElement | null = null;\n\n    const showTooltip = () => {\n      if (portaledTooltip) return;\n\n      const tooltipDocument = button.ownerDocument;\n      const tooltipContainer = tooltipDocument.body;\n      if (!tooltipContainer) return;\n\n      portaledTooltip = createElementInDocument(\n        tooltipDocument,\n        \"div\",\n        \"persona-clear-chat-tooltip\"\n      );\n      portaledTooltip.textContent = clearChatTooltipText;\n\n      const arrow = createElementInDocument(tooltipDocument, \"div\");\n      arrow.className = \"persona-clear-chat-tooltip-arrow\";\n      portaledTooltip.appendChild(arrow);\n\n      const buttonRect = button.getBoundingClientRect();\n\n      portaledTooltip.style.position = \"fixed\";\n      portaledTooltip.style.zIndex = String(PORTALED_OVERLAY_Z_INDEX);\n      portaledTooltip.style.left = `${buttonRect.left + buttonRect.width / 2}px`;\n      portaledTooltip.style.top = `${buttonRect.top - 8}px`;\n      portaledTooltip.style.transform = \"translate(-50%, -100%)\";\n\n      tooltipContainer.appendChild(portaledTooltip);\n    };\n\n    const hideTooltip = () => {\n      if (portaledTooltip && portaledTooltip.parentNode) {\n        portaledTooltip.parentNode.removeChild(portaledTooltip);\n        portaledTooltip = null;\n      }\n    };\n\n    wrapper.addEventListener(\"mouseenter\", showTooltip);\n    wrapper.addEventListener(\"mouseleave\", hideTooltip);\n    button.addEventListener(\"focus\", showTooltip);\n    button.addEventListener(\"blur\", hideTooltip);\n\n    (wrapper as any)._cleanupTooltip = () => {\n      hideTooltip();\n      wrapper.removeEventListener(\"mouseenter\", showTooltip);\n      wrapper.removeEventListener(\"mouseleave\", hideTooltip);\n      button.removeEventListener(\"focus\", showTooltip);\n      button.removeEventListener(\"blur\", hideTooltip);\n    };\n  }\n\n  return { button, wrapper };\n};\n","import { createElement, createNode } from \"../utils/dom\";\nimport { renderLucideIcon } from \"../utils/icons\";\nimport { AgentWidgetConfig } from \"../types\";\nimport { createCloseButton, createClearChatButton } from \"./header-parts\";\n\n/** CSS `color` values; variables are set on `[data-persona-root]` from `theme.components.header`. */\nexport const HEADER_THEME_CSS = {\n  titleColor:\n    \"var(--persona-header-title-fg, var(--persona-primary, #0f0f0f))\",\n  subtitleColor:\n    \"var(--persona-header-subtitle-fg, var(--persona-text-muted, var(--persona-muted, #9ca3af)))\",\n  actionIconColor:\n    \"var(--persona-header-action-icon-fg, var(--persona-muted, #9ca3af))\",\n} as const;\n\nexport interface HeaderElements {\n  header: HTMLElement;\n  iconHolder: HTMLElement;\n  headerTitle: HTMLElement;\n  headerSubtitle: HTMLElement;\n  closeButton: HTMLButtonElement;\n  closeButtonWrapper: HTMLElement;\n  clearChatButton: HTMLButtonElement | null;\n  clearChatButtonWrapper: HTMLElement | null;\n}\n\nexport interface HeaderBuildContext {\n  config?: AgentWidgetConfig;\n  showClose?: boolean;\n  onClose?: () => void;\n  onClearChat?: () => void;\n}\n\n/**\n * Build the header section of the panel.\n * Extracted for reuse and plugin override support.\n */\nexport const buildHeader = (context: HeaderBuildContext): HeaderElements => {\n  const { config, showClose = true } = context;\n\n  const header = createNode(\"div\", {\n    className:\n      \"persona-widget-header persona-flex persona-items-center persona-gap-3 persona-px-6 persona-py-5\",\n    attrs: { \"data-persona-theme-zone\": \"header\" },\n    style: {\n      backgroundColor: \"var(--persona-header-bg, var(--persona-surface, #ffffff))\",\n      borderBottomColor: \"var(--persona-header-border, var(--persona-divider, #f1f5f9))\",\n      boxShadow: \"var(--persona-header-shadow, none)\",\n      borderBottom:\n        \"var(--persona-header-border-bottom, 1px solid var(--persona-header-border, var(--persona-divider, #f1f5f9)))\",\n    },\n  });\n\n  const launcher = config?.launcher ?? {};\n  const headerIconSize = launcher.headerIconSize ?? \"48px\";\n  const closeButtonPlacement = launcher.closeButtonPlacement ?? \"inline\";\n  const headerIconHidden = launcher.headerIconHidden ?? false;\n  const headerIconName = launcher.headerIconName;\n\n  const iconHolder = createNode(\"div\", {\n    className:\n      \"persona-flex persona-items-center persona-justify-center persona-rounded-xl persona-text-xl\",\n    style: {\n      height: headerIconSize,\n      width: headerIconSize,\n      backgroundColor: \"var(--persona-header-icon-bg, var(--persona-primary, #0f0f0f))\",\n      color: \"var(--persona-header-icon-fg, var(--persona-text-inverse, #ffffff))\",\n    },\n  });\n\n  // Render icon based on priority: Lucide icon > iconUrl > agentIconText\n  if (!headerIconHidden) {\n    if (headerIconName) {\n      // Use Lucide icon\n      const iconSize = parseFloat(headerIconSize) || 24;\n      const iconSvg = renderLucideIcon(headerIconName, iconSize * 0.6, \"currentColor\", 1);\n      if (iconSvg) {\n        iconHolder.replaceChildren(iconSvg);\n      } else {\n        // Fallback to agentIconText if Lucide icon fails\n        iconHolder.textContent = config?.launcher?.agentIconText ?? \"💬\";\n      }\n    } else if (config?.launcher?.iconUrl) {\n      // Use image URL\n      const img = createElement(\"img\") as HTMLImageElement;\n      img.src = config.launcher.iconUrl;\n      img.alt = \"\";\n      img.className = \"persona-rounded-xl persona-object-cover\";\n      img.style.height = headerIconSize;\n      img.style.width = headerIconSize;\n      iconHolder.replaceChildren(img);\n    } else {\n      // Use text/emoji\n      iconHolder.textContent = config?.launcher?.agentIconText ?? \"💬\";\n    }\n  }\n\n  const headerCopy = createElement(\"div\", \"persona-flex persona-flex-col persona-flex-1 persona-min-w-0\");\n  const title = createNode(\"span\", {\n    className: \"persona-text-base persona-font-semibold\",\n    text: config?.launcher?.title ?? \"Chat Assistant\",\n    style: { color: HEADER_THEME_CSS.titleColor },\n  });\n  const subtitle = createNode(\"span\", {\n    className: \"persona-text-xs\",\n    text: config?.launcher?.subtitle ?? \"Here to help you get answers fast\",\n    style: { color: HEADER_THEME_CSS.subtitleColor },\n  });\n\n  headerCopy.append(title, subtitle);\n\n  // Only append iconHolder if not hidden\n  if (!headerIconHidden) {\n    header.append(iconHolder, headerCopy);\n  } else {\n    header.append(headerCopy);\n  }\n\n  // Create clear chat button if enabled\n  const clearChatConfig = launcher.clearChat ?? {};\n  const clearChatEnabled = clearChatConfig.enabled ?? true;\n  const clearChatPlacement = clearChatConfig.placement ?? \"inline\";\n  let clearChatButton: HTMLButtonElement | null = null;\n  let clearChatButtonWrapper: HTMLElement | null = null;\n\n  if (clearChatEnabled) {\n    // Top-right placement uses an absolute wrapper offset from the close\n    // button (which lives at right: 16px and is ~32px wide, leaving ~48px\n    // from the panel's right edge for the clear icon).\n    const wrapperClassName =\n      clearChatPlacement === \"top-right\"\n        ? \"persona-absolute persona-top-4 persona-z-50\"\n        : \"persona-relative persona-ml-auto persona-clear-chat-button-wrapper\";\n\n    const parts = createClearChatButton(config, { wrapperClassName });\n    clearChatButton = parts.button;\n    clearChatButtonWrapper = parts.wrapper;\n\n    if (clearChatPlacement === \"top-right\") {\n      clearChatButtonWrapper.style.right = \"48px\";\n    }\n\n    // Only append to header if inline placement\n    if (clearChatPlacement === \"inline\") {\n      header.appendChild(clearChatButtonWrapper);\n    }\n  }\n\n  // Build the close (×) button via the shared factory. The wrapper class\n  // mirrors the clear-chat wrapper's inline-flex centering so both header\n  // action buttons vertically align identically within the header's flex\n  // row. composer-bar mode uses the same factory directly with its own\n  // wrapper class to render a top-right-only close button.\n  const closeButtonWrapperClass =\n    closeButtonPlacement === \"top-right\"\n      ? \"persona-absolute persona-top-4 persona-right-4 persona-z-50\"\n      : clearChatEnabled && clearChatPlacement === \"inline\"\n        ? \"persona-relative persona-inline-flex persona-items-center persona-justify-center\"\n        : \"persona-relative persona-ml-auto persona-inline-flex persona-items-center persona-justify-center\";\n\n  const { button: closeButton, wrapper: closeButtonWrapper } = createCloseButton(\n    config,\n    { showClose, wrapperClassName: closeButtonWrapperClass }\n  );\n\n  // Inline placement: append close button to header\n  if (closeButtonPlacement !== \"top-right\") {\n    header.appendChild(closeButtonWrapper);\n  }\n\n  return {\n    header,\n    iconHolder,\n    headerTitle: title,\n    headerSubtitle: subtitle,\n    closeButton,\n    closeButtonWrapper,\n    clearChatButton,\n    clearChatButtonWrapper\n  };\n};\n\n/**\n * Attach header elements to the container, handling placement modes.\n */\nexport const attachHeaderToContainer = (\n  container: HTMLElement,\n  headerElements: HeaderElements,\n  config?: AgentWidgetConfig\n): void => {\n  const launcher = config?.launcher ?? {};\n  const closeButtonPlacement = launcher.closeButtonPlacement ?? \"inline\";\n  const clearChatPlacement = launcher.clearChat?.placement ?? \"inline\";\n\n  // Add header to container\n  container.appendChild(headerElements.header);\n\n  // Position close button wrapper if top-right placement\n  if (closeButtonPlacement === \"top-right\") {\n    container.style.position = \"relative\";\n    container.appendChild(headerElements.closeButtonWrapper);\n  }\n\n  // Position clear chat button wrapper if top-right placement\n  if (\n    headerElements.clearChatButtonWrapper &&\n    clearChatPlacement === \"top-right\"\n  ) {\n    container.style.position = \"relative\";\n    container.appendChild(headerElements.clearChatButtonWrapper);\n  }\n};\n\n","import { createElement } from \"./dom\";\nimport { renderLucideIcon } from \"./icons\";\nimport { PORTALED_OVERLAY_Z_INDEX } from \"./constants\";\n\nexport interface DropdownMenuItem {\n  id: string;\n  label: string;\n  /** Lucide icon name to show before the label. */\n  icon?: string;\n  /** When true, item text is styled in a destructive/danger color. */\n  destructive?: boolean;\n  /** When true, a visual divider is inserted before this item. */\n  dividerBefore?: boolean;\n}\n\nexport interface CreateDropdownOptions {\n  /** Menu items to render. */\n  items: DropdownMenuItem[];\n  /** Called when a menu item is selected. */\n  onSelect: (id: string) => void;\n  /** Anchor element used for positioning. When `portal` is not set the menu is appended inside this element (which must have position: relative). */\n  anchor: HTMLElement;\n  /** Alignment of the menu relative to the anchor. Default: 'bottom-left'. */\n  position?: 'bottom-left' | 'bottom-right';\n  /**\n   * Portal target element. When set, the menu is appended to this element\n   * and uses fixed positioning calculated from the anchor's bounding rect.\n   * Use this to escape `overflow: hidden` containers.\n   */\n  portal?: HTMLElement;\n}\n\nexport interface DropdownMenuHandle {\n  /** The menu DOM element. */\n  element: HTMLElement;\n  /** Show the menu. */\n  show: () => void;\n  /** Hide the menu. */\n  hide: () => void;\n  /** Toggle visibility. */\n  toggle: () => void;\n  /** Remove the menu and clean up all listeners. */\n  destroy: () => void;\n}\n\n/**\n * Create a dropdown menu attached to an anchor element.\n *\n * The menu is styled via `.persona-dropdown-menu` CSS rules and themed\n * through `--persona-dropdown-*` CSS variables with semantic fallbacks.\n *\n * ```ts\n * import { createDropdownMenu } from \"@runtypelabs/persona\";\n *\n * const dropdown = createDropdownMenu({\n *   items: [\n *     { id: \"edit\", label: \"Edit\", icon: \"pencil\" },\n *     { id: \"delete\", label: \"Delete\", icon: \"trash-2\", destructive: true, dividerBefore: true },\n *   ],\n *   onSelect: (id) => console.log(\"selected\", id),\n *   anchor: buttonElement,\n * });\n * anchor.appendChild(dropdown.element);\n * button.addEventListener(\"click\", () => dropdown.toggle());\n * ```\n */\nexport function createDropdownMenu(options: CreateDropdownOptions): DropdownMenuHandle {\n  const { items, onSelect, anchor, position = 'bottom-left', portal } = options;\n\n  const menu = createElement(\"div\", \"persona-dropdown-menu persona-hidden\");\n  menu.setAttribute(\"role\", \"menu\");\n  menu.setAttribute(\"data-persona-theme-zone\", \"dropdown\");\n\n  if (portal) {\n    // Fixed positioning: menu is portaled outside the anchor's overflow context\n    menu.style.position = \"fixed\";\n    menu.style.zIndex = String(PORTALED_OVERLAY_Z_INDEX);\n  } else {\n    // Absolute positioning: menu lives inside the anchor\n    menu.style.position = \"absolute\";\n    menu.style.top = \"100%\";\n    menu.style.marginTop = \"4px\";\n    if (position === 'bottom-right') {\n      menu.style.right = \"0\";\n    } else {\n      menu.style.left = \"0\";\n    }\n  }\n\n  // Build menu items\n  for (const item of items) {\n    if (item.dividerBefore) {\n      const hr = document.createElement(\"hr\");\n      menu.appendChild(hr);\n    }\n\n    const btn = document.createElement(\"button\");\n    btn.type = \"button\";\n    btn.setAttribute(\"role\", \"menuitem\");\n    btn.setAttribute(\"data-dropdown-item-id\", item.id);\n    if (item.destructive) {\n      btn.setAttribute(\"data-destructive\", \"\");\n    }\n\n    if (item.icon) {\n      const icon = renderLucideIcon(item.icon, 16, \"currentColor\", 1.5);\n      if (icon) btn.appendChild(icon);\n    }\n\n    const labelSpan = document.createElement(\"span\");\n    labelSpan.textContent = item.label;\n    btn.appendChild(labelSpan);\n\n    btn.addEventListener(\"click\", (e) => {\n      e.stopPropagation();\n      hide();\n      onSelect(item.id);\n    });\n\n    menu.appendChild(btn);\n  }\n\n  let cleanupClickOutside: (() => void) | null = null;\n\n  /** Reposition a portaled menu based on the anchor's current bounding rect. */\n  function reposition() {\n    if (!portal) return;\n    const rect = anchor.getBoundingClientRect();\n    menu.style.top = `${rect.bottom + 4}px`;\n    if (position === 'bottom-right') {\n      menu.style.right = `${window.innerWidth - rect.right}px`;\n      menu.style.left = \"auto\";\n    } else {\n      menu.style.left = `${rect.left}px`;\n      menu.style.right = \"auto\";\n    }\n  }\n\n  function show() {\n    reposition();\n    menu.classList.remove(\"persona-hidden\");\n    // Defer click-outside listener to avoid catching the triggering click\n    requestAnimationFrame(() => {\n      const handler = (e: MouseEvent) => {\n        if (!menu.contains(e.target as Node) && !anchor.contains(e.target as Node)) {\n          hide();\n        }\n      };\n      document.addEventListener(\"click\", handler, true);\n      cleanupClickOutside = () => document.removeEventListener(\"click\", handler, true);\n    });\n  }\n\n  function hide() {\n    menu.classList.add(\"persona-hidden\");\n    cleanupClickOutside?.();\n    cleanupClickOutside = null;\n  }\n\n  function toggle() {\n    if (menu.classList.contains(\"persona-hidden\")) {\n      show();\n    } else {\n      hide();\n    }\n  }\n\n  function destroy() {\n    hide();\n    menu.remove();\n  }\n\n  // Append to portal target or let the caller append manually\n  if (portal) {\n    portal.appendChild(menu);\n  }\n\n  return { element: menu, show, hide, toggle, destroy };\n}\n","import { createElement } from \"./dom\";\nimport { renderLucideIcon } from \"./icons\";\nimport { createDropdownMenu, type DropdownMenuItem } from \"./dropdown\";\n\n// ---------------------------------------------------------------------------\n// createIconButton\n// ---------------------------------------------------------------------------\n\n/** Options for {@link createIconButton}. */\nexport interface CreateIconButtonOptions {\n  /** Lucide icon name (kebab-case, e.g. \"eye\", \"chevron-down\"). */\n  icon: string;\n  /** Accessible label (used for aria-label and title). */\n  label: string;\n  /** Icon size in pixels. Default: 16. */\n  size?: number;\n  /** Icon stroke width. Default: 2. */\n  strokeWidth?: number;\n  /** Extra CSS class(es) appended after \"persona-icon-btn\". */\n  className?: string;\n  /** Click handler. */\n  onClick?: (e: MouseEvent) => void;\n  /** Additional ARIA attributes (e.g. { \"aria-haspopup\": \"true\" }). */\n  aria?: Record<string, string>;\n}\n\n/**\n * Creates a minimal icon-only button with accessible labelling.\n *\n * The button receives the base class `persona-icon-btn` and renders a single\n * Lucide icon inside it.\n */\nexport function createIconButton(options: CreateIconButtonOptions): HTMLButtonElement {\n  const { icon, label, size, strokeWidth, className, onClick, aria } = options;\n\n  const btn = createElement(\n    \"button\",\n    \"persona-icon-btn\" + (className ? \" \" + className : \"\"),\n  );\n  btn.type = \"button\";\n  btn.setAttribute(\"aria-label\", label);\n  btn.title = label;\n\n  const svg = renderLucideIcon(icon, size ?? 16, \"currentColor\", strokeWidth ?? 2);\n  if (svg) {\n    btn.appendChild(svg);\n  }\n\n  if (onClick) {\n    btn.addEventListener(\"click\", onClick);\n  }\n\n  if (aria) {\n    for (const [key, value] of Object.entries(aria)) {\n      btn.setAttribute(key, value);\n    }\n  }\n\n  return btn;\n}\n\n// ---------------------------------------------------------------------------\n// createLabelButton\n// ---------------------------------------------------------------------------\n\n/** Options for {@link createLabelButton}. */\nexport interface CreateLabelButtonOptions {\n  /** Optional Lucide icon name shown before the label. */\n  icon?: string;\n  /** Button text label (also used for aria-label). */\n  label: string;\n  /** Visual variant. Default: \"default\". */\n  variant?: \"default\" | \"primary\" | \"destructive\" | \"ghost\";\n  /** Size preset. Default: \"sm\". */\n  size?: \"sm\" | \"md\";\n  /** Icon size in pixels. Default: 14. */\n  iconSize?: number;\n  /** Extra CSS class(es). */\n  className?: string;\n  /** Click handler. */\n  onClick?: (e: MouseEvent) => void;\n  /** Additional ARIA attributes. */\n  aria?: Record<string, string>;\n}\n\n/**\n * Creates a button with an optional leading icon and a text label.\n *\n * CSS classes follow the BEM-like pattern:\n * `persona-label-btn persona-label-btn--{variant} persona-label-btn--{size}`\n */\nexport function createLabelButton(options: CreateLabelButtonOptions): HTMLButtonElement {\n  const {\n    icon,\n    label,\n    variant = \"default\",\n    size = \"sm\",\n    iconSize,\n    className,\n    onClick,\n    aria,\n  } = options;\n\n  let classString = \"persona-label-btn\";\n  if (variant !== \"default\") {\n    classString += \" persona-label-btn--\" + variant;\n  }\n  classString += \" persona-label-btn--\" + size;\n  if (className) {\n    classString += \" \" + className;\n  }\n\n  const btn = createElement(\"button\", classString);\n  btn.type = \"button\";\n  btn.setAttribute(\"aria-label\", label);\n\n  if (icon) {\n    const svg = renderLucideIcon(icon, iconSize ?? 14, \"currentColor\", 2);\n    if (svg) {\n      btn.appendChild(svg);\n    }\n  }\n\n  const span = createElement(\"span\");\n  span.textContent = label;\n  btn.appendChild(span);\n\n  if (onClick) {\n    btn.addEventListener(\"click\", onClick);\n  }\n\n  if (aria) {\n    for (const [key, value] of Object.entries(aria)) {\n      btn.setAttribute(key, value);\n    }\n  }\n\n  return btn;\n}\n\n// ---------------------------------------------------------------------------\n// createToggleGroup\n// ---------------------------------------------------------------------------\n\n/** Describes a single item inside a toggle group. */\nexport interface ToggleGroupItem {\n  id: string;\n  /** Lucide icon name. If omitted, uses label as text. */\n  icon?: string;\n  /** Accessible label for the button. */\n  label: string;\n}\n\n/** Options for {@link createToggleGroup}. */\nexport interface CreateToggleGroupOptions {\n  /** Toggle items. */\n  items: ToggleGroupItem[];\n  /** Initially selected item id. */\n  selectedId: string;\n  /** Called when selection changes. */\n  onSelect: (id: string) => void;\n  /** Extra CSS class(es) on the wrapper. */\n  className?: string;\n}\n\n/** Handle returned by {@link createToggleGroup}. */\nexport interface ToggleGroupHandle {\n  /** The wrapper element containing toggle buttons. */\n  element: HTMLElement;\n  /** Programmatically change the selected item. */\n  setSelected: (id: string) => void;\n}\n\n/**\n * Creates a group of mutually-exclusive toggle buttons.\n *\n * Each button uses `aria-pressed` to communicate its state. Only one button\n * can be active at a time.\n */\nexport function createToggleGroup(options: CreateToggleGroupOptions): ToggleGroupHandle {\n  const { items, selectedId, onSelect, className } = options;\n\n  const wrapper = createElement(\n    \"div\",\n    \"persona-toggle-group\" + (className ? \" \" + className : \"\"),\n  );\n  wrapper.setAttribute(\"role\", \"group\");\n\n  let currentId = selectedId;\n  const buttons: { id: string; btn: HTMLButtonElement }[] = [];\n\n  function updatePressed() {\n    for (const entry of buttons) {\n      entry.btn.setAttribute(\"aria-pressed\", entry.id === currentId ? \"true\" : \"false\");\n    }\n  }\n\n  for (const item of items) {\n    let btn: HTMLButtonElement;\n\n    if (item.icon) {\n      btn = createIconButton({\n        icon: item.icon,\n        label: item.label,\n        onClick: () => {\n          currentId = item.id;\n          updatePressed();\n          onSelect(item.id);\n        },\n      });\n    } else {\n      btn = createElement(\"button\", \"persona-icon-btn\");\n      btn.type = \"button\";\n      btn.setAttribute(\"aria-label\", item.label);\n      btn.title = item.label;\n      btn.textContent = item.label;\n      btn.addEventListener(\"click\", () => {\n        currentId = item.id;\n        updatePressed();\n        onSelect(item.id);\n      });\n    }\n\n    btn.setAttribute(\"aria-pressed\", item.id === currentId ? \"true\" : \"false\");\n    buttons.push({ id: item.id, btn });\n    wrapper.appendChild(btn);\n  }\n\n  function setSelected(id: string) {\n    currentId = id;\n    updatePressed();\n  }\n\n  return { element: wrapper, setSelected };\n}\n\n// ---------------------------------------------------------------------------\n// createComboButton\n// ---------------------------------------------------------------------------\n\n/** Options for {@link createComboButton}. */\nexport interface CreateComboButtonOptions {\n  /** Button text label. */\n  label: string;\n  /** Lucide icon name for the dropdown indicator (default: \"chevron-down\"). */\n  icon?: string;\n  /** Dropdown menu items. */\n  menuItems: DropdownMenuItem[];\n  /** Called when a menu item is selected. */\n  onSelect: (id: string) => void;\n  /** Where to align the dropdown. Default: \"bottom-left\". */\n  position?: \"bottom-left\" | \"bottom-right\";\n  /**\n   * Portal target for the dropdown menu. When set, the menu escapes\n   * overflow containers by rendering inside this element with fixed positioning.\n   */\n  portal?: HTMLElement;\n  /** Extra CSS class(es) on the wrapper element. */\n  className?: string;\n  /** Hover style for the pill effect. */\n  hover?: {\n    background?: string;\n    border?: string;\n    borderRadius?: string;\n    padding?: string;\n  };\n}\n\n/** Handle returned by {@link createComboButton}. */\nexport interface ComboButtonHandle {\n  /** The wrapper element (label + chevron + dropdown). */\n  element: HTMLElement;\n  /** Update the displayed label text. */\n  setLabel: (text: string) => void;\n  /** Open the dropdown. */\n  open: () => void;\n  /** Close the dropdown. */\n  close: () => void;\n  /** Toggle the dropdown. */\n  toggle: () => void;\n  /** Remove from DOM and clean up listeners. */\n  destroy: () => void;\n}\n\n/**\n * Creates a combo button: a clickable label with a chevron that opens a dropdown menu.\n *\n * The entire label + chevron area acts as a single interactive unit with an optional\n * hover pill effect. Clicking anywhere on it toggles the dropdown.\n *\n * ```ts\n * import { createComboButton } from \"@runtypelabs/persona\";\n *\n * const combo = createComboButton({\n *   label: \"Chat Assistant\",\n *   menuItems: [\n *     { id: \"star\", label: \"Star\", icon: \"star\" },\n *     { id: \"rename\", label: \"Rename\", icon: \"pencil\" },\n *     { id: \"delete\", label: \"Delete\", icon: \"trash-2\", destructive: true, dividerBefore: true },\n *   ],\n *   onSelect: (id) => console.log(\"Selected:\", id),\n * });\n * header.appendChild(combo.element);\n * ```\n */\nexport function createComboButton(options: CreateComboButtonOptions): ComboButtonHandle {\n  const {\n    label,\n    icon = \"chevron-down\",\n    menuItems,\n    onSelect,\n    position = \"bottom-left\",\n    portal,\n    className,\n    hover,\n  } = options;\n\n  const wrapper = createElement(\n    \"div\",\n    \"persona-combo-btn\" + (className ? \" \" + className : \"\"),\n  );\n  wrapper.style.position = \"relative\";\n  wrapper.style.display = \"inline-flex\";\n  wrapper.style.alignItems = \"center\";\n  wrapper.style.cursor = \"pointer\";\n  wrapper.setAttribute(\"role\", \"button\");\n  wrapper.setAttribute(\"tabindex\", \"0\");\n  wrapper.setAttribute(\"aria-haspopup\", \"true\");\n  wrapper.setAttribute(\"aria-expanded\", \"false\");\n  wrapper.setAttribute(\"aria-label\", label);\n\n  // Label text\n  const labelEl = createElement(\"span\", \"persona-combo-btn-label\");\n  labelEl.textContent = label;\n  wrapper.appendChild(labelEl);\n\n  // Chevron icon\n  const chevron = renderLucideIcon(icon, 14, \"currentColor\", 2);\n  if (chevron) {\n    chevron.style.marginLeft = \"4px\";\n    chevron.style.opacity = \"0.6\";\n    wrapper.appendChild(chevron);\n  }\n\n  // Hover pill effect\n  if (hover) {\n    wrapper.style.borderRadius = hover.borderRadius ?? \"10px\";\n    wrapper.style.padding = hover.padding ?? \"6px 4px 6px 12px\";\n    wrapper.style.border = \"1px solid transparent\";\n    wrapper.style.transition = \"background-color 0.15s ease, border-color 0.15s ease\";\n    wrapper.addEventListener(\"mouseenter\", () => {\n      wrapper.style.backgroundColor = hover.background ?? \"\";\n      wrapper.style.borderColor = hover.border ?? \"\";\n    });\n    wrapper.addEventListener(\"mouseleave\", () => {\n      wrapper.style.backgroundColor = \"\";\n      wrapper.style.borderColor = \"transparent\";\n    });\n  }\n\n  // Dropdown\n  const dropdown = createDropdownMenu({\n    items: menuItems,\n    onSelect: (id) => {\n      wrapper.setAttribute(\"aria-expanded\", \"false\");\n      onSelect(id);\n    },\n    anchor: wrapper,\n    position,\n    portal,\n  });\n\n  if (!portal) {\n    wrapper.appendChild(dropdown.element);\n  }\n\n  // Click toggles dropdown\n  wrapper.addEventListener(\"click\", (e) => {\n    e.stopPropagation();\n    const isOpen = !dropdown.element.classList.contains(\"persona-hidden\");\n    wrapper.setAttribute(\"aria-expanded\", isOpen ? \"false\" : \"true\");\n    dropdown.toggle();\n  });\n\n  // Keyboard support\n  wrapper.addEventListener(\"keydown\", (e) => {\n    if (e.key === \"Enter\" || e.key === \" \") {\n      e.preventDefault();\n      wrapper.click();\n    }\n  });\n\n  return {\n    element: wrapper,\n    setLabel: (text: string) => {\n      labelEl.textContent = text;\n      wrapper.setAttribute(\"aria-label\", text);\n    },\n    open: () => {\n      wrapper.setAttribute(\"aria-expanded\", \"true\");\n      dropdown.show();\n    },\n    close: () => {\n      wrapper.setAttribute(\"aria-expanded\", \"false\");\n      dropdown.hide();\n    },\n    toggle: () => {\n      const isOpen = !dropdown.element.classList.contains(\"persona-hidden\");\n      wrapper.setAttribute(\"aria-expanded\", isOpen ? \"false\" : \"true\");\n      dropdown.toggle();\n    },\n    destroy: () => {\n      dropdown.destroy();\n      wrapper.remove();\n    },\n  };\n}\n","import { createElement } from \"../utils/dom\";\nimport { renderLucideIcon } from \"../utils/icons\";\nimport { createDropdownMenu } from \"../utils/dropdown\";\nimport { createComboButton } from \"../utils/buttons\";\nimport {\n  AgentWidgetConfig,\n  AgentWidgetHeaderLayoutConfig,\n  AgentWidgetHeaderTrailingAction\n} from \"../types\";\nimport {\n  buildHeader,\n  HEADER_THEME_CSS,\n  HeaderElements,\n  attachHeaderToContainer as _attachHeaderToContainer,\n} from \"./header-builder\";\n\nexport interface HeaderLayoutContext {\n  config: AgentWidgetConfig;\n  showClose?: boolean;\n  onClose?: () => void;\n  onClearChat?: () => void;\n  /** Passed from `buildHeaderWithLayout` for minimal/default chrome extensions */\n  layoutHeaderConfig?: AgentWidgetHeaderLayoutConfig;\n  onHeaderAction?: (actionId: string) => void;\n}\n\nexport type HeaderLayoutRenderer = (context: HeaderLayoutContext) => HeaderElements;\n\n/**\n * Build default header layout\n * Full header with icon, title, subtitle, clear chat, and close button\n */\nexport const buildDefaultHeader: HeaderLayoutRenderer = (context) => {\n  const elements = buildHeader({\n    config: context.config,\n    showClose: context.showClose,\n    onClose: context.onClose,\n    onClearChat: context.onClearChat\n  });\n\n  // Make the title/subtitle area clickable when onTitleClick is provided\n  const onTitleClick = context.layoutHeaderConfig?.onTitleClick;\n  if (onTitleClick) {\n    const headerCopy = elements.headerTitle.parentElement;\n    if (headerCopy) {\n      headerCopy.style.cursor = \"pointer\";\n      headerCopy.setAttribute(\"role\", \"button\");\n      headerCopy.setAttribute(\"tabindex\", \"0\");\n      headerCopy.addEventListener(\"click\", () => onTitleClick());\n      headerCopy.addEventListener(\"keydown\", (e) => {\n        if (e.key === \"Enter\" || e.key === \" \") {\n          e.preventDefault();\n          onTitleClick();\n        }\n      });\n    }\n  }\n\n  return elements;\n};\n\n/**\n * Build minimal header layout\n * Simplified layout with just title and close button\n */\nfunction appendTrailingHeaderActions(\n  container: HTMLElement,\n  actions: AgentWidgetHeaderTrailingAction[] | undefined,\n  onAction?: (id: string) => void\n): void {\n  if (!actions?.length) return;\n  for (const a of actions) {\n    const btn = createElement(\n      \"button\",\n      \"persona-inline-flex persona-items-center persona-justify-center persona-rounded-md persona-border-none persona-bg-transparent persona-p-0 persona-text-persona-muted hover:persona-opacity-80\"\n    ) as HTMLButtonElement;\n    btn.type = \"button\";\n    btn.setAttribute(\"aria-label\", a.ariaLabel ?? a.label ?? a.id);\n    if (a.icon) {\n      const ic = renderLucideIcon(a.icon, 14, \"currentColor\", 2);\n      if (ic) btn.appendChild(ic);\n    } else if (a.label) {\n      btn.textContent = a.label;\n    }\n\n    if (a.menuItems?.length) {\n      // Wrap in a relative container for dropdown positioning\n      const wrapper = createElement(\"div\", \"persona-relative\");\n      wrapper.appendChild(btn);\n      const dropdown = createDropdownMenu({\n        items: a.menuItems,\n        onSelect: (itemId) => onAction?.(itemId),\n        anchor: wrapper,\n        position: 'bottom-left',\n      });\n      wrapper.appendChild(dropdown.element);\n      btn.addEventListener(\"click\", (e) => {\n        e.stopPropagation();\n        dropdown.toggle();\n      });\n      container.appendChild(wrapper);\n    } else {\n      btn.addEventListener(\"click\", () => onAction?.(a.id));\n      container.appendChild(btn);\n    }\n  }\n}\n\nexport const buildMinimalHeader: HeaderLayoutRenderer = (context) => {\n  const { config, showClose = true, onClose, layoutHeaderConfig, onHeaderAction } = context;\n  const launcher = config?.launcher ?? {};\n\n  const header = createElement(\n    \"div\",\n    \"persona-flex persona-items-center persona-justify-between persona-px-6 persona-py-4\"\n  );\n  header.setAttribute(\"data-persona-theme-zone\", \"header\");\n  header.style.backgroundColor = 'var(--persona-header-bg, var(--persona-surface, #ffffff))';\n  header.style.borderBottomColor = 'var(--persona-header-border, var(--persona-divider, #f1f5f9))';\n  header.style.boxShadow = 'var(--persona-header-shadow, none)';\n  header.style.borderBottom =\n    'var(--persona-header-border-bottom, 1px solid var(--persona-header-border, var(--persona-divider, #f1f5f9)))';\n\n  // Build the title area: either a combo button (titleMenu) or standard title row\n  const titleMenuConfig = layoutHeaderConfig?.titleMenu;\n  let titleRow: HTMLElement;\n  let headerTitle: HTMLElement;\n\n  if (titleMenuConfig) {\n    // Combo button replaces title + trailing actions + hover\n    const combo = createComboButton({\n      label: launcher.title ?? \"Chat Assistant\",\n      menuItems: titleMenuConfig.menuItems,\n      onSelect: titleMenuConfig.onSelect,\n      hover: titleMenuConfig.hover,\n      className: \"\",\n    });\n    titleRow = combo.element;\n    titleRow.style.color = HEADER_THEME_CSS.titleColor;\n    // The combo button's label span acts as headerTitle for update()\n    headerTitle = titleRow.querySelector(\".persona-combo-btn-label\") ?? titleRow;\n  } else {\n    titleRow = createElement(\n      \"div\",\n      \"persona-flex persona-min-w-0 persona-flex-1 persona-items-center persona-gap-1\"\n    );\n\n    // Title only (no icon, no subtitle)\n    headerTitle = createElement(\"span\", \"persona-text-base persona-font-semibold persona-truncate\");\n    headerTitle.style.color = HEADER_THEME_CSS.titleColor;\n    headerTitle.textContent = launcher.title ?? \"Chat Assistant\";\n\n    titleRow.appendChild(headerTitle);\n    appendTrailingHeaderActions(\n      titleRow,\n      layoutHeaderConfig?.trailingActions,\n      layoutHeaderConfig?.onAction ?? onHeaderAction\n    );\n\n    // Make title row clickable when onTitleClick is provided\n    if (layoutHeaderConfig?.onTitleClick) {\n      titleRow.style.cursor = \"pointer\";\n      titleRow.setAttribute(\"role\", \"button\");\n      titleRow.setAttribute(\"tabindex\", \"0\");\n      const handleTitleClick = layoutHeaderConfig.onTitleClick;\n      titleRow.addEventListener(\"click\", (e) => {\n        if ((e.target as HTMLElement).closest(\"button\")) return;\n        handleTitleClick();\n      });\n      titleRow.addEventListener(\"keydown\", (e) => {\n        if (e.key === \"Enter\" || e.key === \" \") {\n          e.preventDefault();\n          handleTitleClick();\n        }\n      });\n    }\n\n    // Title row hover pill effect\n    const hoverCfg = layoutHeaderConfig?.titleRowHover;\n    if (hoverCfg) {\n      titleRow.style.borderRadius = hoverCfg.borderRadius ?? '10px';\n      titleRow.style.padding = hoverCfg.padding ?? '6px 4px 6px 12px';\n      titleRow.style.margin = '-6px 0 -6px -12px';\n      titleRow.style.border = '1px solid transparent';\n      titleRow.style.transition = 'background-color 0.15s ease, border-color 0.15s ease';\n      titleRow.style.width = 'fit-content';\n      titleRow.style.flex = 'none';\n      titleRow.addEventListener('mouseenter', () => {\n        titleRow.style.backgroundColor = hoverCfg.background ?? '';\n        titleRow.style.borderColor = hoverCfg.border ?? '';\n      });\n      titleRow.addEventListener('mouseleave', () => {\n        titleRow.style.backgroundColor = '';\n        titleRow.style.borderColor = 'transparent';\n      });\n    }\n  }\n\n  header.appendChild(titleRow);\n\n  // Close button\n  const closeButtonSize = launcher.closeButtonSize ?? \"32px\";\n  const closeButtonWrapper = createElement(\"div\", \"\");\n\n  const closeButton = createElement(\n    \"button\",\n    \"persona-inline-flex persona-items-center persona-justify-center persona-rounded-full hover:persona-bg-gray-100 persona-cursor-pointer persona-border-none\"\n  ) as HTMLButtonElement;\n  closeButton.style.height = closeButtonSize;\n  closeButton.style.width = closeButtonSize;\n  closeButton.type = \"button\";\n  closeButton.setAttribute(\"aria-label\", \"Close chat\");\n  closeButton.style.display = showClose ? \"\" : \"none\";\n  closeButton.style.color =\n    launcher.closeButtonColor || HEADER_THEME_CSS.actionIconColor;\n\n  const closeButtonIconName = launcher.closeButtonIconName ?? \"x\";\n  // Larger intrinsic size compensates for the X glyph's sparse viewBox\n  // (paths only occupy the middle 50%). Matches header-builder.ts.\n  const closeIconSvg = renderLucideIcon(closeButtonIconName, \"28px\", \"currentColor\", 1);\n  if (closeIconSvg) {\n    closeButton.appendChild(closeIconSvg);\n  } else {\n    closeButton.textContent = \"×\";\n  }\n\n  if (onClose) {\n    closeButton.addEventListener(\"click\", onClose);\n  }\n\n  closeButtonWrapper.appendChild(closeButton);\n  header.appendChild(closeButtonWrapper);\n\n  // title was moved into titleRow; keep headerTitle ref pointing at title for updateController\n\n  // Create placeholder elements for compatibility\n  const iconHolder = createElement(\"div\");\n  iconHolder.style.display = \"none\";\n  const headerSubtitle = createElement(\"span\");\n  headerSubtitle.style.display = \"none\";\n\n  return {\n    header,\n    iconHolder,\n    headerTitle,\n    headerSubtitle,\n    closeButton,\n    closeButtonWrapper,\n    clearChatButton: null,\n    clearChatButtonWrapper: null\n  };\n};\n\n/**\n * Header layout registry\n * Maps layout names to their renderer functions\n */\nexport const headerLayouts: Record<string, HeaderLayoutRenderer> = {\n  default: buildDefaultHeader,\n  minimal: buildMinimalHeader\n};\n\n/**\n * Get header layout renderer by name\n */\nexport const getHeaderLayout = (layoutName: string): HeaderLayoutRenderer => {\n  return headerLayouts[layoutName] ?? headerLayouts.default;\n};\n\n/**\n * Build header based on layout configuration\n * Applies layout config settings to determine which layout to use\n */\nexport const buildHeaderWithLayout = (\n  config: AgentWidgetConfig,\n  layoutConfig?: AgentWidgetHeaderLayoutConfig,\n  context?: Partial<HeaderLayoutContext>\n): HeaderElements => {\n  // If custom render is provided, use it\n  if (layoutConfig?.render) {\n    const customHeader = layoutConfig.render({\n      config,\n      onClose: context?.onClose,\n      onClearChat: context?.onClearChat,\n      trailingActions: layoutConfig.trailingActions,\n      onAction: layoutConfig.onAction\n    });\n    \n    // Wrap in HeaderElements structure\n    const iconHolder = createElement(\"div\");\n    iconHolder.style.display = \"none\";\n    const headerTitle = createElement(\"span\");\n    const headerSubtitle = createElement(\"span\");\n    const closeButton = createElement(\"button\") as HTMLButtonElement;\n    closeButton.style.display = \"none\";\n    const closeButtonWrapper = createElement(\"div\");\n    closeButtonWrapper.style.display = \"none\";\n    \n    return {\n      header: customHeader,\n      iconHolder,\n      headerTitle,\n      headerSubtitle,\n      closeButton,\n      closeButtonWrapper,\n      clearChatButton: null,\n      clearChatButtonWrapper: null\n    };\n  }\n\n  // Get layout renderer\n  const layoutName = layoutConfig?.layout ?? \"default\";\n  const layoutRenderer = getHeaderLayout(layoutName);\n\n  // Build header with layout\n  const headerElements = layoutRenderer({\n    config,\n    showClose: layoutConfig?.showCloseButton ?? context?.showClose ?? true,\n    onClose: context?.onClose,\n    onClearChat: context?.onClearChat,\n    layoutHeaderConfig: layoutConfig,\n    onHeaderAction: layoutConfig?.onAction\n  });\n\n  // Apply visibility settings from layout config\n  if (layoutConfig) {\n    if (layoutConfig.showIcon === false) {\n      headerElements.iconHolder.style.display = \"none\";\n    }\n    if (layoutConfig.showTitle === false) {\n      headerElements.headerTitle.style.display = \"none\";\n    }\n    if (layoutConfig.showSubtitle === false) {\n      headerElements.headerSubtitle.style.display = \"none\";\n    }\n    if (layoutConfig.showCloseButton === false) {\n      headerElements.closeButton.style.display = \"none\";\n    }\n    if (layoutConfig.showClearChat === false && headerElements.clearChatButtonWrapper) {\n      headerElements.clearChatButtonWrapper.style.display = \"none\";\n    }\n  }\n\n  return headerElements;\n};\n\n","import { createElement, createNode, cx } from \"../utils/dom\";\nimport { renderLucideIcon } from \"../utils/icons\";\nimport { AgentWidgetConfig } from \"../types\";\nimport { ALL_SUPPORTED_MIME_TYPES } from \"../utils/content\";\n\n/**\n * Low-level composer control factories. Both `buildComposer` (full,\n * column-stacked card) and `buildPillComposer` (single-row pill) consume\n * these: the only meaningful difference between the two composers is the\n * layout shell + className. No DOM assembly here; each factory returns the\n * element plus any handles the caller needs.\n *\n * Stable selectors (data attributes + class hooks) live with the elements\n * so `bindComposerRefsFromFooter()` in ui.ts finds them regardless of\n * which builder ran.\n */\n\nexport interface ComposerTextareaParts {\n  textarea: HTMLTextAreaElement;\n  /**\n   * Wire the input listener that grows the textarea up to its current\n   * `maxHeight`. Caller decides when to attach (full composer attaches\n   * immediately; pill composer also attaches because expanded mode users\n   * want multi-line composition).\n   */\n  attachAutoResize: () => void;\n}\n\nexport const createComposerTextarea = (config?: AgentWidgetConfig): ComposerTextareaParts => {\n  const textarea = createElement(\"textarea\") as HTMLTextAreaElement;\n  textarea.setAttribute(\"data-persona-composer-input\", \"\");\n  textarea.placeholder = config?.copy?.inputPlaceholder ?? \"Type your message…\";\n  textarea.className =\n    \"persona-w-full persona-min-h-[24px] persona-resize-none persona-border-none persona-bg-transparent persona-text-sm persona-text-persona-primary focus:persona-outline-none focus:persona-border-none persona-composer-textarea\";\n  textarea.rows = 1;\n\n  textarea.style.fontFamily =\n    'var(--persona-input-font-family, var(--persona-font-family, -apple-system, BlinkMacSystemFont, \"Segoe UI\", \"Helvetica Neue\", Arial, sans-serif))';\n  textarea.style.fontWeight = \"var(--persona-input-font-weight, var(--persona-font-weight, 400))\";\n\n  // Auto-resize: expand up to 3 lines for the full composer (line-height ~20px\n  // for text-sm). The pill composer overrides this maxHeight after construction\n  // (allowing more growth in expanded mode), and the closure below honors\n  // whatever maxHeight is set at the time of the input event.\n  const defaultMaxLines = 3;\n  const lineHeight = 20;\n  textarea.style.maxHeight = `${defaultMaxLines * lineHeight}px`;\n  textarea.style.overflowY = \"auto\";\n\n  // Read maxHeight at event time so callers can change it after construction.\n  const readMaxHeight = (): number => {\n    const parsed = parseFloat(textarea.style.maxHeight);\n    return Number.isFinite(parsed) && parsed > 0 ? parsed : defaultMaxLines * lineHeight;\n  };\n\n  const attachAutoResize = () => {\n    textarea.addEventListener(\"input\", () => {\n      textarea.style.height = \"auto\";\n      const newHeight = Math.min(textarea.scrollHeight, readMaxHeight());\n      textarea.style.height = `${newHeight}px`;\n    });\n  };\n\n  // Strip browser default focus rings: the composer wraps the textarea in\n  // its own surface, so the textarea itself must be visually transparent.\n  textarea.style.border = \"none\";\n  textarea.style.outline = \"none\";\n  textarea.style.borderWidth = \"0\";\n  textarea.style.borderStyle = \"none\";\n  textarea.style.borderColor = \"transparent\";\n  textarea.addEventListener(\"focus\", () => {\n    textarea.style.border = \"none\";\n    textarea.style.outline = \"none\";\n    textarea.style.borderWidth = \"0\";\n    textarea.style.borderStyle = \"none\";\n    textarea.style.borderColor = \"transparent\";\n    textarea.style.boxShadow = \"none\";\n  });\n  textarea.addEventListener(\"blur\", () => {\n    textarea.style.border = \"none\";\n    textarea.style.outline = \"none\";\n  });\n\n  return { textarea, attachAutoResize };\n};\n\nexport interface SendButtonParts {\n  button: HTMLButtonElement;\n  wrapper: HTMLElement;\n  /**\n   * Swap the button between its idle (\"send\") and streaming (\"stop\")\n   * appearances. In icon mode this swaps the SVG; in text mode it swaps\n   * the label. Tooltip text and aria-label update too.\n   */\n  setMode: (mode: \"send\" | \"stop\") => void;\n}\n\nexport const createSendButton = (config?: AgentWidgetConfig): SendButtonParts => {\n  const sendButtonConfig = config?.sendButton ?? {};\n  const useIcon = sendButtonConfig.useIcon ?? false;\n  const iconText = sendButtonConfig.iconText ?? \"↑\";\n  const iconName = sendButtonConfig.iconName;\n  const stopIconName = sendButtonConfig.stopIconName ?? \"square\";\n  const tooltipText = sendButtonConfig.tooltipText ?? \"Send message\";\n  const stopTooltipText = sendButtonConfig.stopTooltipText ?? \"Stop generating\";\n  const sendLabel = config?.copy?.sendButtonLabel ?? \"Send\";\n  const stopLabel = config?.copy?.stopButtonLabel ?? \"Stop\";\n  const showTooltip = sendButtonConfig.showTooltip ?? false;\n  const buttonSize = sendButtonConfig.size ?? \"40px\";\n  const backgroundColor = sendButtonConfig.backgroundColor;\n  const textColor = sendButtonConfig.textColor;\n\n  const wrapper = createElement(\"div\", \"persona-send-button-wrapper\");\n\n  const button = createNode(\"button\", {\n    className: cx(\n      \"persona-rounded-button disabled:persona-opacity-50 persona-cursor-pointer\",\n      useIcon\n        ? \"persona-flex persona-items-center persona-justify-center\"\n        : \"persona-bg-persona-accent persona-px-4 persona-py-2 persona-text-sm persona-font-semibold\",\n      // Icon mode without an explicit background falls back to the primary bg\n      // class; text mode without an explicit color falls back to white text.\n      useIcon && !backgroundColor && \"persona-bg-persona-primary\",\n      !useIcon && !textColor && \"persona-text-white\"\n    ),\n    attrs: { type: \"submit\", \"data-persona-composer-submit\": \"\" },\n    style: {\n      // Sizing is icon-mode-only (text mode is sized by its padding classes).\n      width: useIcon ? buttonSize : undefined,\n      height: useIcon ? buttonSize : undefined,\n      minWidth: useIcon ? buttonSize : undefined,\n      minHeight: useIcon ? buttonSize : undefined,\n      fontSize: useIcon ? \"18px\" : undefined,\n      lineHeight: useIcon ? \"1\" : undefined,\n      // Icon mode always sets a color; text mode only when textColor is given.\n      color: useIcon\n        ? textColor || \"var(--persona-button-primary-fg, #ffffff)\"\n        : textColor || undefined,\n      // backgroundColor is honored in icon mode only.\n      backgroundColor: useIcon ? backgroundColor || undefined : undefined,\n      borderWidth: sendButtonConfig.borderWidth || undefined,\n      borderStyle: sendButtonConfig.borderWidth ? \"solid\" : undefined,\n      borderColor: sendButtonConfig.borderColor || undefined,\n      paddingLeft: sendButtonConfig.paddingX || undefined,\n      paddingRight: sendButtonConfig.paddingX || undefined,\n      paddingTop: sendButtonConfig.paddingY || undefined,\n      paddingBottom: sendButtonConfig.paddingY || undefined,\n    },\n  });\n\n  // Both icons are pre-rendered so setMode can swap cheaply.\n  let sendIcon: SVGElement | null = null;\n  let stopIcon: SVGElement | null = null;\n\n  if (useIcon) {\n    const iconSize = parseFloat(buttonSize) || 24;\n    const iconColor = textColor?.trim() || \"currentColor\";\n\n    if (iconName) {\n      sendIcon = renderLucideIcon(iconName, iconSize, iconColor, 2);\n      if (sendIcon) {\n        button.appendChild(sendIcon);\n      } else {\n        button.textContent = iconText;\n      }\n    } else {\n      button.textContent = iconText;\n    }\n\n    stopIcon = renderLucideIcon(stopIconName, iconSize, iconColor, 2);\n  } else {\n    button.textContent = sendLabel;\n  }\n\n  let tooltip: HTMLElement | null = null;\n  if (showTooltip && tooltipText) {\n    tooltip = createElement(\"div\", \"persona-send-button-tooltip\");\n    tooltip.textContent = tooltipText;\n    wrapper.appendChild(tooltip);\n  }\n\n  button.setAttribute(\"aria-label\", tooltipText);\n  wrapper.appendChild(button);\n\n  let currentMode: \"send\" | \"stop\" = \"send\";\n  const setMode = (mode: \"send\" | \"stop\") => {\n    if (mode === currentMode) return;\n    currentMode = mode;\n    const label = mode === \"stop\" ? stopTooltipText : tooltipText;\n    button.setAttribute(\"aria-label\", label);\n    if (tooltip) {\n      tooltip.textContent = label;\n    }\n\n    if (useIcon) {\n      if (sendIcon && stopIcon) {\n        const next = mode === \"stop\" ? stopIcon : sendIcon;\n        // Replace whatever icon is currently mounted: the button only ever\n        // holds the single active icon. We use replaceChildren(next) rather\n        // than replaceChild(next, prev) against a captured `prev` reference:\n        // an external re-render/morph can swap the live icon child out from\n        // under us, detaching our captured node so `prev.parentNode !== button`.\n        // The old appendChild fallback then left BOTH icons mounted, which is\n        // how the send button ended up showing two stacked arrows after the\n        // first send→stop→send cycle.\n        button.replaceChildren(next);\n      }\n    } else {\n      button.textContent = mode === \"stop\" ? stopLabel : sendLabel;\n    }\n  };\n\n  return { button, wrapper, setMode };\n};\n\nexport interface MicButtonParts {\n  button: HTMLButtonElement;\n  wrapper: HTMLElement;\n}\n\n/**\n * Returns null when voice recognition is disabled or the browser doesn't\n * support either the Web Speech API or a Runtype voice provider.\n */\nexport const createMicButton = (config?: AgentWidgetConfig): MicButtonParts | null => {\n  const voiceRecognitionConfig = config?.voiceRecognition ?? {};\n  const voiceRecognitionEnabled = voiceRecognitionConfig.enabled === true;\n  if (!voiceRecognitionEnabled) return null;\n\n  const hasSpeechRecognition =\n    typeof window !== \"undefined\" &&\n    (typeof (window as unknown as { webkitSpeechRecognition?: unknown }).webkitSpeechRecognition !== \"undefined\" ||\n      typeof (window as unknown as { SpeechRecognition?: unknown }).SpeechRecognition !== \"undefined\");\n  const hasRuntypeProvider = voiceRecognitionConfig.provider?.type === \"runtype\";\n  const hasVoiceInput = hasSpeechRecognition || hasRuntypeProvider;\n  if (!hasVoiceInput) return null;\n\n  const buttonSize = config?.sendButton?.size ?? \"40px\";\n  const micIconName = voiceRecognitionConfig.iconName ?? \"mic\";\n  const micIconSize = voiceRecognitionConfig.iconSize ?? buttonSize;\n  const micIconSizeNum = parseFloat(micIconSize) || 24;\n  const micBackgroundColor =\n    voiceRecognitionConfig.backgroundColor ?? config?.sendButton?.backgroundColor;\n  const micIconColor = voiceRecognitionConfig.iconColor ?? config?.sendButton?.textColor;\n\n  const wrapper = createElement(\"div\", \"persona-send-button-wrapper\");\n  const button = createNode(\"button\", {\n    className:\n      \"persona-rounded-button persona-flex persona-items-center persona-justify-center disabled:persona-opacity-50 persona-cursor-pointer\",\n    attrs: {\n      type: \"button\",\n      \"data-persona-composer-mic\": \"\",\n      \"aria-label\": \"Start voice recognition\",\n    },\n    style: {\n      width: micIconSize,\n      height: micIconSize,\n      minWidth: micIconSize,\n      minHeight: micIconSize,\n      fontSize: \"18px\",\n      lineHeight: \"1\",\n      color: micIconColor || \"var(--persona-text, #111827)\",\n      backgroundColor: micBackgroundColor || undefined,\n      borderWidth: voiceRecognitionConfig.borderWidth || undefined,\n      borderStyle: voiceRecognitionConfig.borderWidth ? \"solid\" : undefined,\n      borderColor: voiceRecognitionConfig.borderColor || undefined,\n      paddingLeft: voiceRecognitionConfig.paddingX || undefined,\n      paddingRight: voiceRecognitionConfig.paddingX || undefined,\n      paddingTop: voiceRecognitionConfig.paddingY || undefined,\n      paddingBottom: voiceRecognitionConfig.paddingY || undefined,\n    },\n  });\n\n  const iconColorValue = micIconColor || \"currentColor\";\n  const micIconSvg = renderLucideIcon(micIconName, micIconSizeNum, iconColorValue, 1.5);\n  if (micIconSvg) {\n    button.appendChild(micIconSvg);\n  } else {\n    button.textContent = \"🎤\";\n  }\n\n  wrapper.appendChild(button);\n\n  const micTooltipText = voiceRecognitionConfig.tooltipText ?? \"Start voice recognition\";\n  const showMicTooltip = voiceRecognitionConfig.showTooltip ?? false;\n  if (showMicTooltip && micTooltipText) {\n    const tooltip = createElement(\"div\", \"persona-send-button-tooltip\");\n    tooltip.textContent = micTooltipText;\n    wrapper.appendChild(tooltip);\n  }\n\n  return { button, wrapper };\n};\n\nexport interface AttachmentControlParts {\n  button: HTMLButtonElement;\n  wrapper: HTMLElement;\n  input: HTMLInputElement;\n  previewsContainer: HTMLElement;\n}\n\n/**\n * Returns null when attachments are disabled. Caller decides where to\n * place the previewsContainer (full composer puts it inside the form\n * above the textarea; pill composer floats it above the pill in a\n * separate row).\n */\nexport const createAttachmentControls = (config?: AgentWidgetConfig): AttachmentControlParts | null => {\n  const attachmentsConfig = config?.attachments ?? {};\n  if (attachmentsConfig.enabled !== true) return null;\n\n  const buttonSize = config?.sendButton?.size ?? \"40px\";\n\n  const previewsContainer = createElement(\n    \"div\",\n    \"persona-attachment-previews persona-flex persona-flex-wrap persona-gap-2 persona-mb-2\"\n  );\n  previewsContainer.setAttribute(\"data-persona-composer-attachment-previews\", \"\");\n  previewsContainer.style.display = \"none\";\n\n  const input = createElement(\"input\") as HTMLInputElement;\n  input.type = \"file\";\n  input.setAttribute(\"data-persona-composer-attachment-input\", \"\");\n  input.accept = (attachmentsConfig.allowedTypes ?? ALL_SUPPORTED_MIME_TYPES).join(\",\");\n  input.multiple = (attachmentsConfig.maxFiles ?? 4) > 1;\n  input.style.display = \"none\";\n  input.setAttribute(\"aria-label\", \"Attach files\");\n\n  const attachIconName = attachmentsConfig.buttonIconName ?? \"paperclip\";\n  const attachIconSize = buttonSize;\n  const buttonSizeNum = parseFloat(attachIconSize) || 40;\n  const attachIconSizeNum = Math.round(buttonSizeNum * 0.6);\n\n  const wrapper = createElement(\"div\", \"persona-send-button-wrapper\");\n  const button = createNode(\"button\", {\n    className:\n      \"persona-rounded-button persona-flex persona-items-center persona-justify-center disabled:persona-opacity-50 persona-cursor-pointer persona-attachment-button\",\n    attrs: {\n      type: \"button\",\n      \"data-persona-composer-attachment-button\": \"\",\n      \"aria-label\": attachmentsConfig.buttonTooltipText ?? \"Attach file\",\n    },\n    style: {\n      width: attachIconSize,\n      height: attachIconSize,\n      minWidth: attachIconSize,\n      minHeight: attachIconSize,\n      fontSize: \"18px\",\n      lineHeight: \"1\",\n      backgroundColor: \"transparent\",\n      color: \"var(--persona-primary, #111827)\",\n      border: \"none\",\n      borderRadius: \"6px\",\n      transition: \"background-color 0.15s ease\",\n    },\n  });\n\n  button.addEventListener(\"mouseenter\", () => {\n    button.style.backgroundColor = \"var(--persona-palette-colors-black-alpha-50, rgba(0, 0, 0, 0.05))\";\n  });\n  button.addEventListener(\"mouseleave\", () => {\n    button.style.backgroundColor = \"transparent\";\n  });\n\n  const attachIconSvg = renderLucideIcon(attachIconName, attachIconSizeNum, \"currentColor\", 1.5);\n  if (attachIconSvg) {\n    button.appendChild(attachIconSvg);\n  } else {\n    button.textContent = \"📎\";\n  }\n\n  button.addEventListener(\"click\", (e) => {\n    e.preventDefault();\n    input.click();\n  });\n\n  wrapper.appendChild(button);\n\n  const attachTooltipText = attachmentsConfig.buttonTooltipText ?? \"Attach file\";\n  const tooltip = createElement(\"div\", \"persona-send-button-tooltip\");\n  tooltip.textContent = attachTooltipText;\n  wrapper.appendChild(tooltip);\n\n  return { button, wrapper, input, previewsContainer };\n};\n\nexport const createStatusText = (config?: AgentWidgetConfig): HTMLElement => {\n  const statusConfig = config?.statusIndicator ?? {};\n  const alignClass =\n    statusConfig.align === \"left\"\n      ? \"persona-text-left\"\n      : statusConfig.align === \"center\"\n        ? \"persona-text-center\"\n        : \"persona-text-right\";\n  const statusText = createElement(\n    \"div\",\n    `persona-mt-2 ${alignClass} persona-text-xs persona-text-persona-muted`\n  );\n  statusText.setAttribute(\"data-persona-composer-status\", \"\");\n\n  const isVisible = statusConfig.visible ?? true;\n  statusText.style.display = isVisible ? \"\" : \"none\";\n  const idleLabel = statusConfig.idleText ?? \"Online\";\n  if (statusConfig.idleLink) {\n    const link = createElement(\"a\") as HTMLAnchorElement;\n    link.href = statusConfig.idleLink;\n    link.target = \"_blank\";\n    link.rel = \"noopener noreferrer\";\n    link.textContent = idleLabel;\n    link.style.color = \"inherit\";\n    link.style.textDecoration = \"none\";\n    statusText.appendChild(link);\n  } else {\n    statusText.textContent = idleLabel;\n  }\n\n  return statusText;\n};\n\nexport const createSuggestionsRow = (): HTMLElement =>\n  createNode(\"div\", {\n    className: \"persona-mb-3 persona-flex persona-flex-wrap persona-gap-2\",\n    attrs: { \"data-persona-composer-suggestions\": \"\" },\n  });\n","import { createElement, createNode } from \"../utils/dom\";\nimport { AgentWidgetConfig, ContentPart } from \"../types\";\nimport {\n  createAttachmentControls,\n  createComposerTextarea,\n  createMicButton,\n  createSendButton,\n  createStatusText,\n  createSuggestionsRow,\n} from \"./composer-parts\";\n\nexport interface ComposerElements {\n  footer: HTMLElement;\n  suggestions: HTMLElement;\n  composerForm: HTMLFormElement;\n  textarea: HTMLTextAreaElement;\n  sendButton: HTMLButtonElement;\n  sendButtonWrapper: HTMLElement;\n  micButton: HTMLButtonElement | null;\n  micButtonWrapper: HTMLElement | null;\n  statusText: HTMLElement;\n  attachmentButton: HTMLButtonElement | null;\n  attachmentButtonWrapper: HTMLElement | null;\n  attachmentInput: HTMLInputElement | null;\n  attachmentPreviewsContainer: HTMLElement | null;\n  actionsRow: HTMLElement;\n  leftActions: HTMLElement;\n  rightActions: HTMLElement;\n  /**\n   * Swap the send button between its idle (\"send\") appearance and its\n   * streaming (\"stop\") appearance. In icon mode this swaps the SVG; in text\n   * mode it swaps the button label. Tooltip text is updated when a tooltip\n   * element is present.\n   */\n  setSendButtonMode: (mode: \"send\" | \"stop\") => void;\n}\n\nexport interface PendingAttachment {\n  id: string;\n  file: File;\n  previewUrl: string;\n  contentPart: ContentPart;\n}\n\nexport interface ComposerBuildContext {\n  config?: AgentWidgetConfig;\n  onSubmit?: (text: string) => void;\n  disabled?: boolean;\n}\n\n/**\n * Build the full footer + composer form (column-stacked card layout) for\n * the floating, docked, and inline-embed launcher modes. The pill variant\n * for `mountMode: \"composer-bar\"` lives in `pill-composer-builder.ts` and\n * shares the same low-level part factories from `composer-parts.ts`.\n */\nexport const buildComposer = (context: ComposerBuildContext): ComposerElements => {\n  const { config } = context;\n\n  const footer = createNode(\"div\", {\n    className:\n      \"persona-widget-footer persona-border-t-persona-divider persona-bg-persona-surface persona-px-6 persona-py-4\",\n    attrs: { \"data-persona-theme-zone\": \"composer\" },\n  });\n\n  const suggestions = createSuggestionsRow();\n\n  const composerForm = createNode(\"form\", {\n    className:\n      \"persona-widget-composer persona-flex persona-flex-col persona-gap-2 persona-rounded-2xl persona-border persona-border-gray-200 persona-bg-persona-input-background persona-px-4 persona-py-3\",\n    attrs: { \"data-persona-composer-form\": \"\" },\n    style: { outline: \"none\" },\n  });\n\n  const { textarea, attachAutoResize } = createComposerTextarea(config);\n  attachAutoResize();\n\n  const send = createSendButton(config);\n  const mic = createMicButton(config);\n  const attachment = createAttachmentControls(config);\n  const statusText = createStatusText(config);\n\n  // Layout (column):\n  //   row 1: attachment previews (above textarea, smaller)\n  //   row 2: textarea (full width)\n  //   row 3: actions (paperclip left, mic + send right)\n  if (attachment) {\n    attachment.previewsContainer.style.gap = \"8px\";\n    composerForm.append(attachment.previewsContainer, attachment.input);\n  }\n  composerForm.append(textarea);\n\n  // The bare class names (persona-widget-composer__actions / __left-actions /\n  // __right-actions) are stable CSS hooks. The pill composer reuses\n  // __left-actions / __right-actions as semantic markers in its grid.\n  const actionsRow = createNode(\"div\", {\n    className:\n      \"persona-widget-composer__actions persona-flex persona-items-center persona-justify-between persona-w-full\",\n    attrs: { \"data-persona-composer-actions\": \"\" },\n  });\n  const leftActions = createElement(\n    \"div\",\n    \"persona-widget-composer__left-actions persona-flex persona-items-center persona-gap-2\"\n  );\n  const rightActions = createElement(\n    \"div\",\n    \"persona-widget-composer__right-actions persona-flex persona-items-center persona-gap-1\"\n  );\n  if (attachment) leftActions.append(attachment.wrapper);\n  if (mic) rightActions.append(mic.wrapper);\n  rightActions.append(send.wrapper);\n  actionsRow.append(leftActions, rightActions);\n  composerForm.append(actionsRow);\n\n  // Click anywhere on the composer (other than the action buttons) → focus\n  // textarea so the click target feels like the whole input bar.\n  composerForm.addEventListener(\"click\", (e) => {\n    if (\n      e.target !== send.button &&\n      e.target !== send.wrapper &&\n      e.target !== mic?.button &&\n      e.target !== mic?.wrapper &&\n      e.target !== attachment?.button &&\n      e.target !== attachment?.wrapper\n    ) {\n      textarea.focus();\n    }\n  });\n\n  footer.append(suggestions, composerForm, statusText);\n\n  return {\n    footer,\n    suggestions,\n    composerForm,\n    textarea,\n    sendButton: send.button,\n    sendButtonWrapper: send.wrapper,\n    micButton: mic?.button ?? null,\n    micButtonWrapper: mic?.wrapper ?? null,\n    statusText,\n    attachmentButton: attachment?.button ?? null,\n    attachmentButtonWrapper: attachment?.wrapper ?? null,\n    attachmentInput: attachment?.input ?? null,\n    attachmentPreviewsContainer: attachment?.previewsContainer ?? null,\n    actionsRow,\n    leftActions,\n    rightActions,\n    setSendButtonMode: send.setMode,\n  };\n};\n","import { createElement, createNode } from \"../utils/dom\";\nimport { renderLucideIcon } from \"../utils/icons\";\nimport { ComposerBuildContext, ComposerElements } from \"./composer-builder\";\nimport {\n  createAttachmentControls,\n  createComposerTextarea,\n  createMicButton,\n  createSendButton,\n  createStatusText,\n  createSuggestionsRow,\n} from \"./composer-parts\";\n\nexport interface PillPeekBanner {\n  /**\n   * The peek button itself: a chrome-less row that floats above the pill,\n   * showing a chat-bubble icon, a trailing-100-char preview of the most\n   * recent assistant message, and a chevron-up. Rendered hidden by default\n   * (opacity 0, pointer-events none); ui.ts toggles\n   * `.persona-pill-peek--visible` based on streaming/hover/open state.\n   */\n  root: HTMLButtonElement;\n  /** Wrapper around the trailing message preview text. */\n  textNode: HTMLElement;\n}\n\n/**\n * Build the peek banner for `launcher.mountMode: \"composer-bar\"`. The peek\n * is the user's path back into the expanded chat from the collapsed pill: * it fades in during streaming OR on composer hover, and clicking it opens\n * the panel. ui.ts owns visibility + content updates via\n * `syncComposerBarPeek`; this factory just produces the inert DOM shell.\n *\n * Placed in the panel between `container` and `footer` so it visually sits\n * just above the pill in the collapsed-state UI.\n */\nexport const buildPillPeekBanner = (): PillPeekBanner => {\n  const root = createNode(\"button\", {\n    className: \"persona-pill-peek\",\n    attrs: {\n      type: \"button\",\n      \"data-persona-pill-peek\": \"\",\n      \"aria-label\": \"Show conversation\",\n      tabindex: \"-1\",\n    },\n  });\n\n  const iconHolder = createElement(\"span\", \"persona-pill-peek__icon\");\n  const messageIcon = renderLucideIcon(\"message-square\", 16, \"currentColor\", 1.5);\n  if (messageIcon) {\n    iconHolder.appendChild(messageIcon);\n  }\n\n  const textNode = createElement(\"span\", \"persona-pill-peek__text\");\n\n  const caret = createElement(\"span\", \"persona-pill-peek__caret\");\n  const caretIcon = renderLucideIcon(\"chevron-up\", 16, \"currentColor\", 1.5);\n  if (caretIcon) {\n    caret.appendChild(caretIcon);\n  }\n\n  root.append(iconHolder, textNode, caret);\n  return { root, textNode };\n};\n\n/**\n * Single-row pill composer for `launcher.mountMode: \"composer-bar\"`.\n *\n * Same control factories as `buildComposer`: the only difference is the\n * layout shell + className. The form ships with `persona-pill-composer`\n * (no `persona-flex-col` / `persona-rounded-2xl` baggage), so the CSS\n * layout rules apply at normal specificity without `!important` fights.\n *\n * Returns the same `ComposerElements` shape as `buildComposer` so panel.ts\n * and ui.ts plumbing is unconditional past the choice of builder.\n *\n * Suggestions row + status text are built (so plugin code that mutates\n * them keeps working and `bindComposerRefsFromFooter` finds them) but are\n * `display: none` by default: pill UX is just textarea + 3 buttons.\n *\n * Attachment previews float ABOVE the pill in their own row when\n * AttachmentManager toggles the previews container's `display` property\n * as items are added/removed.\n */\nexport const buildPillComposer = (context: ComposerBuildContext): ComposerElements => {\n  const { config } = context;\n\n  const footer = createNode(\"div\", {\n    className: \"persona-widget-footer persona-widget-footer--pill\",\n    attrs: { \"data-persona-theme-zone\": \"composer\" },\n  });\n\n  const suggestions = createSuggestionsRow();\n  suggestions.style.display = \"none\";\n  const statusText = createStatusText(config);\n  statusText.style.display = \"none\";\n\n  const { textarea, attachAutoResize } = createComposerTextarea(config);\n  // Pill textarea: starts single-line, allowed to grow up to ~5 lines so\n  // expanded mode still supports multi-line composition. attachAutoResize\n  // reads max-height at event time, so this override flows through.\n  textarea.style.maxHeight = \"100px\";\n  attachAutoResize();\n\n  const send = createSendButton(config);\n  const mic = createMicButton(config);\n  const attachment = createAttachmentControls(config);\n\n  if (attachment) {\n    attachment.previewsContainer.classList.add(\"persona-pill-composer__previews\");\n  }\n\n  // Pill form: NO `persona-flex-col`. Only the marker classes that the rest\n  // of the codebase queries by name.\n  const composerForm = createNode(\"form\", {\n    className: \"persona-widget-composer persona-pill-composer\",\n    attrs: { \"data-persona-composer-form\": \"\" },\n    style: { outline: \"none\" },\n  });\n\n  // Three columns of the grid: [paperclip?] · textarea · mic + send.\n  // The empty leftActions wrapper still ships when attachments are off so\n  // the grid has a consistent first cell (auto width → collapses to 0).\n  const leftActions = createElement(\n    \"div\",\n    \"persona-widget-composer__left-actions persona-pill-composer__left\"\n  );\n  if (attachment) leftActions.append(attachment.wrapper);\n\n  const rightActions = createElement(\n    \"div\",\n    \"persona-widget-composer__right-actions persona-pill-composer__right\"\n  );\n  if (mic) rightActions.append(mic.wrapper);\n  rightActions.append(send.wrapper);\n\n  composerForm.addEventListener(\"click\", (e) => {\n    if (\n      e.target !== send.button &&\n      e.target !== send.wrapper &&\n      e.target !== mic?.button &&\n      e.target !== mic?.wrapper &&\n      e.target !== attachment?.button &&\n      e.target !== attachment?.wrapper\n    ) {\n      textarea.focus();\n    }\n  });\n\n  if (attachment) composerForm.append(attachment.input);\n  composerForm.append(leftActions, textarea, rightActions);\n\n  // Footer assembly:\n  //   [previews row, hidden until attachments exist]\n  //   [pill form]\n  //   [hidden suggestions]\n  //   [hidden status]\n  if (attachment) footer.append(attachment.previewsContainer);\n  footer.append(composerForm, suggestions, statusText);\n\n  // The pill flattens left/right into the form's grid; there's no separate\n  // wrapper. Surface the form itself as `actionsRow` to satisfy the\n  // ComposerElements contract: downstream code only treats it as an\n  // opaque ref. The pill form intentionally carries no\n  // `data-persona-composer-actions` marker, so `bindComposerRefsFromFooter`\n  // finds no actions row in pill mode; that lookup writes to `_actionsRow`\n  // (the underscore prefix marks it as soft-optional).\n  const actionsRow = composerForm;\n\n  return {\n    footer,\n    suggestions,\n    composerForm,\n    textarea,\n    sendButton: send.button,\n    sendButtonWrapper: send.wrapper,\n    micButton: mic?.button ?? null,\n    micButtonWrapper: mic?.wrapper ?? null,\n    statusText,\n    attachmentButton: attachment?.button ?? null,\n    attachmentButtonWrapper: attachment?.wrapper ?? null,\n    attachmentInput: attachment?.input ?? null,\n    attachmentPreviewsContainer: attachment?.previewsContainer ?? null,\n    actionsRow,\n    leftActions,\n    rightActions,\n    setSendButtonMode: send.setMode,\n  };\n};\n","import { createElement, createNode } from \"../utils/dom\";\nimport { DEFAULT_FLOATING_LAUNCHER_WIDTH } from \"../defaults\";\nimport { AgentWidgetConfig } from \"../types\";\nimport { positionMap } from \"../utils/positioning\";\nimport { isComposerBarMountMode, isDockedMountMode } from \"../utils/dock\";\nimport { DEFAULT_OVERLAY_Z_INDEX } from \"../utils/constants\";\nimport { buildHeader, attachHeaderToContainer, HeaderElements } from \"./header-builder\";\nimport { buildHeaderWithLayout } from \"./header-layouts\";\nimport { createCloseButton, createClearChatButton } from \"./header-parts\";\nimport { buildComposer, ComposerElements } from \"./composer-builder\";\nimport { buildPillComposer, buildPillPeekBanner } from \"./pill-composer-builder\";\n\nexport interface PanelWrapper {\n  wrapper: HTMLElement;\n  panel: HTMLElement;\n  /**\n   * Composer-bar mode only: viewport-fixed sibling of `wrapper` that owns\n   * the persistent pill (`footer`) and peek banner. Lives outside the\n   * wrapper so it never inherits the wrapper's geometry transitions:   * critical for modal mode where the wrapper is `transform: translate(-50%, -50%)`\n   * (a transformed ancestor establishes a containing block for `position: fixed`\n   * descendants, which would trap the pill inside the wrapper otherwise).\n   */\n  pillRoot?: HTMLElement;\n}\n\nexport const createWrapper = (config?: AgentWidgetConfig): PanelWrapper => {\n  const launcherEnabled = config?.launcher?.enabled ?? true;\n  const dockedMode = isDockedMountMode(config);\n  const composerBarMode = isComposerBarMountMode(config);\n\n  if (composerBarMode) {\n    const cb = config?.launcher?.composerBar ?? {};\n    // Geometry (left/transform/bottom/top/width/max-width) is intentionally\n    // NOT set here: it's owned entirely by `applyComposerBarGeometry()` in\n    // ui.ts so that collapsed → expanded transitions can clear the previous\n    // state's inline styles cleanly. Setting geometry here would persist\n    // across state changes and override the per-state values, which\n    // previously caused expanded panels to render at collapsed dimensions.\n    const wrapper = createElement(\n      \"div\",\n      \"persona-widget-wrapper persona-fixed persona-transition\"\n    );\n    wrapper.setAttribute(\"data-persona-composer-bar\", \"\");\n    wrapper.dataset.state = \"collapsed\";\n    wrapper.dataset.expandedSize = cb.expandedSize ?? \"anchored\";\n    wrapper.style.zIndex = String(\n      config?.launcher?.zIndex ?? DEFAULT_OVERLAY_Z_INDEX\n    );\n\n    const panel = createElement(\n      \"div\",\n      \"persona-widget-panel persona-relative persona-flex persona-flex-1 persona-min-h-0 persona-flex-col\"\n    );\n    panel.style.width = \"100%\";\n    wrapper.appendChild(panel);\n\n    // Pill lives in a separate viewport-fixed sibling: see PanelWrapper\n    // docs above. ui.ts appends `peekBanner` and `footer` here, then\n    // appends pillRoot to `mount` immediately after the wrapper.\n    const pillRoot = createElement(\"div\", \"persona-widget-pill-root\");\n    pillRoot.setAttribute(\"data-persona-composer-bar\", \"\");\n    pillRoot.dataset.state = \"collapsed\";\n    pillRoot.dataset.expandedSize = cb.expandedSize ?? \"anchored\";\n    pillRoot.style.zIndex = String(\n      config?.launcher?.zIndex ?? DEFAULT_OVERLAY_Z_INDEX\n    );\n\n    return { wrapper, panel, pillRoot };\n  }\n\n  if (dockedMode) {\n    const wrapper = createElement(\n      \"div\",\n      \"persona-relative persona-h-full persona-w-full persona-flex persona-flex-1 persona-min-h-0 persona-flex-col\"\n    );\n    const panel = createElement(\n      \"div\",\n      \"persona-relative persona-h-full persona-w-full persona-flex persona-flex-1 persona-min-h-0 persona-flex-col\"\n    );\n\n    wrapper.appendChild(panel);\n    return { wrapper, panel };\n  }\n\n  if (!launcherEnabled) {\n    // For inline embed mode, use flex layout to ensure the widget fills its container\n    // and only the chat messages area scrolls\n    const wrapper = createElement(\n      \"div\",\n      \"persona-relative persona-h-full persona-flex persona-flex-col persona-flex-1 persona-min-h-0\"\n    );\n    const panel = createElement(\n      \"div\",\n      \"persona-relative persona-flex-1 persona-flex persona-flex-col persona-min-h-0\"\n    );\n    \n    // Apply width from config, defaulting to 100% for inline embed mode\n    const inlineWidth = config?.launcher?.width ?? \"100%\";\n    wrapper.style.width = inlineWidth;\n    panel.style.width = \"100%\";\n    \n    wrapper.appendChild(panel);\n    return { wrapper, panel };\n  }\n\n  const launcher = config?.launcher ?? {};\n  const position =\n    launcher.position && positionMap[launcher.position]\n      ? positionMap[launcher.position]\n      : positionMap[\"bottom-right\"];\n\n  const wrapper = createElement(\n    \"div\",\n    `persona-widget-wrapper persona-fixed ${position} persona-transition`\n  );\n  wrapper.style.zIndex = String(config?.launcher?.zIndex ?? DEFAULT_OVERLAY_Z_INDEX);\n\n  const panel = createElement(\n    \"div\",\n    \"persona-widget-panel persona-relative persona-min-h-[320px]\"\n  );\n  const launcherWidth = config?.launcher?.width ?? config?.launcherWidth;\n  const width = launcherWidth ?? DEFAULT_FLOATING_LAUNCHER_WIDTH;\n  panel.style.width = width;\n  panel.style.maxWidth = width;\n\n  wrapper.appendChild(panel);\n  return { wrapper, panel };\n};\n\nexport interface PanelElements {\n  container: HTMLElement;\n  body: HTMLElement;\n  messagesWrapper: HTMLElement;\n  /**\n   * Absolute-positioned slot above the composer footer. Interactive sheets\n   * (e.g. the answer-pill sheet for the ask_user_question tool) mount here\n   * so they slide in without reflowing the chat transcript.\n   */\n  composerOverlay: HTMLElement;\n  suggestions: HTMLElement;\n  textarea: HTMLTextAreaElement;\n  sendButton: HTMLButtonElement;\n  sendButtonWrapper: HTMLElement;\n  micButton: HTMLButtonElement | null;\n  micButtonWrapper: HTMLElement | null;\n  composerForm: HTMLFormElement;\n  statusText: HTMLElement;\n  introTitle: HTMLElement;\n  introSubtitle: HTMLElement;\n  closeButton: HTMLButtonElement;\n  closeButtonWrapper: HTMLElement;\n  clearChatButton: HTMLButtonElement | null;\n  clearChatButtonWrapper: HTMLElement | null;\n  iconHolder: HTMLElement;\n  headerTitle: HTMLElement;\n  headerSubtitle: HTMLElement;\n  // Exposed for potential header replacement\n  header: HTMLElement;\n  footer: HTMLElement;\n  // Attachment elements\n  attachmentButton: HTMLButtonElement | null;\n  attachmentButtonWrapper: HTMLElement | null;\n  attachmentInput: HTMLInputElement | null;\n  attachmentPreviewsContainer: HTMLElement | null;\n  // Actions row layout elements\n  actionsRow: HTMLElement;\n  leftActions: HTMLElement;\n  rightActions: HTMLElement;\n  /** Swap the send button between its send and stop appearances. */\n  setSendButtonMode: (mode: \"send\" | \"stop\") => void;\n  /**\n   * Composer-bar peek banner: the chrome-less row above the pill that\n   * shows a trailing preview of the most recent assistant message.\n   * Undefined for non-composer-bar modes.\n   */\n  peekBanner?: HTMLButtonElement;\n  peekTextNode?: HTMLElement;\n}\n\n/**\n * Composer-bar panel: minimal close-only header (× in the top-right of the\n * chat chrome). The pill (`footer`) and `peekBanner` are NOT children of\n * the panel: caller (`ui.ts`) appends them to the `pillRoot` returned by\n * `createWrapper`, which is a viewport-fixed sibling of the wrapper.\n * This decouples the pill from the wrapper's geometry transitions (so the\n * pill stays anchored at the viewport bottom regardless of expanded mode).\n */\nconst buildComposerBarPanel = (\n  config: AgentWidgetConfig | undefined,\n  showClose: boolean,\n): PanelElements => {\n  // Container = the chat panel chrome (border + bg + radius + shadow applied\n  // inline by `applyFullHeightStyles` in ui.ts). `position: relative` anchors\n  // the absolute close button + composerOverlay.\n  const container = createElement(\n    \"div\",\n    \"persona-widget-container persona-relative persona-flex persona-flex-1 persona-min-h-0 persona-flex-col persona-text-persona-primary\"\n  );\n  container.setAttribute(\"data-persona-theme-zone\", \"container\");\n\n  // Minimal header: just an absolutely-positioned close button.\n  // The wrapper uses inline styles (top/right/z-index values) because the\n  // widget's hand-authored CSS doesn't ship every Tailwind utility.\n  // Composer-bar's defaults are roughly half the floating-launcher's\n  // (button 16px, icon 14px) to match the minimal aesthetic; the user can\n  // still override via `launcher.closeButtonSize`.\n  const { button: closeButton, wrapper: closeButtonWrapper } = createCloseButton(\n    config,\n    {\n      showClose,\n      wrapperClassName: \"persona-composer-bar-close\",\n      buttonSize: \"16px\",\n      iconSize: \"14px\",\n    }\n  );\n  closeButtonWrapper.style.position = \"absolute\";\n  closeButtonWrapper.style.top = \"8px\";\n  closeButtonWrapper.style.right = \"8px\";\n  closeButtonWrapper.style.zIndex = \"10\";\n\n  // Clear / \"start over\" button: sits immediately to the left of the close\n  // button in the panel chrome's top-right corner. Same minimal sizing as\n  // the close icon (16px button, 14px icon) so the two read as a paired\n  // action group rather than a header strip. Wired by `setupClearChatButton()`\n  // in ui.ts via the `clearChatButton` field on PanelElements.\n  //\n  // Right offset = close button right (8px) + close button width (16px) +\n  // 8px inter-button gap = 32px.\n  const clearChatEnabled = config?.launcher?.clearChat?.enabled ?? true;\n  let clearChatButton: HTMLButtonElement | null = null;\n  let clearChatButtonWrapper: HTMLElement | null = null;\n  if (clearChatEnabled) {\n    const parts = createClearChatButton(config, {\n      wrapperClassName: \"persona-composer-bar-clear-chat\",\n      buttonSize: \"16px\",\n      iconSize: \"14px\",\n    });\n    clearChatButton = parts.button;\n    clearChatButtonWrapper = parts.wrapper;\n    clearChatButtonWrapper.style.position = \"absolute\";\n    clearChatButtonWrapper.style.top = \"8px\";\n    clearChatButtonWrapper.style.right = \"32px\";\n    clearChatButtonWrapper.style.zIndex = \"10\";\n  }\n  // Placeholder header element so PanelElements.header exists (some downstream\n  // code reads it). It carries `data-persona-widget-header` for plugin /\n  // selector parity but renders nothing visible: the close button is the only\n  // header chrome in composer-bar mode.\n  const headerPlaceholder = createNode(\"span\", {\n    className: \"persona-widget-header\",\n    attrs: { \"data-persona-theme-zone\": \"header\" },\n    style: { display: \"none\" },\n  });\n\n  // Body: extra top padding (set inline so the hand-authored widget.css\n  // doesn't need a `pt-12` utility) so the absolute close button doesn't\n  // overlap the welcome card / first message.\n  const body = createNode(\"div\", {\n    className:\n      \"persona-widget-body persona-flex persona-flex-1 persona-min-h-0 persona-flex-col persona-gap-6 persona-overflow-y-auto persona-bg-persona-container persona-px-6 persona-py-6\",\n    attrs: { id: \"persona-scroll-container\", \"data-persona-theme-zone\": \"messages\" },\n    style: { paddingTop: \"48px\" },\n  });\n  // Reserve the scrollbar gutter so the transcript doesn't shift horizontally\n  // when streaming content first overflows and the scrollbar appears.\n  body.style.setProperty(\"scrollbar-gutter\", \"stable\");\n\n  const introTitle = createNode(\"h2\", {\n    className: \"persona-text-lg persona-font-semibold persona-text-persona-primary\",\n    text: config?.copy?.welcomeTitle ?? \"Hello 👋\",\n  });\n  const introSubtitle = createNode(\"p\", {\n    className: \"persona-mt-2 persona-text-sm persona-text-persona-muted\",\n    text:\n      config?.copy?.welcomeSubtitle ??\n      \"Ask anything about your account or products.\",\n  });\n  const introCard = createNode(\n    \"div\",\n    {\n      className: \"persona-rounded-2xl persona-p-6\",\n      attrs: { \"data-persona-intro-card\": \"\" },\n      style: {\n        background:\n          \"var(--persona-intro-card-bg, var(--persona-surface, #ffffff))\",\n        boxShadow:\n          \"var(--persona-intro-card-shadow, 0 5px 15px rgba(15, 23, 42, 0.08))\",\n      },\n    },\n    introTitle,\n    introSubtitle\n  );\n\n  const messagesWrapper = createElement(\n    \"div\",\n    \"persona-flex persona-flex-col persona-gap-3\"\n  );\n  const contentMaxWidth = config?.layout?.contentMaxWidth;\n  if (contentMaxWidth) {\n    messagesWrapper.style.maxWidth = contentMaxWidth;\n    messagesWrapper.style.marginLeft = \"auto\";\n    messagesWrapper.style.marginRight = \"auto\";\n    messagesWrapper.style.width = \"100%\";\n  }\n\n  const showWelcomeCard = config?.copy?.showWelcomeCard !== false;\n  if (!showWelcomeCard) {\n    introCard.style.display = \"none\";\n    body.classList.remove(\"persona-gap-6\");\n    body.classList.add(\"persona-gap-3\");\n  }\n  body.append(introCard, messagesWrapper);\n\n  // Composer overlay (interactive sheets like ask_user_question slide up here).\n  // Anchored to the bottom of the container (which is `position: relative`),\n  // so sheets render at the bottom of the chat chrome: just above the gap +\n  // pill that sit below the container.\n  const composerOverlay = createNode(\"div\", {\n    className: \"persona-composer-overlay persona-pointer-events-none\",\n    attrs: { \"data-persona-composer-overlay\": \"\" },\n    style: { position: \"absolute\", left: \"0\", right: \"0\", bottom: \"0\", zIndex: \"20\" },\n  });\n\n  // Pill composer: caller appends as a sibling of container in the panel.\n  const composerElements: ComposerElements = buildPillComposer({ config });\n\n  // Peek banner: caller inserts as a sibling of container/footer between\n  // them in the panel. Hidden by default; ui.ts toggles visibility.\n  const { root: peekBanner, textNode: peekTextNode } = buildPillPeekBanner();\n\n  // Container = [close button (absolute), placeholder header, body, overlay].\n  // Footer (pill) is intentionally NOT appended here.\n  container.append(headerPlaceholder, closeButtonWrapper, body, composerOverlay);\n  if (clearChatButtonWrapper) {\n    container.appendChild(clearChatButtonWrapper);\n  }\n\n  return {\n    container,\n    body,\n    messagesWrapper,\n    composerOverlay,\n    suggestions: composerElements.suggestions,\n    textarea: composerElements.textarea,\n    sendButton: composerElements.sendButton,\n    sendButtonWrapper: composerElements.sendButtonWrapper,\n    micButton: composerElements.micButton,\n    micButtonWrapper: composerElements.micButtonWrapper,\n    composerForm: composerElements.composerForm,\n    statusText: composerElements.statusText,\n    introTitle,\n    introSubtitle,\n    closeButton,\n    closeButtonWrapper,\n    clearChatButton,\n    clearChatButtonWrapper,\n    iconHolder: createElement(\"span\"),\n    headerTitle: createElement(\"span\"),\n    headerSubtitle: createElement(\"span\"),\n    header: headerPlaceholder,\n    footer: composerElements.footer,\n    attachmentButton: composerElements.attachmentButton,\n    attachmentButtonWrapper: composerElements.attachmentButtonWrapper,\n    attachmentInput: composerElements.attachmentInput,\n    attachmentPreviewsContainer: composerElements.attachmentPreviewsContainer,\n    actionsRow: composerElements.actionsRow,\n    leftActions: composerElements.leftActions,\n    rightActions: composerElements.rightActions,\n    setSendButtonMode: composerElements.setSendButtonMode,\n    peekBanner,\n    peekTextNode,\n  };\n};\n\nexport const buildPanel = (config?: AgentWidgetConfig, showClose = true): PanelElements => {\n  // Composer-bar mode renders a purpose-built panel: minimal close-only\n  // header (small × in the top-right of the chat panel chrome), and the\n  // pill (footer) is a SIBLING of the container so it stays visible/usable\n  // when the chat is expanded above it.\n  if (isComposerBarMountMode(config)) {\n    return buildComposerBarPanel(config, showClose);\n  }\n\n  const container = createNode(\"div\", {\n    className:\n      \"persona-widget-container persona-flex persona-h-full persona-w-full persona-flex-1 persona-min-h-0 persona-flex-col persona-text-persona-primary persona-bg-persona-surface persona-rounded-2xl persona-overflow-hidden persona-border persona-border-persona-border\",\n    attrs: { \"data-persona-theme-zone\": \"container\" },\n  });\n\n  // Build header using layout config if available, otherwise use standard builder\n  const headerLayoutConfig = config?.layout?.header;\n  const showHeader = config?.layout?.showHeader !== false; // default to true\n  const headerElements: HeaderElements = headerLayoutConfig\n    ? buildHeaderWithLayout(config!, headerLayoutConfig, { showClose })\n    : buildHeader({ config, showClose });\n\n  // Build body with intro card and messages wrapper\n  const body = createNode(\"div\", {\n    className:\n      \"persona-widget-body persona-flex persona-flex-1 persona-min-h-0 persona-flex-col persona-gap-6 persona-overflow-y-auto persona-bg-persona-container persona-px-6 persona-py-6\",\n    attrs: { id: \"persona-scroll-container\", \"data-persona-theme-zone\": \"messages\" },\n  });\n  // Reserve the scrollbar gutter so the transcript doesn't shift horizontally\n  // when streaming content first overflows and the scrollbar appears.\n  body.style.setProperty(\"scrollbar-gutter\", \"stable\");\n\n  const introTitle = createNode(\"h2\", {\n    className: \"persona-text-lg persona-font-semibold persona-text-persona-primary\",\n    text: config?.copy?.welcomeTitle ?? \"Hello 👋\",\n  });\n  const introSubtitle = createNode(\"p\", {\n    className: \"persona-mt-2 persona-text-sm persona-text-persona-muted\",\n    text:\n      config?.copy?.welcomeSubtitle ??\n      \"Ask anything about your account or products.\",\n  });\n  // Background and box-shadow flow through the themable `components.introCard`\n  // tokens (--persona-intro-card-bg / --persona-intro-card-shadow), each\n  // falling back to its legacy value (`--persona-surface` / `persona-shadow-sm`)\n  // when no token is set. Docked mode keeps a flat shadow by default.\n  const introCard = createNode(\n    \"div\",\n    {\n      className: \"persona-rounded-2xl persona-p-6\",\n      attrs: { \"data-persona-intro-card\": \"\" },\n      style: {\n        background:\n          \"var(--persona-intro-card-bg, var(--persona-surface, #ffffff))\",\n        boxShadow: isDockedMountMode(config)\n          ? \"none\"\n          : \"var(--persona-intro-card-shadow, 0 5px 15px rgba(15, 23, 42, 0.08))\",\n      },\n    },\n    introTitle,\n    introSubtitle\n  );\n\n  const messagesWrapper = createElement(\n    \"div\",\n    \"persona-flex persona-flex-col persona-gap-3\"\n  );\n\n  const contentMaxWidth = config?.layout?.contentMaxWidth;\n  if (contentMaxWidth) {\n    messagesWrapper.style.maxWidth = contentMaxWidth;\n    messagesWrapper.style.marginLeft = \"auto\";\n    messagesWrapper.style.marginRight = \"auto\";\n    messagesWrapper.style.width = \"100%\";\n  }\n\n  const showWelcomeCard = config?.copy?.showWelcomeCard !== false;\n  if (!showWelcomeCard) {\n    introCard.style.display = \"none\";\n    body.classList.remove(\"persona-gap-6\");\n    body.classList.add(\"persona-gap-3\");\n  }\n  body.append(introCard, messagesWrapper);\n\n  // composer-bar mode early-returned above with its own pill builder; this\n  // path always uses the standard column-stacked composer card.\n  const composerElements: ComposerElements = buildComposer({ config });\n  const showFooter = config?.layout?.showFooter !== false; // default to true\n\n  // Assemble container with header, body, and footer\n  if (showHeader) {\n    attachHeaderToContainer(container, headerElements, config);\n  } else {\n    // Hide header completely\n    headerElements.header.style.display = 'none';\n    attachHeaderToContainer(container, headerElements, config);\n  }\n  \n  container.append(body);\n  \n  // Composer overlay slot: sits between body and footer, absolutely positioned\n  // above the composer so sheets (e.g. the ask_user_question answer-pill sheet)\n  // can slide up without reflowing the chat transcript above. Uses inline\n  // styles for left/right/bottom because widget.css is hand-authored and\n  // doesn't ship `.persona-left-0` / `.persona-right-0` rules: without\n  // them the overlay shrink-wraps to content and collapses the sheet width.\n  // zIndex 20 sits above .persona-scroll-to-bottom-indicator (z-index 10,\n  // sibling in the container) so suggestion chips and the ask-user-question\n  // sheet are not covered by the \"jump to latest\" button.\n  const composerOverlay = createNode(\"div\", {\n    className: \"persona-composer-overlay persona-pointer-events-none\",\n    attrs: { \"data-persona-composer-overlay\": \"\" },\n    style: { position: \"absolute\", left: \"0\", right: \"0\", bottom: \"0\", zIndex: \"20\" },\n  });\n\n  if (showFooter) {\n    container.append(composerElements.footer);\n  } else {\n    // Hide footer completely\n    composerElements.footer.style.display = 'none';\n    container.append(composerElements.footer);\n  }\n\n  // Append overlay last so it stacks above the footer / body content.\n  container.append(composerOverlay);\n\n  return {\n    container,\n    body,\n    messagesWrapper,\n    composerOverlay,\n    suggestions: composerElements.suggestions,\n    textarea: composerElements.textarea,\n    sendButton: composerElements.sendButton,\n    sendButtonWrapper: composerElements.sendButtonWrapper,\n    micButton: composerElements.micButton,\n    micButtonWrapper: composerElements.micButtonWrapper,\n    composerForm: composerElements.composerForm,\n    statusText: composerElements.statusText,\n    introTitle,\n    introSubtitle,\n    closeButton: headerElements.closeButton,\n    closeButtonWrapper: headerElements.closeButtonWrapper,\n    clearChatButton: headerElements.clearChatButton,\n    clearChatButtonWrapper: headerElements.clearChatButtonWrapper,\n    iconHolder: headerElements.iconHolder,\n    headerTitle: headerElements.headerTitle,\n    headerSubtitle: headerElements.headerSubtitle,\n    header: headerElements.header,\n    footer: composerElements.footer,\n    // Attachment elements\n    attachmentButton: composerElements.attachmentButton,\n    attachmentButtonWrapper: composerElements.attachmentButtonWrapper,\n    attachmentInput: composerElements.attachmentInput,\n    attachmentPreviewsContainer: composerElements.attachmentPreviewsContainer,\n    // Actions row layout elements\n    actionsRow: composerElements.actionsRow,\n    leftActions: composerElements.leftActions,\n    rightActions: composerElements.rightActions,\n    setSendButtonMode: composerElements.setSendButtonMode\n  };\n};\n\n// Re-export builder types and functions for plugin use\nexport { buildHeader, buildComposer, attachHeaderToContainer };\nexport type { HeaderElements, HeaderBuildContext } from \"./header-builder\";\nexport type { ComposerElements, ComposerBuildContext } from \"./composer-builder\";\n","import { createElement } from \"../utils/dom\";\nimport { AgentWidgetConfig } from \"../types\";\nimport { positionMap } from \"../utils/positioning\";\nimport { isDockedMountMode } from \"../utils/dock\";\nimport { renderLucideIcon } from \"../utils/icons\";\nimport { DEFAULT_OVERLAY_Z_INDEX } from \"../utils/constants\";\n\nexport interface LauncherButton {\n  element: HTMLButtonElement;\n  update: (config: AgentWidgetConfig) => void;\n  destroy: () => void;\n}\n\nexport const createLauncherButton = (\n  config: AgentWidgetConfig | undefined,\n  onToggle: () => void\n): LauncherButton => {\n  const button = createElement(\"button\") as HTMLButtonElement;\n  button.type = \"button\";\n  button.innerHTML = `\n    <span class=\"persona-inline-flex persona-items-center persona-justify-center persona-rounded-full persona-bg-persona-primary persona-text-white\" data-role=\"launcher-icon\">💬</span>\n    <img data-role=\"launcher-image\" class=\"persona-rounded-full persona-object-cover\" alt=\"\" style=\"display:none\" />\n    <span class=\"persona-flex persona-min-w-0 persona-flex-1 persona-flex-col persona-items-start persona-text-left\">\n      <span class=\"persona-block persona-w-full persona-truncate persona-text-sm persona-font-semibold persona-text-persona-primary\" data-role=\"launcher-title\"></span>\n      <span class=\"persona-block persona-w-full persona-truncate persona-text-xs persona-text-persona-muted\" data-role=\"launcher-subtitle\"></span>\n    </span>\n    <span class=\"persona-ml-2 persona-grid persona-place-items-center persona-rounded-full persona-bg-persona-primary persona-text-persona-call-to-action\" data-role=\"launcher-call-to-action-icon\">↗</span>\n  `;\n  button.addEventListener(\"click\", onToggle);\n\n  const update = (newConfig: AgentWidgetConfig) => {\n    const launcher = newConfig.launcher ?? {};\n    const dockedMode = isDockedMountMode(newConfig);\n\n    const titleEl = button.querySelector(\"[data-role='launcher-title']\");\n    if (titleEl) {\n      const t = launcher.title ?? \"Chat Assistant\";\n      titleEl.textContent = t;\n      titleEl.setAttribute(\"title\", t);\n    }\n\n    const subtitleEl = button.querySelector(\"[data-role='launcher-subtitle']\");\n    if (subtitleEl) {\n      const s = launcher.subtitle ?? \"Here to help you get answers fast\";\n      subtitleEl.textContent = s;\n      subtitleEl.setAttribute(\"title\", s);\n    }\n\n    // Hide/show text container\n    const textContainer = button.querySelector(\".persona-flex-col\");\n    if (textContainer) {\n      if (launcher.textHidden || dockedMode) {\n        (textContainer as HTMLElement).style.display = \"none\";\n      } else {\n        (textContainer as HTMLElement).style.display = \"\";\n      }\n    }\n\n    const icon = button.querySelector<HTMLSpanElement>(\"[data-role='launcher-icon']\");\n    if (icon) {\n      if (launcher.agentIconHidden) {\n        icon.style.display = \"none\";\n      } else {\n        const iconSize = launcher.agentIconSize ?? \"40px\";\n        icon.style.height = iconSize;\n        icon.style.width = iconSize;\n\n        // Optional custom background color for the agent icon circle. When set,\n        // override the default primary-color background; otherwise restore it.\n        if (launcher.agentIconBackgroundColor) {\n          icon.style.backgroundColor = launcher.agentIconBackgroundColor;\n          icon.classList.remove(\"persona-bg-persona-primary\");\n        } else {\n          icon.style.backgroundColor = \"\";\n          icon.classList.add(\"persona-bg-persona-primary\");\n        }\n\n        // Clear existing content\n        icon.innerHTML = \"\";\n        \n        // Render icon based on priority: Lucide icon > iconUrl > agentIconText\n        if (launcher.agentIconName) {\n          // Use Lucide icon\n          const iconSizeNum = parseFloat(iconSize) || 24;\n          const iconSvg = renderLucideIcon(launcher.agentIconName, iconSizeNum * 0.6, \"var(--persona-text-inverse, #ffffff)\", 2);\n          if (iconSvg) {\n            icon.appendChild(iconSvg);\n            icon.style.display = \"\";\n          } else {\n            // Fallback to agentIconText if Lucide icon fails\n            icon.textContent = launcher.agentIconText ?? \"💬\";\n            icon.style.display = \"\";\n          }\n        } else if (launcher.iconUrl) {\n          // Use image URL - hide icon span and show img\n          icon.style.display = \"none\";\n        } else {\n          // Use text/emoji\n          icon.textContent = launcher.agentIconText ?? \"💬\";\n          icon.style.display = \"\";\n        }\n      }\n    }\n\n    const img = button.querySelector<HTMLImageElement>(\"[data-role='launcher-image']\");\n    if (img) {\n      const iconSize = launcher.agentIconSize ?? \"40px\";\n      img.style.height = iconSize;\n      img.style.width = iconSize;\n      if (launcher.iconUrl && !launcher.agentIconName && !launcher.agentIconHidden) {\n        // Only show image if not using Lucide icon and not hidden\n        img.src = launcher.iconUrl;\n        img.style.display = \"block\";\n      } else {\n        img.style.display = \"none\";\n      }\n    }\n\n    const callToActionIconEl = button.querySelector<HTMLSpanElement>(\"[data-role='launcher-call-to-action-icon']\");\n    if (callToActionIconEl) {\n      const callToActionIconSize = launcher.callToActionIconSize ?? \"32px\";\n      callToActionIconEl.style.height = callToActionIconSize;\n      callToActionIconEl.style.width = callToActionIconSize;\n      \n      // Apply background color if configured\n      if (launcher.callToActionIconBackgroundColor) {\n        callToActionIconEl.style.backgroundColor = launcher.callToActionIconBackgroundColor;\n        callToActionIconEl.classList.remove(\"persona-bg-persona-primary\");\n      } else {\n        callToActionIconEl.style.backgroundColor = \"\";\n        callToActionIconEl.classList.add(\"persona-bg-persona-primary\");\n      }\n\n      // Apply foreground/icon color if configured\n      if (launcher.callToActionIconColor) {\n        callToActionIconEl.style.color = launcher.callToActionIconColor;\n        callToActionIconEl.classList.remove(\"persona-text-persona-call-to-action\");\n      } else {\n        callToActionIconEl.style.color = \"\";\n        callToActionIconEl.classList.add(\"persona-text-persona-call-to-action\");\n      }\n      \n      // Calculate padding to adjust icon size\n      let paddingTotal = 0;\n      if (launcher.callToActionIconPadding) {\n        callToActionIconEl.style.boxSizing = \"border-box\";\n        callToActionIconEl.style.padding = launcher.callToActionIconPadding;\n        // Parse padding value to calculate total padding (padding applies to both sides)\n        const paddingValue = parseFloat(launcher.callToActionIconPadding) || 0;\n        paddingTotal = paddingValue * 2; // padding on both sides\n      } else {\n        callToActionIconEl.style.boxSizing = \"\";\n        callToActionIconEl.style.padding = \"\";\n      }\n      \n      if (launcher.callToActionIconHidden) {\n        callToActionIconEl.style.display = \"none\";\n      } else {\n        callToActionIconEl.style.display = dockedMode ? \"none\" : \"\";\n        \n        // Clear existing content\n        callToActionIconEl.innerHTML = \"\";\n        \n        // Use Lucide icon if provided, otherwise fall back to text\n        if (launcher.callToActionIconName) {\n          // Calculate actual icon size by subtracting padding\n          const containerSize = parseFloat(callToActionIconSize) || 24;\n          const iconSize = Math.max(containerSize - paddingTotal, 8); // Ensure minimum size of 8px\n          const iconSvg = renderLucideIcon(launcher.callToActionIconName, iconSize, \"currentColor\", 2);\n          if (iconSvg) {\n            callToActionIconEl.appendChild(iconSvg);\n          } else {\n            // Fallback to text if icon fails to render\n            callToActionIconEl.textContent = launcher.callToActionIconText ?? \"↗\";\n          }\n        } else {\n          callToActionIconEl.textContent = launcher.callToActionIconText ?? \"↗\";\n        }\n      }\n    }\n\n    const positionClass =\n      launcher.position && positionMap[launcher.position]\n        ? positionMap[launcher.position]\n        : positionMap[\"bottom-right\"];\n\n    const floatingBase =\n      \"persona-fixed persona-flex persona-items-center persona-gap-3 persona-rounded-launcher persona-bg-persona-surface persona-py-2.5 persona-pl-3 persona-pr-3 persona-transition hover:persona-translate-y-[-2px] persona-cursor-pointer\";\n    const dockedBase =\n      \"persona-relative persona-mt-4 persona-mb-4 persona-mx-auto persona-flex persona-items-center persona-justify-center persona-rounded-launcher persona-bg-persona-surface persona-transition hover:persona-translate-y-[-2px] persona-cursor-pointer\";\n\n    button.className = dockedMode ? dockedBase : `${floatingBase} ${positionClass}`;\n\n    if (!dockedMode) {\n      button.style.zIndex = String(launcher.zIndex ?? DEFAULT_OVERLAY_Z_INDEX);\n    }\n\n    // Apply launcher border and shadow from config (with defaults matching previous Tailwind classes)\n    const defaultBorder = \"1px solid var(--persona-border, #e5e7eb)\";\n    const defaultShadow = \"var(--persona-launcher-shadow, 0 10px 15px -3px rgba(0, 0, 0, 0.1), 0 4px 6px -4px rgba(0, 0, 0, 0.1))\";\n\n    button.style.border = launcher.border ?? defaultBorder;\n    button.style.boxShadow =\n      launcher.shadow !== undefined\n        ? (launcher.shadow.trim() === \"\" ? \"none\" : launcher.shadow)\n        : defaultShadow;\n\n    if (dockedMode) {\n      // Docked mode uses a 0px column when closed and hides this button; keep no hit target.\n      button.style.width = \"0\";\n      button.style.minWidth = \"0\";\n      button.style.maxWidth = \"0\";\n      button.style.padding = \"0\";\n      button.style.overflow = \"hidden\";\n      button.style.border = \"none\";\n      button.style.boxShadow = \"none\";\n    } else {\n      button.style.width = \"\";\n      button.style.minWidth = \"\";\n      button.style.maxWidth = launcher.collapsedMaxWidth ?? \"\";\n      button.style.justifyContent = \"\";\n      button.style.padding = \"\";\n      button.style.overflow = \"\";\n    }\n  };\n\n  const destroy = () => {\n    button.removeEventListener(\"click\", onToggle);\n    button.remove();\n  };\n\n  // Initial update\n  if (config) {\n    update(config);\n  }\n\n  return {\n    element: button,\n    update,\n    destroy\n  };\n};\n","import { AgentWidgetConfig } from \"../types\";\nimport { AgentWidgetPlugin } from \"../plugins/types\";\nimport { createWrapper, buildPanel, PanelElements } from \"./panel\";\nimport { HeaderElements } from \"./header-builder\";\nimport { createLauncherButton, LauncherButton } from \"./launcher\";\n\n/**\n * Widget view assembly layer.\n *\n * `ui.ts` owns behavior (session dispatch, streaming, scroll, voice, events);\n * this module owns the one-time structural assembly of the widget's DOM and\n * groups the resulting element references into named regions so callers read\n * `view.composer.textarea` instead of destructuring a ~30-field flat object.\n *\n * The grouped refs are a view over the same nodes returned by `buildPanel`;\n * the raw flat `PanelElements` is still exposed as `view.panelElements` for\n * incremental migration. No framework, no virtual DOM: these are the real\n * elements built by the existing `panel.ts` / `composer-*` / `header-*`\n * builders.\n */\n\nexport interface WidgetShellRefs {\n  wrapper: HTMLElement;\n  panel: HTMLElement;\n  /** Composer-bar mode only: viewport-fixed sibling that owns the pill. */\n  pillRoot?: HTMLElement;\n}\n\nexport interface WidgetTranscriptRefs {\n  container: HTMLElement;\n  body: HTMLElement;\n  messagesWrapper: HTMLElement;\n  /** Absolute slot above the composer where interactive sheets mount. */\n  composerOverlay: HTMLElement;\n  introTitle: HTMLElement;\n  introSubtitle: HTMLElement;\n}\n\nexport interface WidgetHeaderRefs {\n  /** The header root element currently mounted in the container. */\n  element: HTMLElement;\n  iconHolder: HTMLElement;\n  headerTitle: HTMLElement;\n  headerSubtitle: HTMLElement;\n  closeButton: HTMLButtonElement;\n  closeButtonWrapper: HTMLElement | null;\n  clearChatButton: HTMLButtonElement | null;\n  clearChatButtonWrapper: HTMLElement | null;\n}\n\nexport interface WidgetComposerRefs {\n  /** The footer root element currently mounted in the panel. */\n  footer: HTMLElement;\n  form: HTMLFormElement;\n  textarea: HTMLTextAreaElement;\n  sendButton: HTMLButtonElement;\n  sendButtonWrapper: HTMLElement;\n  micButton: HTMLButtonElement | null;\n  micButtonWrapper: HTMLElement | null;\n  statusText: HTMLElement;\n  suggestions: HTMLElement;\n  attachmentButton: HTMLButtonElement | null;\n  attachmentButtonWrapper: HTMLElement | null;\n  attachmentInput: HTMLInputElement | null;\n  attachmentPreviewsContainer: HTMLElement | null;\n  actionsRow: HTMLElement;\n  leftActions: HTMLElement;\n  rightActions: HTMLElement;\n  setSendButtonMode: (mode: \"send\" | \"stop\") => void;\n  /** Composer-bar peek banner (undefined for non-composer-bar modes). */\n  peekBanner?: HTMLButtonElement;\n  peekTextNode?: HTMLElement;\n}\n\nexport interface WidgetView {\n  shell: WidgetShellRefs;\n  /** Raw flat refs from `buildPanel`, retained for incremental migration. */\n  panelElements: PanelElements;\n  transcript: WidgetTranscriptRefs;\n  header: WidgetHeaderRefs;\n  composer: WidgetComposerRefs;\n  /**\n   * Swap the mounted header element for `next.header` and mirror the header\n   * sub-refs (icon/title/subtitle/close) onto `view.header`. Used by the\n   * header-layout rebuild path. Returns the new `HeaderElements` for the\n   * caller to mirror into its own locals during incremental migration.\n   */\n  replaceHeader: (next: HeaderElements) => HeaderElements;\n  /**\n   * Swap the mounted footer element for `nextFooter` and update\n   * `view.composer.footer`. The remaining composer sub-refs become stale\n   * after a plugin-provided composer replaces the footer; callers re-derive\n   * them from the new footer (see `bindComposerRefsFromFooter` in ui.ts).\n   */\n  replaceComposer: (nextFooter: HTMLElement) => void;\n}\n\nexport interface CreateWidgetViewOptions {\n  config: AgentWidgetConfig;\n  /** Whether the panel renders a close affordance (panel is toggleable). */\n  showClose: boolean;\n}\n\n/**\n * Build the widget shell + panel once and group the element references into\n * named regions. Equivalent to the previous `createWrapper(config)` +\n * `buildPanel(config, showClose)` pair, plus the grouping/replacement helpers.\n */\nexport const createWidgetView = ({\n  config,\n  showClose,\n}: CreateWidgetViewOptions): WidgetView => {\n  const { wrapper, panel, pillRoot } = createWrapper(config);\n  const panelElements = buildPanel(config, showClose);\n\n  const shell: WidgetShellRefs = { wrapper, panel, pillRoot };\n\n  const transcript: WidgetTranscriptRefs = {\n    container: panelElements.container,\n    body: panelElements.body,\n    messagesWrapper: panelElements.messagesWrapper,\n    composerOverlay: panelElements.composerOverlay,\n    introTitle: panelElements.introTitle,\n    introSubtitle: panelElements.introSubtitle,\n  };\n\n  const header: WidgetHeaderRefs = {\n    element: panelElements.header,\n    iconHolder: panelElements.iconHolder,\n    headerTitle: panelElements.headerTitle,\n    headerSubtitle: panelElements.headerSubtitle,\n    closeButton: panelElements.closeButton,\n    closeButtonWrapper: panelElements.closeButtonWrapper,\n    clearChatButton: panelElements.clearChatButton,\n    clearChatButtonWrapper: panelElements.clearChatButtonWrapper,\n  };\n\n  const composer: WidgetComposerRefs = {\n    footer: panelElements.footer,\n    form: panelElements.composerForm,\n    textarea: panelElements.textarea,\n    sendButton: panelElements.sendButton,\n    sendButtonWrapper: panelElements.sendButtonWrapper,\n    micButton: panelElements.micButton,\n    micButtonWrapper: panelElements.micButtonWrapper,\n    statusText: panelElements.statusText,\n    suggestions: panelElements.suggestions,\n    attachmentButton: panelElements.attachmentButton,\n    attachmentButtonWrapper: panelElements.attachmentButtonWrapper,\n    attachmentInput: panelElements.attachmentInput,\n    attachmentPreviewsContainer: panelElements.attachmentPreviewsContainer,\n    actionsRow: panelElements.actionsRow,\n    leftActions: panelElements.leftActions,\n    rightActions: panelElements.rightActions,\n    setSendButtonMode: panelElements.setSendButtonMode,\n    peekBanner: panelElements.peekBanner,\n    peekTextNode: panelElements.peekTextNode,\n  };\n\n  const replaceHeader = (next: HeaderElements): HeaderElements => {\n    header.element.replaceWith(next.header);\n    header.element = next.header;\n    header.iconHolder = next.iconHolder;\n    header.headerTitle = next.headerTitle;\n    header.headerSubtitle = next.headerSubtitle;\n    header.closeButton = next.closeButton;\n    header.closeButtonWrapper = next.closeButtonWrapper;\n    header.clearChatButton = next.clearChatButton;\n    header.clearChatButtonWrapper = next.clearChatButtonWrapper;\n    return next;\n  };\n\n  const replaceComposer = (nextFooter: HTMLElement): void => {\n    composer.footer.replaceWith(nextFooter);\n    composer.footer = nextFooter;\n  };\n\n  return {\n    shell,\n    panelElements,\n    transcript,\n    header,\n    composer,\n    replaceHeader,\n    replaceComposer,\n  };\n};\n\nexport interface ResolveLauncherOptions {\n  config: AgentWidgetConfig;\n  plugins: AgentWidgetPlugin[];\n  /** Toggles the panel open/closed; wired to the launcher's click. */\n  onToggle: () => void;\n}\n\nexport interface ResolvedLauncher {\n  /**\n   * The default launcher controller (with `update`/`destroy`), or null when a\n   * plugin supplied a custom launcher element.\n   */\n  instance: LauncherButton | null;\n  /** The element to mount: either the default button or the plugin's element. */\n  element: HTMLElement;\n}\n\n/**\n * Resolve the launcher element, honoring a `renderLauncher` plugin if present.\n * De-duplicates the identical default-or-plugin logic that previously lived at\n * both the initial-build and re-enable call sites in ui.ts.\n *\n * When a plugin returns a custom element, `instance` is null (the plugin owns\n * its own updates); otherwise the default `LauncherButton` controller is\n * returned so the caller can drive `update()`/`destroy()`.\n */\nexport const resolveLauncher = ({\n  config,\n  plugins,\n  onToggle,\n}: ResolveLauncherOptions): ResolvedLauncher => {\n  const launcherPlugin = plugins.find((p) => p.renderLauncher);\n  if (launcherPlugin?.renderLauncher) {\n    const customLauncher = launcherPlugin.renderLauncher({\n      config,\n      defaultRenderer: () => createLauncherButton(config, onToggle).element,\n      onToggle,\n    });\n    if (customLauncher) {\n      return { instance: null, element: customLauncher };\n    }\n  }\n\n  const instance = createLauncherButton(config, onToggle);\n  return { instance, element: instance.element };\n};\n","import { createElement } from \"../utils/dom\";\nimport {\n  AgentWidgetMessage,\n  AgentWidgetMessageLayoutConfig,\n  AgentWidgetAvatarConfig,\n  AgentWidgetTimestampConfig,\n  AgentWidgetMessageActionsConfig,\n  AgentWidgetMessageFeedback,\n  LoadingIndicatorRenderContext,\n  ImageContentPart,\n  AudioContentPart,\n  VideoContentPart,\n  FileContentPart,\n  StopReasonKind\n} from \"../types\";\nimport { createIconButton } from \"../utils/buttons\";\nimport { IMAGE_ONLY_MESSAGE_FALLBACK_TEXT } from \"../utils/content\";\nimport {\n  applyStreamBuffer,\n  createSkeletonPlaceholder,\n  createStreamCaret,\n  resolveStreamAnimation,\n  resolveStreamAnimationPlugin,\n  wrapStreamAnimation,\n} from \"../utils/stream-animation\";\n\n/**\n * Default copy for the inline notice rendered when a turn ends with a\n * non-natural stop reason. Deployers override per-reason via\n * `config.copy.stopReasonNotice`. Returns `null` for natural completions\n * (`end_turn`) and uninformative reasons (`unknown`): those never render\n * an affordance.\n */\nexport const getDefaultStopReasonNoticeCopy = (\n  stopReason: StopReasonKind\n): string | null => {\n  switch (stopReason) {\n    case \"max_tool_calls\":\n      return \"Stopped after calling a tool. Send a follow-up to continue.\";\n    case \"length\":\n      return \"Response cut off as max tokens reached. Ask for more to continue.\";\n    case \"content_filter\":\n      return \"The provider filtered this response.\";\n    case \"error\":\n      return \"Something went wrong generating this response.\";\n    case \"end_turn\":\n    case \"unknown\":\n    default:\n      return null;\n  }\n};\n\n/**\n * Resolve the notice text for a stop reason, applying user overrides on\n * top of the built-in defaults. Returns `null` when the reason does not\n * warrant a notice or when the resolved string is empty (deployers can\n * suppress per-reason by setting an empty override).\n */\nexport const resolveStopReasonNoticeText = (\n  stopReason: StopReasonKind | undefined,\n  overrides?: Partial<Record<StopReasonKind, string>>\n): string | null => {\n  if (!stopReason) return null;\n  const fallback = getDefaultStopReasonNoticeCopy(stopReason);\n  // Reasons without a default (end_turn, unknown) never render: overrides\n  // for those keys are intentionally ignored.\n  if (fallback === null) return null;\n  const override = overrides?.[stopReason];\n  const text = override !== undefined ? override : fallback;\n  if (!text) return null;\n  return text;\n};\n\n/**\n * Build the inline notice element rendered on assistant bubbles whose\n * turn ended with `max_tool_calls`, `length`, `content_filter`, or `error`.\n */\nconst createStopReasonNotice = (\n  stopReason: StopReasonKind,\n  text: string\n): HTMLElement => {\n  const notice = createElement(\n    \"div\",\n    \"persona-message-stop-reason persona-text-xs persona-mt-2 persona-italic\"\n  );\n  notice.setAttribute(\"data-stop-reason\", stopReason);\n  notice.setAttribute(\"role\", \"note\");\n  notice.style.opacity = \"0.75\";\n  notice.textContent = text;\n  return notice;\n};\n\n/** Validate that an image src URL uses a safe scheme (blocks javascript: and SVG data URIs). */\nexport const isSafeImageSrc = (src: string): boolean => {\n  const lower = src.toLowerCase();\n  if (lower.startsWith(\"data:image/svg+xml\")) return false;\n  if (/^(?:https?|blob):/i.test(src)) return true;\n  if (lower.startsWith(\"data:image/\")) return true;\n  // Relative URLs are safe\n  if (!src.includes(\":\")) return true;\n  return false;\n};\n\n/**\n * Validate that a media src URL (audio/video/file) uses a safe scheme.\n * Allows http(s), blob:, and inert data: URIs. Blocks `javascript:` and the\n * executable data: types whose payloads a browser would render or run when a\n * user right-clicks the link → \"Open in new tab\" (bypassing `download`):\n * `data:text/html`, `data:text/javascript`, `data:text/xml`,\n * `data:application/xhtml`, and `data:image/svg+xml`. Inert text payloads\n * (`text/plain`, `text/csv`, `text/markdown`) remain allowed so attachments\n * with those mime types render as download links instead of vanishing.\n */\nexport const isSafeMediaSrc = (src: string): boolean => {\n  const lower = src.toLowerCase();\n  if (lower.startsWith(\"javascript:\")) return false;\n  if (lower.startsWith(\"data:text/html\")) return false;\n  if (lower.startsWith(\"data:text/javascript\")) return false;\n  if (lower.startsWith(\"data:text/xml\")) return false;\n  if (lower.startsWith(\"data:application/xhtml\")) return false;\n  if (lower.startsWith(\"data:image/svg+xml\")) return false;\n  if (/^(?:https?|blob):/i.test(src)) return true;\n  if (lower.startsWith(\"data:\")) return true;\n  // Relative URLs are safe\n  if (!src.includes(\":\")) return true;\n  return false;\n};\n\nexport type LoadingIndicatorRenderer = (context: LoadingIndicatorRenderContext) => HTMLElement | null;\n\nexport type MessageTransform = (context: {\n  text: string;\n  message: AgentWidgetMessage;\n  streaming: boolean;\n  raw?: string;\n}) => string;\n\nexport type MessageActionCallbacks = {\n  onCopy?: (message: AgentWidgetMessage) => void;\n  onFeedback?: (feedback: AgentWidgetMessageFeedback) => void;\n};\n\nconst MESSAGE_IMAGE_PREVIEW_MAX_WIDTH_PX = 320;\nconst MESSAGE_IMAGE_PREVIEW_MAX_HEIGHT_PX = 320;\n\nconst getMessageImageParts = (message: AgentWidgetMessage): ImageContentPart[] => {\n  if (!message.contentParts || message.contentParts.length === 0) {\n    return [];\n  }\n\n  return message.contentParts.filter(\n    (part): part is ImageContentPart =>\n      part.type === \"image\" &&\n      typeof part.image === \"string\" &&\n      part.image.trim().length > 0\n  );\n};\n\nconst getMessageAudioParts = (message: AgentWidgetMessage): AudioContentPart[] => {\n  if (!message.contentParts || message.contentParts.length === 0) return [];\n  return message.contentParts.filter(\n    (part): part is AudioContentPart =>\n      part.type === \"audio\" &&\n      typeof part.audio === \"string\" &&\n      part.audio.trim().length > 0\n  );\n};\n\nconst getMessageVideoParts = (message: AgentWidgetMessage): VideoContentPart[] => {\n  if (!message.contentParts || message.contentParts.length === 0) return [];\n  return message.contentParts.filter(\n    (part): part is VideoContentPart =>\n      part.type === \"video\" &&\n      typeof part.video === \"string\" &&\n      part.video.trim().length > 0\n  );\n};\n\nconst getMessageFileParts = (message: AgentWidgetMessage): FileContentPart[] => {\n  if (!message.contentParts || message.contentParts.length === 0) return [];\n  return message.contentParts.filter(\n    (part): part is FileContentPart =>\n      part.type === \"file\" &&\n      typeof part.data === \"string\" &&\n      part.data.trim().length > 0\n  );\n};\n\nconst createMessageImagePreviews = (\n  imageParts: ImageContentPart[],\n  hasVisibleText: boolean,\n  onPreviewFailed?: () => void\n): HTMLElement | null => {\n  if (imageParts.length === 0) return null;\n\n  try {\n    const container = createElement(\n      \"div\",\n      \"persona-flex persona-flex-col persona-gap-2\"\n    );\n    container.setAttribute(\"data-message-attachments\", \"images\");\n    if (hasVisibleText) {\n      container.style.marginBottom = \"8px\";\n    }\n\n    let visiblePreviewCount = 0;\n    let failureHandled = false;\n\n    const handleTotalPreviewFailure = () => {\n      if (failureHandled) return;\n      failureHandled = true;\n      container.remove();\n      onPreviewFailed?.();\n    };\n\n    imageParts.forEach((imagePart, index) => {\n      const imageElement = createElement(\"img\") as HTMLImageElement;\n      imageElement.alt = imagePart.alt?.trim() || `Attached image ${index + 1}`;\n      imageElement.loading = \"lazy\";\n      imageElement.decoding = \"async\";\n      imageElement.referrerPolicy = \"no-referrer\";\n      imageElement.style.display = \"block\";\n      imageElement.style.width = \"100%\";\n      imageElement.style.maxWidth = `${MESSAGE_IMAGE_PREVIEW_MAX_WIDTH_PX}px`;\n      imageElement.style.maxHeight = `${MESSAGE_IMAGE_PREVIEW_MAX_HEIGHT_PX}px`;\n      imageElement.style.height = \"auto\";\n      imageElement.style.objectFit = \"contain\";\n      imageElement.style.borderRadius = \"10px\";\n      imageElement.style.backgroundColor = \"var(--persona-attachment-image-bg, var(--persona-container, #f3f4f6))\";\n      imageElement.style.border = \"1px solid var(--persona-attachment-image-border, var(--persona-border, #e5e7eb))\";\n\n      let settled = false;\n      visiblePreviewCount += 1;\n      imageElement.addEventListener(\"error\", () => {\n        if (settled) return;\n        settled = true;\n        visiblePreviewCount = Math.max(0, visiblePreviewCount - 1);\n        imageElement.remove();\n        if (visiblePreviewCount === 0) {\n          handleTotalPreviewFailure();\n        }\n      });\n      imageElement.addEventListener(\"load\", () => {\n        settled = true;\n      });\n\n      if (isSafeImageSrc(imagePart.image)) {\n        imageElement.src = imagePart.image;\n        container.appendChild(imageElement);\n      } else {\n        // Treat blocked images like load errors so fallback triggers correctly\n        settled = true;\n        visiblePreviewCount = Math.max(0, visiblePreviewCount - 1);\n        imageElement.remove();\n      }\n    });\n\n    if (visiblePreviewCount === 0) {\n      handleTotalPreviewFailure();\n      return null;\n    }\n\n    return container;\n  } catch {\n    onPreviewFailed?.();\n    return null;\n  }\n};\n\nconst createMessageAudioPreviews = (\n  audioParts: AudioContentPart[]\n): HTMLElement | null => {\n  if (audioParts.length === 0) return null;\n  try {\n    const container = createElement(\n      \"div\",\n      \"persona-flex persona-flex-col persona-gap-2\"\n    );\n    container.setAttribute(\"data-message-attachments\", \"audio\");\n    let visible = 0;\n    audioParts.forEach((part) => {\n      if (!isSafeMediaSrc(part.audio)) return;\n      const audioElement = createElement(\"audio\") as HTMLAudioElement;\n      audioElement.controls = true;\n      audioElement.preload = \"metadata\";\n      audioElement.src = part.audio;\n      audioElement.style.display = \"block\";\n      audioElement.style.width = \"100%\";\n      audioElement.style.maxWidth = `${MESSAGE_IMAGE_PREVIEW_MAX_WIDTH_PX}px`;\n      container.appendChild(audioElement);\n      visible += 1;\n    });\n    if (visible === 0) {\n      container.remove();\n      return null;\n    }\n    return container;\n  } catch {\n    return null;\n  }\n};\n\nconst createMessageVideoPreviews = (\n  videoParts: VideoContentPart[]\n): HTMLElement | null => {\n  if (videoParts.length === 0) return null;\n  try {\n    const container = createElement(\n      \"div\",\n      \"persona-flex persona-flex-col persona-gap-2\"\n    );\n    container.setAttribute(\"data-message-attachments\", \"video\");\n    let visible = 0;\n    videoParts.forEach((part) => {\n      if (!isSafeMediaSrc(part.video)) return;\n      const videoElement = createElement(\"video\") as HTMLVideoElement;\n      videoElement.controls = true;\n      videoElement.preload = \"metadata\";\n      videoElement.src = part.video;\n      videoElement.style.display = \"block\";\n      videoElement.style.width = \"100%\";\n      videoElement.style.maxWidth = `${MESSAGE_IMAGE_PREVIEW_MAX_WIDTH_PX}px`;\n      videoElement.style.maxHeight = `${MESSAGE_IMAGE_PREVIEW_MAX_HEIGHT_PX}px`;\n      videoElement.style.borderRadius = \"10px\";\n      videoElement.style.backgroundColor =\n        \"var(--persona-attachment-image-bg, var(--persona-container, #f3f4f6))\";\n      container.appendChild(videoElement);\n      visible += 1;\n    });\n    if (visible === 0) {\n      container.remove();\n      return null;\n    }\n    return container;\n  } catch {\n    return null;\n  }\n};\n\nconst createMessageFilePreviews = (\n  fileParts: FileContentPart[]\n): HTMLElement | null => {\n  if (fileParts.length === 0) return null;\n  try {\n    const container = createElement(\n      \"div\",\n      \"persona-flex persona-flex-col persona-gap-2\"\n    );\n    container.setAttribute(\"data-message-attachments\", \"files\");\n    let visible = 0;\n    fileParts.forEach((part) => {\n      if (!isSafeMediaSrc(part.data)) return;\n      const link = createElement(\"a\") as HTMLAnchorElement;\n      link.href = part.data;\n      link.download = part.filename;\n      // Cross-origin URLs ignore the `download` attribute, so without\n      // `target=_blank` the link would navigate the chat page to the file.\n      // Pair with `rel=\"noopener noreferrer\"` to prevent reverse tabnabbing.\n      link.target = \"_blank\";\n      link.rel = \"noopener noreferrer\";\n      link.textContent = part.filename;\n      link.className = \"persona-message-file-attachment\";\n      link.style.display = \"inline-flex\";\n      link.style.alignItems = \"center\";\n      link.style.gap = \"6px\";\n      link.style.padding = \"6px 10px\";\n      link.style.borderRadius = \"8px\";\n      link.style.fontSize = \"0.875rem\";\n      link.style.textDecoration = \"underline\";\n      link.style.backgroundColor =\n        \"var(--persona-attachment-file-bg, var(--persona-container, #f3f4f6))\";\n      link.style.border =\n        \"1px solid var(--persona-attachment-file-border, var(--persona-border, #e5e7eb))\";\n      link.style.color = \"inherit\";\n      container.appendChild(link);\n      visible += 1;\n    });\n    if (visible === 0) {\n      container.remove();\n      return null;\n    }\n    return container;\n  } catch {\n    return null;\n  }\n};\n\n// Create typing indicator element\nexport const createTypingIndicator = (): HTMLElement => {\n  const container = document.createElement(\"div\");\n  container.className = \"persona-flex persona-items-center persona-space-x-1 persona-h-5 persona-mt-2\";\n\n  const dot1 = document.createElement(\"div\");\n  dot1.className = \"persona-animate-typing persona-rounded-full persona-h-1.5 persona-w-1.5\";\n  dot1.style.backgroundColor = \"currentColor\";\n  dot1.style.opacity = \"0.4\";\n  dot1.style.animationDelay = \"0ms\";\n\n  const dot2 = document.createElement(\"div\");\n  dot2.className = \"persona-animate-typing persona-rounded-full persona-h-1.5 persona-w-1.5\";\n  dot2.style.backgroundColor = \"currentColor\";\n  dot2.style.opacity = \"0.4\";\n  dot2.style.animationDelay = \"250ms\";\n\n  const dot3 = document.createElement(\"div\");\n  dot3.className = \"persona-animate-typing persona-rounded-full persona-h-1.5 persona-w-1.5\";\n  dot3.style.backgroundColor = \"currentColor\";\n  dot3.style.opacity = \"0.4\";\n  dot3.style.animationDelay = \"500ms\";\n\n  const srOnly = document.createElement(\"span\");\n  srOnly.className = \"persona-sr-only\";\n  srOnly.textContent = \"Loading\";\n\n  container.appendChild(dot1);\n  container.appendChild(dot2);\n  container.appendChild(dot3);\n  container.appendChild(srOnly);\n\n  return container;\n};\n\n/**\n * Render loading indicator with fallback chain:\n * 1. Custom renderer (if provided and returns non-null)\n * 2. Default typing indicator\n */\nexport const renderLoadingIndicatorWithFallback = (\n  location: 'inline' | 'standalone',\n  customRenderer?: LoadingIndicatorRenderer,\n  widgetConfig?: import(\"../types\").AgentWidgetConfig\n): HTMLElement | null => {\n  const context: LoadingIndicatorRenderContext = {\n    config: widgetConfig ?? ({} as import(\"../types\").AgentWidgetConfig),\n    streaming: true,\n    location,\n    defaultRenderer: createTypingIndicator\n  };\n\n  // Try custom renderer first\n  if (customRenderer) {\n    const result = customRenderer(context);\n    if (result !== null) {\n      return result;\n    }\n  }\n\n  // Fall back to default\n  return createTypingIndicator();\n};\n\n/**\n * Create an avatar element\n */\nconst createAvatar = (\n  avatarConfig: AgentWidgetAvatarConfig,\n  role: \"user\" | \"assistant\"\n): HTMLElement => {\n  const avatar = createElement(\n    \"div\",\n    \"persona-flex-shrink-0 persona-w-8 persona-h-8 persona-rounded-full persona-flex persona-items-center persona-justify-center persona-text-sm\"\n  );\n\n  const avatarContent = role === \"user\" \n    ? avatarConfig.userAvatar \n    : avatarConfig.assistantAvatar;\n\n  if (avatarContent) {\n    // Check if it's a URL or emoji/text\n    if (avatarContent.startsWith(\"http\") || avatarContent.startsWith(\"/\") || avatarContent.startsWith(\"data:\")) {\n      const img = createElement(\"img\") as HTMLImageElement;\n      img.src = avatarContent;\n      img.alt = role === \"user\" ? \"User\" : \"Assistant\";\n      img.className = \"persona-w-full persona-h-full persona-rounded-full persona-object-cover\";\n      avatar.appendChild(img);\n    } else {\n      // Emoji or text\n      avatar.textContent = avatarContent;\n      avatar.classList.add(\n        role === \"user\" ? \"persona-bg-persona-accent\" : \"persona-bg-persona-primary\",\n        \"persona-text-white\"\n      );\n    }\n  } else {\n    // Default avatar\n    avatar.textContent = role === \"user\" ? \"U\" : \"A\";\n    avatar.classList.add(\n      role === \"user\" ? \"persona-bg-persona-accent\" : \"persona-bg-persona-primary\",\n      \"persona-text-white\"\n    );\n  }\n\n  return avatar;\n};\n\n/**\n * Create a timestamp element\n */\nconst createTimestamp = (\n  message: AgentWidgetMessage,\n  timestampConfig: AgentWidgetTimestampConfig,\n  tagName: \"div\" | \"span\" = \"div\"\n): HTMLElement => {\n  const timestamp = createElement(\n    tagName,\n    \"persona-text-xs persona-text-persona-muted\"\n  );\n\n  const date = new Date(message.createdAt);\n  \n  if (timestampConfig.format) {\n    timestamp.textContent = timestampConfig.format(date);\n  } else {\n    // Default format: HH:MM\n    timestamp.textContent = date.toLocaleTimeString([], { \n      hour: \"2-digit\", \n      minute: \"2-digit\" \n    });\n  }\n\n  return timestamp;\n};\n\n/**\n * Get bubble classes based on layout preset\n */\nconst getBubbleClasses = (\n  role: \"user\" | \"assistant\" | \"system\",\n  layout: AgentWidgetMessageLayoutConfig[\"layout\"] = \"bubble\"\n): string[] => {\n  const baseClasses = [\"persona-message-bubble\", \"persona-max-w-[85%]\"];\n\n  switch (layout) {\n    case \"flat\":\n      // Flat layout: no bubble styling, just text\n      if (role === \"user\") {\n        baseClasses.push(\n          \"persona-message-user-bubble\",\n          \"persona-ml-auto\",\n          \"persona-text-persona-primary\",\n          \"persona-py-2\"\n        );\n      } else {\n        baseClasses.push(\n          \"persona-message-assistant-bubble\",\n          \"persona-text-persona-primary\",\n          \"persona-py-2\"\n        );\n      }\n      break;\n\n    case \"minimal\":\n      // Minimal layout: reduced padding and styling\n      baseClasses.push(\n        \"persona-text-sm\",\n        \"persona-leading-relaxed\"\n      );\n      if (role === \"user\") {\n        baseClasses.push(\n          \"persona-message-user-bubble\",\n          \"persona-ml-auto\",\n          \"persona-bg-persona-accent\",\n          \"persona-text-white\",\n          \"persona-px-3\",\n          \"persona-py-2\",\n          \"persona-rounded-lg\"\n        );\n      } else {\n        baseClasses.push(\n          \"persona-message-assistant-bubble\",\n          \"persona-bg-persona-surface\",\n          \"persona-text-persona-primary\",\n          \"persona-px-3\",\n          \"persona-py-2\",\n          \"persona-rounded-lg\"\n        );\n      }\n      break;\n\n    case \"bubble\":\n    default:\n      // Default bubble layout\n      baseClasses.push(\n        \"persona-rounded-2xl\",\n        \"persona-text-sm\",\n        \"persona-leading-relaxed\",\n        \"persona-shadow-sm\"\n      );\n      if (role === \"user\") {\n        baseClasses.push(\n          \"persona-message-user-bubble\",\n          \"persona-ml-auto\",\n          \"persona-bg-persona-accent\",\n          \"persona-text-white\",\n          \"persona-px-5\",\n          \"persona-py-3\"\n        );\n      } else {\n        baseClasses.push(\n          \"persona-message-assistant-bubble\",\n          \"persona-bg-persona-surface\",\n          \"persona-border\",\n          \"persona-border-persona-message-border\",\n          \"persona-text-persona-primary\",\n          \"persona-px-5\",\n          \"persona-py-3\"\n        );\n      }\n      break;\n  }\n\n  return baseClasses;\n};\n\n/**\n * Create message action buttons (copy, upvote, downvote)\n *\n * This is a pure rendering function. It creates button elements with the\n * correct `data-action` attributes, icons, and CSS classes. All click\n * handling, vote state management, clipboard logic, and callback dispatch\n * is handled via event delegation in `ui.ts` so that handlers survive\n * idiomorph DOM morphing.\n */\nexport const createMessageActions = (\n  message: AgentWidgetMessage,\n  actionsConfig: AgentWidgetMessageActionsConfig,\n  // eslint-disable-next-line @typescript-eslint/no-unused-vars\n  _callbacks?: MessageActionCallbacks\n): HTMLElement => {\n  const showCopy = actionsConfig.showCopy ?? true;\n  const showUpvote = actionsConfig.showUpvote ?? true;\n  const showDownvote = actionsConfig.showDownvote ?? true;\n  const showReadAloud = actionsConfig.showReadAloud ?? false;\n\n  // Don't render the container at all when no actions are visible\n  if (!showCopy && !showUpvote && !showDownvote && !showReadAloud) {\n    const empty = createElement(\"div\");\n    empty.style.display = \"none\";\n    empty.id = `actions-${message.id}`;\n    empty.setAttribute(\"data-actions-for\", message.id);\n    return empty;\n  }\n\n  const visibility = actionsConfig.visibility ?? \"hover\";\n  const align = actionsConfig.align ?? \"right\";\n  const layout = actionsConfig.layout ?? \"pill-inside\";\n\n  // Map alignment to CSS class\n  const alignClass = {\n    left: \"persona-message-actions-left\",\n    center: \"persona-message-actions-center\",\n    right: \"persona-message-actions-right\",\n  }[align];\n\n  // Map layout to CSS class\n  const layoutClass = {\n    \"pill-inside\": \"persona-message-actions-pill\",\n    \"row-inside\": \"persona-message-actions-row\",\n  }[layout];\n\n  const container = createElement(\n    \"div\",\n    `persona-message-actions persona-flex persona-items-center persona-gap-1 persona-mt-2 ${alignClass} ${layoutClass} ${\n      visibility === \"hover\" ? \"persona-message-actions-hover\" : \"\"\n    }`\n  );\n  // Set id for idiomorph matching (prevents recreation on morph)\n  container.id = `actions-${message.id}`;\n  container.setAttribute(\"data-actions-for\", message.id);\n\n  const createActionButton = (\n    iconName: string,\n    label: string,\n    dataAction: string\n  ): HTMLButtonElement => {\n    const button = createIconButton({\n      icon: iconName,\n      label,\n      size: 14,\n      className: \"persona-message-action-btn\",\n    });\n    button.setAttribute(\"data-action\", dataAction);\n    return button;\n  };\n\n  // Copy button\n  if (showCopy) {\n    container.appendChild(createActionButton(\"copy\", \"Copy message\", \"copy\"));\n  }\n\n  // Read-aloud button. Click handling and play/pause/resume state are managed\n  // in ui.ts (event delegation + ReadAloudController) so they survive morphs.\n  if (showReadAloud) {\n    container.appendChild(createActionButton(\"volume-2\", \"Read aloud\", \"read-aloud\"));\n  }\n\n  // Upvote button\n  if (showUpvote) {\n    container.appendChild(createActionButton(\"thumbs-up\", \"Upvote\", \"upvote\"));\n  }\n\n  // Downvote button\n  if (showDownvote) {\n    container.appendChild(createActionButton(\"thumbs-down\", \"Downvote\", \"downvote\"));\n  }\n\n  return container;\n};\n\n/**\n * Options for creating a standard message bubble\n */\nexport type CreateStandardBubbleOptions = {\n  /**\n   * Custom loading indicator renderer for inline location\n   */\n  loadingIndicatorRenderer?: LoadingIndicatorRenderer;\n  /**\n   * Full widget config (needed for loading indicator context)\n   */\n  widgetConfig?: import(\"../types\").AgentWidgetConfig;\n};\n\n/**\n * Create standard message bubble\n * Supports layout configuration for avatars, timestamps, and visual presets\n */\nexport const createStandardBubble = (\n  message: AgentWidgetMessage,\n  transform: MessageTransform,\n  layoutConfig?: AgentWidgetMessageLayoutConfig,\n  actionsConfig?: AgentWidgetMessageActionsConfig,\n  actionCallbacks?: MessageActionCallbacks,\n  options?: CreateStandardBubbleOptions\n): HTMLElement => {\n  const config = layoutConfig ?? {};\n  const layout = config.layout ?? \"bubble\";\n  const avatarConfig = config.avatar;\n  const timestampConfig = config.timestamp;\n  const showAvatar = avatarConfig?.show ?? false;\n  const showTimestamp = timestampConfig?.show ?? false;\n  const avatarPosition = avatarConfig?.position ?? \"left\";\n  const timestampPosition = timestampConfig?.position ?? \"below\";\n\n  // Create the bubble element\n  const classes = getBubbleClasses(message.role, layout);\n  const bubble = createElement(\"div\", classes.join(\" \"));\n  // Set id for idiomorph matching\n  bubble.id = `bubble-${message.id}`;\n  bubble.setAttribute(\"data-message-id\", message.id);\n\n  bubble.setAttribute(\"data-persona-theme-zone\", message.role === \"user\" ? \"user-message\" : \"assistant-message\");\n\n  // Apply component-level color overrides via CSS variables\n  if (message.role === \"user\") {\n    bubble.style.backgroundColor = 'var(--persona-message-user-bg, var(--persona-accent))';\n    bubble.style.color = 'var(--persona-message-user-text, white)';\n  } else if (message.role === \"assistant\") {\n    bubble.style.backgroundColor = 'var(--persona-message-assistant-bg, var(--persona-surface))';\n    bubble.style.color = 'var(--persona-message-assistant-text, var(--persona-text))';\n  }\n\n  const imageParts = getMessageImageParts(message);\n  const messageContentText = message.content?.trim() ?? \"\";\n  const isImageOnlyFallbackMessage =\n    imageParts.length > 0 && messageContentText === IMAGE_ONLY_MESSAGE_FALLBACK_TEXT;\n  const shouldHideTextUntilPreviewFails = isImageOnlyFallbackMessage;\n\n  const streamAnimation = resolveStreamAnimation(\n    options?.widgetConfig?.features?.streamAnimation\n  );\n  const streamPluginOverrides =\n    options?.widgetConfig?.features?.streamAnimation?.plugins;\n  const streamPlugin =\n    message.role === \"assistant\" && streamAnimation.type !== \"none\"\n      ? resolveStreamAnimationPlugin(streamAnimation.type, streamPluginOverrides)\n      : null;\n  // Stay in \"streaming-animated\" mode while the plugin reports in-flight\n  // work for this message: e.g. glyph-cycle's tick loops still walking\n  // through the tail after the last token arrived. Without this, the final\n  // non-animated render rips out the cycling spans mid-animation.\n  const pluginStillAnimating =\n    message.role === \"assistant\" &&\n    streamPlugin?.isAnimating?.(message) === true;\n  const streamAnimationActive =\n    message.role === \"assistant\" &&\n    streamPlugin !== null &&\n    (Boolean(message.streaming) || pluginStillAnimating);\n\n  if (streamAnimationActive && streamPlugin?.bubbleClass) {\n    bubble.classList.add(streamPlugin.bubbleClass);\n  }\n\n  // Add message content\n  const contentDiv = document.createElement(\"div\");\n  contentDiv.classList.add(\"persona-message-content\");\n\n  // While streaming, lock table column widths (see widget.css) so rows append\n  // without per-chunk horizontal reflow. The class is dropped on the final,\n  // non-streaming render, relaxing tables to natural content-fit widths.\n  if (message.streaming) {\n    contentDiv.classList.add(\"persona-content-streaming\");\n  }\n\n  if (streamAnimationActive && streamPlugin) {\n    if (streamPlugin.containerClass) {\n      contentDiv.classList.add(streamPlugin.containerClass);\n    }\n    contentDiv.style.setProperty(\"--persona-stream-step\", `${streamAnimation.speed}ms`);\n    contentDiv.style.setProperty(\"--persona-stream-duration\", `${streamAnimation.duration}ms`);\n  }\n\n  const bufferedContent = streamAnimationActive\n    ? applyStreamBuffer(\n        message.content ?? \"\",\n        streamAnimation.buffer,\n        streamPlugin,\n        message,\n        Boolean(message.streaming)\n      )\n    : (message.content ?? \"\");\n\n  const transformedContent = transform({\n    text: bufferedContent,\n    message,\n    streaming: Boolean(message.streaming),\n    raw: message.rawContent\n  });\n\n  let animatedContent = transformedContent;\n  if (streamAnimationActive && streamPlugin?.wrap === \"char\") {\n    animatedContent = wrapStreamAnimation(transformedContent, \"char\", message.id, {\n      skipTags: streamPlugin.skipTags,\n    });\n  } else if (streamAnimationActive && streamPlugin?.wrap === \"word\") {\n    animatedContent = wrapStreamAnimation(transformedContent, \"word\", message.id, {\n      skipTags: streamPlugin.skipTags,\n    });\n  }\n\n  let textContentDiv: HTMLElement | null = null;\n\n  if (shouldHideTextUntilPreviewFails) {\n    textContentDiv = document.createElement(\"div\");\n    textContentDiv.innerHTML = animatedContent;\n    textContentDiv.style.display = \"none\";\n    contentDiv.appendChild(textContentDiv);\n  } else {\n    contentDiv.innerHTML = animatedContent;\n  }\n\n  if (\n    streamAnimationActive &&\n    streamPlugin?.useCaret &&\n    !shouldHideTextUntilPreviewFails &&\n    messageContentText\n  ) {\n    const caret = createStreamCaret();\n    // Caret must sit on the same line as the final char. Markdown wraps text\n    // in block elements (<p>, <li>, <pre>), so appending to contentDiv would\n    // drop the caret onto a fresh line. Tuck it after the last char/word span,\n    // or fall back to the last block when no spans exist yet.\n    const spans = contentDiv.querySelectorAll(\n      \".persona-stream-char, .persona-stream-word\"\n    );\n    const lastSpan = spans[spans.length - 1];\n    if (lastSpan?.parentNode) {\n      lastSpan.parentNode.insertBefore(caret, lastSpan.nextSibling);\n    } else {\n      const lastChild = contentDiv.lastElementChild;\n      if (lastChild) {\n        lastChild.appendChild(caret);\n      } else {\n        contentDiv.appendChild(caret);\n      }\n    }\n  }\n\n  // Usung <span>, not <div> for timestamp \n  // because a block child of a <p> gets re-parented outside of it on re-render (idiomorph).\n  if (showTimestamp && timestampPosition === \"inline\" && message.createdAt) {\n    const timestamp = createTimestamp(message, timestampConfig!, \"span\");\n    timestamp.classList.add(\"persona-timestamp-inline\");\n    const lastBlock = contentDiv.lastElementChild;\n    if (lastBlock) {\n      lastBlock.appendChild(timestamp);\n    } else {\n      contentDiv.appendChild(timestamp);\n    }\n  }\n\n  if (imageParts.length > 0) {\n    const imagePreviews = createMessageImagePreviews(\n      imageParts,\n      !shouldHideTextUntilPreviewFails && Boolean(messageContentText),\n      () => {\n        if (shouldHideTextUntilPreviewFails && textContentDiv) {\n          textContentDiv.style.display = \"\";\n        }\n      }\n    );\n\n    if (imagePreviews) {\n      bubble.appendChild(imagePreviews);\n    } else if (shouldHideTextUntilPreviewFails && textContentDiv) {\n      textContentDiv.style.display = \"\";\n    }\n  }\n\n  const audioParts = getMessageAudioParts(message);\n  if (audioParts.length > 0) {\n    const audioPreviews = createMessageAudioPreviews(audioParts);\n    if (audioPreviews) {\n      bubble.appendChild(audioPreviews);\n    }\n  }\n\n  const videoParts = getMessageVideoParts(message);\n  if (videoParts.length > 0) {\n    const videoPreviews = createMessageVideoPreviews(videoParts);\n    if (videoPreviews) {\n      bubble.appendChild(videoPreviews);\n    }\n  }\n\n  const fileParts = getMessageFileParts(message);\n  if (fileParts.length > 0) {\n    const filePreviews = createMessageFilePreviews(fileParts);\n    if (filePreviews) {\n      bubble.appendChild(filePreviews);\n    }\n  }\n\n  bubble.appendChild(contentDiv);\n\n  // Add timestamp below if configured\n  if (showTimestamp && timestampPosition === \"below\" && message.createdAt) {\n    const timestamp = createTimestamp(message, timestampConfig!);\n    timestamp.classList.add(\"persona-mt-1\");\n    bubble.appendChild(timestamp);\n  }\n\n  // Resolve the stop-reason notice (if any). Only assistant messages can\n  // carry a stop reason worth surfacing.\n  const stopReasonNoticeText =\n    message.role === \"assistant\"\n      ? resolveStopReasonNoticeText(\n          message.stopReason,\n          options?.widgetConfig?.copy?.stopReasonNotice\n        )\n      : null;\n\n  // Add typing indicator (or skeleton placeholder) for streaming assistant\n  // messages. Check the buffered content: a plugin's `bufferContent` may\n  // hold back the first N chars (e.g. glyph-cycle waits for 50 chars), during\n  // which the bubble would otherwise appear empty.\n  //\n  // When the `\"line\"` buffer strategy is paired with the skeleton placeholder,\n  // the skeleton trails below any already-revealed content to hint that more\n  // lines are on the way. It disappears on stream completion.\n  if (message.streaming && message.role === \"assistant\") {\n    const hasVisibleContent = Boolean(bufferedContent && bufferedContent.trim());\n    const skeletonEnabled = streamAnimation.placeholder === \"skeleton\";\n    const trailSkeleton =\n      skeletonEnabled && streamAnimation.buffer === \"line\" && hasVisibleContent;\n    if (!hasVisibleContent) {\n      if (skeletonEnabled) {\n        bubble.appendChild(createSkeletonPlaceholder());\n      } else {\n        const indicator = renderLoadingIndicatorWithFallback(\n          'inline',\n          options?.loadingIndicatorRenderer,\n          options?.widgetConfig\n        );\n        if (indicator) {\n          bubble.appendChild(indicator);\n        }\n      }\n    } else if (trailSkeleton) {\n      bubble.appendChild(createSkeletonPlaceholder());\n    }\n  }\n\n  // Append the stop-reason notice for non-natural completions. When the\n  // assistant produced no text (the `max_tool_calls` empty-bubble symptom),\n  // hide the empty content div so the notice carries the entire bubble\n  // instead of trailing under a blank space.\n  if (stopReasonNoticeText && message.stopReason && !message.streaming) {\n    if (!messageContentText) {\n      contentDiv.style.display = \"none\";\n    }\n    bubble.appendChild(createStopReasonNotice(message.stopReason, stopReasonNoticeText));\n  }\n\n  // Add message actions for assistant messages (only when not streaming and has content)\n  const shouldShowActions = \n    message.role === \"assistant\" && \n    !message.streaming && \n    message.content && \n    message.content.trim() &&\n    actionsConfig?.enabled !== false;\n\n  if (shouldShowActions && actionsConfig) {\n    const actions = createMessageActions(message, actionsConfig, actionCallbacks);\n    bubble.appendChild(actions);\n  }\n\n  // If no avatar needed, return bubble directly\n  if (!showAvatar || message.role === \"system\") {\n    return bubble;\n  }\n\n  // Create wrapper with avatar\n  const wrapper = createElement(\n    \"div\",\n    `persona-flex persona-gap-2 ${message.role === \"user\" ? \"persona-flex-row-reverse\" : \"\"}`\n  );\n\n  const avatar = createAvatar(avatarConfig!, message.role);\n\n  if (avatarPosition === \"right\" || (avatarPosition === \"left\" && message.role === \"user\")) {\n    wrapper.append(bubble, avatar);\n  } else {\n    wrapper.append(avatar, bubble);\n  }\n\n  // Adjust bubble max-width when avatar is present\n  bubble.classList.remove(\"persona-max-w-[85%]\");\n  bubble.classList.add(\"persona-max-w-[calc(85%-2.5rem)]\");\n\n  return wrapper;\n};\n\n/**\n * Create bubble with custom renderer support\n * Uses custom renderer if provided in layout config, otherwise falls back to standard bubble\n */\nexport const createBubbleWithLayout = (\n  message: AgentWidgetMessage,\n  transform: MessageTransform,\n  layoutConfig?: AgentWidgetMessageLayoutConfig,\n  actionsConfig?: AgentWidgetMessageActionsConfig,\n  actionCallbacks?: MessageActionCallbacks,\n  options?: CreateStandardBubbleOptions\n): HTMLElement => {\n  const config = layoutConfig ?? {};\n\n  // Check for custom renderers\n  if (message.role === \"user\" && config.renderUserMessage) {\n    return config.renderUserMessage({\n      message,\n      config: {} as any, // Will be populated by caller\n      streaming: Boolean(message.streaming)\n    });\n  }\n\n  if (message.role === \"assistant\" && config.renderAssistantMessage) {\n    return config.renderAssistantMessage({\n      message,\n      config: {} as any, // Will be populated by caller\n      streaming: Boolean(message.streaming)\n    });\n  }\n\n  // Fall back to standard bubble\n  return createStandardBubble(message, transform, layoutConfig, actionsConfig, actionCallbacks, options);\n};\n","import { createElement } from \"../utils/dom\";\nimport { AgentWidgetConfig, AgentWidgetMessage } from \"../types\";\nimport { describeReasonStatus, computeReasoningElapsed, parseFormattedTemplate } from \"../utils/formatting\";\nimport { renderLucideIcon } from \"../utils/icons\";\n\n// Expansion state per widget instance\nexport const reasoningExpansionState = new Set<string>();\n\nconst appendRenderedValue = (\n  container: HTMLElement,\n  value: HTMLElement | string | null | undefined\n): boolean => {\n  if (value == null) return false;\n  if (typeof value === \"string\") {\n    container.textContent = value;\n    return true;\n  }\n  container.appendChild(value);\n  return true;\n};\n\nconst getReasoningPreviewText = (message: AgentWidgetMessage, maxLines: number): string => {\n  const text = message.reasoning?.chunks.join(\"\").trim() ?? \"\";\n  if (!text) return \"\";\n  return text\n    .split(/\\r?\\n/)\n    .map((line) => line.trim())\n    .filter(Boolean)\n    .slice(0, maxLines)\n    .join(\"\\n\");\n};\n\n// Helper function to update reasoning bubble UI after expansion state changes\nexport const updateReasoningBubbleUI = (messageId: string, bubble: HTMLElement): void => {\n  const expanded = reasoningExpansionState.has(messageId);\n  const header = bubble.querySelector('button[data-expand-header=\"true\"]') as HTMLElement;\n  const content = bubble.querySelector('.persona-border-t') as HTMLElement;\n  const preview = bubble.querySelector('[data-persona-collapsed-preview=\"reasoning\"]') as HTMLElement | null;\n  \n  if (!header || !content) return;\n  \n  header.setAttribute(\"aria-expanded\", expanded ? \"true\" : \"false\");\n  \n  // Find toggle icon container - it's the direct child div of headerMeta (which has persona-ml-auto)\n  const headerMeta = header.querySelector('.persona-ml-auto') as HTMLElement;\n  const toggleIcon = headerMeta?.querySelector(':scope > .persona-flex.persona-items-center') as HTMLElement;\n  if (toggleIcon) {\n    toggleIcon.innerHTML = \"\";\n    const iconColor = \"currentColor\";\n    const chevronIcon = renderLucideIcon(expanded ? \"chevron-up\" : \"chevron-down\", 16, iconColor, 2);\n    if (chevronIcon) {\n      toggleIcon.appendChild(chevronIcon);\n    } else {\n      toggleIcon.textContent = expanded ? \"Hide\" : \"Show\";\n    }\n  }\n  \n  content.style.display = expanded ? \"\" : \"none\";\n  if (preview) {\n    preview.style.display = expanded\n      ? \"none\"\n      : ((preview.textContent || preview.childNodes.length) ? \"\" : \"none\");\n  }\n};\n\nexport const createReasoningBubble = (message: AgentWidgetMessage, config?: AgentWidgetConfig): HTMLElement => {\n  const reasoning = message.reasoning;\n  const bubble = createElement(\n    \"div\",\n    [\n      \"persona-message-bubble\",\n      \"persona-reasoning-bubble\",\n      \"persona-w-full\",\n      \"persona-max-w-[85%]\",\n      \"persona-rounded-2xl\",\n      \"persona-bg-persona-surface\",\n      \"persona-border\",\n      \"persona-border-persona-message-border\",\n      \"persona-text-persona-primary\",\n      \"persona-shadow-sm\",\n      \"persona-overflow-hidden\",\n      \"persona-px-0\",\n      \"persona-py-0\"\n    ].join(\" \")\n  );\n  // Set id for idiomorph matching\n  bubble.id = `bubble-${message.id}`;\n  bubble.setAttribute(\"data-message-id\", message.id);\n\n  if (!reasoning) {\n    return bubble;\n  }\n\n  const reasoningDisplayConfig = config?.features?.reasoningDisplay ?? {};\n  const expandable = reasoningDisplayConfig.expandable !== false;\n  let expanded = expandable && reasoningExpansionState.has(message.id);\n  const isActive = reasoning.status !== \"complete\";\n  const previewText = getReasoningPreviewText(message, reasoningDisplayConfig.previewMaxLines ?? 3);\n  const header = createElement(\n    \"button\",\n    expandable\n      ? \"persona-flex persona-w-full persona-items-center persona-justify-between persona-gap-3 persona-bg-transparent persona-px-4 persona-py-3 persona-text-left persona-cursor-pointer persona-border-none\"\n      : \"persona-flex persona-w-full persona-items-center persona-justify-between persona-gap-3 persona-bg-transparent persona-px-4 persona-py-3 persona-text-left persona-cursor-default persona-border-none\"\n  ) as HTMLButtonElement;\n  header.type = \"button\";\n  if (expandable) {\n    header.setAttribute(\"aria-expanded\", expanded ? \"true\" : \"false\");\n    header.setAttribute(\"data-expand-header\", \"true\");\n  }\n  header.setAttribute(\"data-bubble-type\", \"reasoning\");\n\n  const headerContent = createElement(\"div\", \"persona-flex persona-flex-col persona-text-left\");\n  const title = createElement(\"span\", \"persona-text-xs persona-text-persona-primary\");\n  const defaultSummary = \"Thinking...\";\n  const reasoningConfig = config?.reasoning ?? {};\n\n  // Elapsed helpers: defined early so they're available to renderCollapsedSummary\n  const startedAt = String(reasoning.startedAt ?? Date.now());\n\n  const createElapsedSpan = (): HTMLElement => {\n    const span = createElement(\"span\", \"\");\n    span.setAttribute(\"data-tool-elapsed\", startedAt);\n    span.textContent = computeReasoningElapsed(reasoning);\n    return span;\n  };\n\n  const customSummary = reasoningConfig.renderCollapsedSummary?.({\n    message,\n    reasoning,\n    defaultSummary,\n    previewText,\n    isActive,\n    config: config ?? {},\n    elapsed: computeReasoningElapsed(reasoning),\n    createElapsedElement: createElapsedSpan,\n  });\n  if (typeof customSummary === \"string\" && customSummary.trim()) {\n    title.textContent = customSummary;\n    headerContent.appendChild(title);\n  } else if (customSummary instanceof HTMLElement) {\n    headerContent.appendChild(customSummary);\n  } else {\n    title.textContent = defaultSummary;\n    headerContent.appendChild(title);\n  }\n\n  // Status span: used in the legacy (no-template) path\n  const status = createElement(\"span\", \"persona-text-xs persona-text-persona-primary\");\n  status.textContent = describeReasonStatus(reasoning);\n  headerContent.appendChild(status);\n\n  // Template and animation support\n  const loadingAnimation = reasoningDisplayConfig.loadingAnimation ?? \"none\";\n  const activeTemplate = reasoningConfig.activeTextTemplate;\n  const completeTemplate = reasoningConfig.completeTextTemplate;\n  const currentTemplate = isActive ? activeTemplate : completeTemplate;\n  const skipCustomElement = customSummary instanceof HTMLElement;\n\n  // Helper: append text as individual animated character spans\n  const appendCharSpans = (container: HTMLElement, text: string, startIndex: number): number => {\n    let idx = startIndex;\n    for (const char of text) {\n      const span = createElement(\"span\", \"persona-tool-char\");\n      span.style.setProperty(\"--char-index\", String(idx));\n      span.textContent = char === \" \" ? \"\\u00A0\" : char;\n      container.appendChild(span);\n      idx++;\n    }\n    return idx;\n  };\n\n  /**\n   * Renders a template into the title element, handling:\n   * - Inline formatting markers: **bold**, *italic*, ~dim~\n   * - {duration} as a live-updating elapsed span (active) or static text (complete)\n   * - Character-by-character animation wrapping when `animated` is true\n   */\n  const renderFormattedTitle = (template: string, animated: boolean) => {\n    title.textContent = \"\";\n    const segments = parseFormattedTemplate(template, \"\");\n    let charIndex = 0;\n\n    for (const seg of segments) {\n      const parent = seg.styles.length > 0\n        ? (() => {\n            const w = createElement(\"span\", seg.styles.map(s => `persona-tool-text-${s}`).join(\" \"));\n            title.appendChild(w);\n            return w;\n          })()\n        : title;\n\n      if (seg.isDuration && isActive) {\n        parent.appendChild(createElapsedSpan());\n      } else {\n        const text = seg.isDuration ? computeReasoningElapsed(reasoning) : seg.text;\n        if (animated) {\n          charIndex = appendCharSpans(parent, text, charIndex);\n        } else {\n          parent.appendChild(document.createTextNode(text));\n        }\n      }\n    }\n  };\n\n  // Apply template + animation, or fall back to legacy title/status approach\n  if (!skipCustomElement && currentTemplate) {\n    // Template mode: unified title replaces separate title/status spans\n    status.style.display = \"none\";\n    title.style.display = \"\";\n\n    if (isActive && loadingAnimation !== \"none\") {\n      const animDuration = reasoningConfig.loadingAnimationDuration ?? 2000;\n      title.setAttribute(\"data-preserve-animation\", \"true\");\n\n      if (loadingAnimation === \"pulse\") {\n        title.classList.add(\"persona-tool-loading-pulse\");\n        title.style.setProperty(\"--persona-tool-anim-duration\", `${animDuration}ms`);\n        renderFormattedTitle(currentTemplate, false);\n      } else {\n        title.classList.add(`persona-tool-loading-${loadingAnimation}`);\n        title.style.setProperty(\"--persona-tool-anim-duration\", `${animDuration}ms`);\n\n        if (loadingAnimation === \"shimmer-color\") {\n          if (reasoningConfig.loadingAnimationColor) {\n            title.style.setProperty(\"--persona-tool-anim-color\", reasoningConfig.loadingAnimationColor);\n          }\n          if (reasoningConfig.loadingAnimationSecondaryColor) {\n            title.style.setProperty(\"--persona-tool-anim-secondary-color\", reasoningConfig.loadingAnimationSecondaryColor);\n          }\n        }\n\n        renderFormattedTitle(currentTemplate, true);\n      }\n    } else {\n      renderFormattedTitle(currentTemplate, false);\n    }\n  } else if (!skipCustomElement && isActive && loadingAnimation !== \"none\") {\n    // Animation without template: animate the default \"Thinking...\" text\n    title.style.display = \"\";\n    const animDuration = reasoningConfig.loadingAnimationDuration ?? 2000;\n    title.setAttribute(\"data-preserve-animation\", \"true\");\n\n    if (loadingAnimation === \"pulse\") {\n      title.classList.add(\"persona-tool-loading-pulse\");\n      title.style.setProperty(\"--persona-tool-anim-duration\", `${animDuration}ms`);\n    } else {\n      title.classList.add(`persona-tool-loading-${loadingAnimation}`);\n      title.style.setProperty(\"--persona-tool-anim-duration\", `${animDuration}ms`);\n\n      if (loadingAnimation === \"shimmer-color\") {\n        if (reasoningConfig.loadingAnimationColor) {\n          title.style.setProperty(\"--persona-tool-anim-color\", reasoningConfig.loadingAnimationColor);\n        }\n        if (reasoningConfig.loadingAnimationSecondaryColor) {\n          title.style.setProperty(\"--persona-tool-anim-secondary-color\", reasoningConfig.loadingAnimationSecondaryColor);\n        }\n      }\n\n      const text = title.textContent || defaultSummary;\n      title.textContent = \"\";\n      appendCharSpans(title, text, 0);\n    }\n\n    // Legacy: hide title on complete, show status\n    if (reasoning.status === \"complete\") {\n      title.style.display = \"none\";\n    }\n  } else if (!skipCustomElement) {\n    // Legacy path: no template, no animation\n    if (reasoning.status === \"complete\") {\n      title.style.display = \"none\";\n    } else {\n      title.style.display = \"\";\n    }\n  }\n\n  let toggleIcon: HTMLElement | null = null;\n  if (expandable) {\n    toggleIcon = createElement(\"div\", \"persona-flex persona-items-center\");\n    const iconColor = \"currentColor\";\n    const chevronIcon = renderLucideIcon(expanded ? \"chevron-up\" : \"chevron-down\", 16, iconColor, 2);\n    if (chevronIcon) {\n      toggleIcon.appendChild(chevronIcon);\n    } else {\n      toggleIcon.textContent = expanded ? \"Hide\" : \"Show\";\n    }\n\n    const headerMeta = createElement(\"div\", \"persona-flex persona-items-center persona-ml-auto\");\n    headerMeta.append(toggleIcon);\n    header.append(headerContent, headerMeta);\n  } else {\n    header.append(headerContent);\n  }\n\n  const collapsedPreview = createElement(\n    \"div\",\n    \"persona-px-4 persona-py-3 persona-text-xs persona-leading-snug persona-text-persona-muted\"\n  );\n  collapsedPreview.setAttribute(\"data-persona-collapsed-preview\", \"reasoning\");\n  collapsedPreview.style.display = \"none\";\n  collapsedPreview.style.whiteSpace = \"pre-wrap\";\n\n  if (!expanded && isActive && reasoningDisplayConfig.activePreview && previewText) {\n    const renderedPreview = config?.reasoning?.renderCollapsedPreview?.({\n      message,\n      reasoning,\n      defaultPreview: previewText,\n      isActive,\n      config: config ?? {},\n    });\n    if (!appendRenderedValue(collapsedPreview, renderedPreview)) {\n      collapsedPreview.textContent = previewText;\n    }\n    collapsedPreview.style.display = \"\";\n  }\n\n  if (!expanded && isActive && reasoningDisplayConfig.activeMinHeight) {\n    bubble.style.minHeight = reasoningDisplayConfig.activeMinHeight;\n  }\n\n  if (!expandable) {\n    bubble.append(header, collapsedPreview);\n    return bubble;\n  }\n\n  const content = createElement(\n    \"div\",\n    \"persona-border-t persona-border-gray-200 persona-bg-gray-50 persona-px-4 persona-py-3\"\n  );\n  content.style.display = expanded ? \"\" : \"none\";\n\n  const text = reasoning.chunks.join(\"\");\n  const body = createElement(\n    \"div\",\n    \"persona-whitespace-pre-wrap persona-text-xs persona-leading-snug persona-text-persona-muted\"\n  );\n  body.textContent =\n    text ||\n    (reasoning.status === \"complete\"\n      ? \"No additional context was shared.\"\n      : \"Waiting for details…\");\n  content.appendChild(body);\n\n  const applyExpansionState = () => {\n    header.setAttribute(\"aria-expanded\", expanded ? \"true\" : \"false\");\n    if (toggleIcon) {\n      toggleIcon.innerHTML = \"\";\n      const iconColor = \"currentColor\";\n      const chevronIcon = renderLucideIcon(expanded ? \"chevron-up\" : \"chevron-down\", 16, iconColor, 2);\n      if (chevronIcon) {\n        toggleIcon.appendChild(chevronIcon);\n      } else {\n        toggleIcon.textContent = expanded ? \"Hide\" : \"Show\";\n      }\n    }\n    content.style.display = expanded ? \"\" : \"none\";\n    collapsedPreview.style.display = expanded ? \"none\" : ((collapsedPreview.textContent || collapsedPreview.childNodes.length) ? \"\" : \"none\");\n  };\n\n  applyExpansionState();\n\n  bubble.append(header, collapsedPreview, content);\n  return bubble;\n};\n\n\n\n","import { createElement } from \"../utils/dom\";\nimport { AgentWidgetMessage, AgentWidgetConfig } from \"../types\";\nimport { formatUnknownValue, describeToolTitle, resolveToolHeaderText, computeToolElapsed, parseFormattedTemplate } from \"../utils/formatting\";\nimport { renderLucideIcon } from \"../utils/icons\";\n\n// Expansion state per widget instance\nexport const toolExpansionState = new Set<string>();\n\n\nconst appendRenderedValue = (\n  container: HTMLElement,\n  value: HTMLElement | string | null | undefined\n): boolean => {\n  if (value == null) return false;\n  if (typeof value === \"string\") {\n    container.textContent = value;\n    return true;\n  }\n  container.appendChild(value);\n  return true;\n};\n\nconst getToolPreviewText = (message: AgentWidgetMessage, maxLines: number): string => {\n  const tool = message.toolCall;\n  if (!tool) return \"\";\n\n  const chunkText = (tool.chunks ?? []).join(\"\").trim();\n  if (chunkText) {\n    const lines = chunkText\n      .split(/\\r?\\n/)\n      .map((line) => line.trim())\n      .filter(Boolean)\n      .slice(-maxLines);\n    return lines.join(\"\\n\");\n  }\n\n  const argsText = formatUnknownValue(tool.args).trim();\n  if (!argsText) return \"\";\n\n  return argsText\n    .split(/\\r?\\n/)\n    .map((line) => line.trim())\n    .filter(Boolean)\n    .slice(0, maxLines)\n    .join(\"\\n\");\n};\n\n/**\n * Apply the colors for a tool-bubble code block (Arguments / Activity / Result).\n *\n * Defaults are theme-aware tokens so the blocks stay readable in dark themes.\n */\nconst applyToolCodeBlockColors = (\n  pre: HTMLElement,\n  toolCallConfig: NonNullable<AgentWidgetConfig[\"toolCall\"]>\n): void => {\n  pre.style.backgroundColor =\n    toolCallConfig.codeBlockBackgroundColor ?? \"var(--persona-container, #f3f4f6)\";\n  pre.style.borderColor =\n    toolCallConfig.codeBlockBorderColor ?? \"var(--persona-border, #e5e7eb)\";\n  pre.style.color = toolCallConfig.codeBlockTextColor ?? \"var(--persona-text, #171717)\";\n};\n\nconst getToolSummaryText = (\n  message: AgentWidgetMessage,\n  config?: AgentWidgetConfig\n): { summary: string; previewText: string; isActive: boolean } => {\n  const tool = message.toolCall;\n  const toolDisplayConfig = config?.features?.toolCallDisplay;\n  const collapsedMode = toolDisplayConfig?.collapsedMode ?? \"tool-call\";\n  const previewText = getToolPreviewText(message, toolDisplayConfig?.previewMaxLines ?? 3);\n  const defaultSummary = tool ? describeToolTitle(tool) : \"\";\n\n  if (!tool) {\n    return { summary: defaultSummary, previewText, isActive: false };\n  }\n\n  const isActive = tool.status !== \"complete\";\n  const toolCallConfig = config?.toolCall ?? {};\n  let summary = defaultSummary;\n  if (collapsedMode === \"tool-name\") {\n    summary = tool.name?.trim() || defaultSummary;\n  } else if (collapsedMode === \"tool-preview\" && previewText) {\n    summary = previewText;\n  }\n\n  // Apply text templates if configured\n  if (isActive && toolCallConfig.activeTextTemplate) {\n    summary = resolveToolHeaderText(tool, toolCallConfig.activeTextTemplate, summary);\n  } else if (!isActive && toolCallConfig.completeTextTemplate) {\n    summary = resolveToolHeaderText(tool, toolCallConfig.completeTextTemplate, summary);\n  }\n\n  return { summary, previewText, isActive };\n};\n\n// Helper function to update tool bubble UI after expansion state changes\nexport const updateToolBubbleUI = (messageId: string, bubble: HTMLElement, config?: AgentWidgetConfig): void => {\n  const expanded = toolExpansionState.has(messageId);\n  const toolCallConfig = config?.toolCall ?? {};\n  const header = bubble.querySelector('button[data-expand-header=\"true\"]') as HTMLElement;\n  const content = bubble.querySelector('.persona-border-t') as HTMLElement;\n  const preview = bubble.querySelector('[data-persona-collapsed-preview=\"tool\"]') as HTMLElement | null;\n  \n  if (!header || !content) return;\n  \n  header.setAttribute(\"aria-expanded\", expanded ? \"true\" : \"false\");\n  \n  // Find toggle icon container - it's the direct child div of headerMeta (which has persona-ml-auto)\n  const headerMeta = header.querySelector('.persona-ml-auto') as HTMLElement;\n  const toggleIcon = headerMeta?.querySelector(':scope > .persona-flex.persona-items-center') as HTMLElement;\n  if (toggleIcon) {\n    toggleIcon.innerHTML = \"\";\n    // Default the toggle chevron to the tool-call title color so it stays\n    // readable on whatever surface the title does. The title falls back to\n    // `.persona-text-persona-primary` (var(--persona-primary)) when no\n    // `headerTextColor` is set, so mirror that here instead of `currentColor`.\n    const iconColor =\n      toolCallConfig.toggleTextColor ||\n      toolCallConfig.headerTextColor ||\n      \"var(--persona-primary, #171717)\";\n    const chevronIcon = renderLucideIcon(expanded ? \"chevron-up\" : \"chevron-down\", 16, iconColor, 2);\n    if (chevronIcon) {\n      toggleIcon.appendChild(chevronIcon);\n    } else {\n      toggleIcon.textContent = expanded ? \"Hide\" : \"Show\";\n    }\n  }\n  \n  content.style.display = expanded ? \"\" : \"none\";\n  if (preview) {\n    preview.style.display = expanded\n      ? \"none\"\n      : ((preview.textContent || preview.childNodes.length) ? \"\" : \"none\");\n  }\n};\n\nexport const createToolBubble = (message: AgentWidgetMessage, config?: AgentWidgetConfig): HTMLElement => {\n  const tool = message.toolCall;\n  const toolCallConfig = config?.toolCall ?? {};\n  \n  const bubble = createElement(\n    \"div\",\n    [\n      \"persona-message-bubble\",\n      \"persona-tool-bubble\",\n      \"persona-w-full\",\n      \"persona-max-w-[85%]\",\n      \"persona-rounded-2xl\",\n      \"persona-bg-persona-surface\",\n      \"persona-border\",\n      \"persona-border-persona-message-border\",\n      \"persona-text-persona-primary\",\n      \"persona-shadow-sm\",\n      \"persona-overflow-hidden\",\n      \"persona-px-0\",\n      \"persona-py-0\"\n    ].join(\" \")\n  );\n  // Set id for idiomorph matching\n  bubble.id = `bubble-${message.id}`;\n  bubble.setAttribute(\"data-message-id\", message.id);\n\n  // Apply bubble-level styles\n  if (toolCallConfig.backgroundColor) {\n    bubble.style.backgroundColor = toolCallConfig.backgroundColor;\n  }\n  if (toolCallConfig.borderColor) {\n    bubble.style.borderColor = toolCallConfig.borderColor;\n  }\n  if (toolCallConfig.borderWidth) {\n    bubble.style.borderWidth = toolCallConfig.borderWidth;\n  }\n  if (toolCallConfig.borderRadius) {\n    bubble.style.borderRadius = toolCallConfig.borderRadius;\n  }\n  bubble.style.boxShadow =\n    toolCallConfig.shadow !== undefined\n      ? (toolCallConfig.shadow.trim() === \"\" ? \"none\" : toolCallConfig.shadow)\n      : \"var(--persona-tool-bubble-shadow, 0 5px 15px rgba(15, 23, 42, 0.08))\";\n\n  if (!tool) {\n    return bubble;\n  }\n\n  const toolDisplayConfig = config?.features?.toolCallDisplay ?? {};\n  const expandable = toolDisplayConfig.expandable !== false;\n  let expanded = expandable && toolExpansionState.has(message.id);\n  const { summary, previewText, isActive } = getToolSummaryText(message, config);\n\n  const header = createElement(\n    \"button\",\n    expandable\n      ? \"persona-flex persona-w-full persona-items-center persona-justify-between persona-gap-3 persona-bg-transparent persona-px-4 persona-py-3 persona-text-left persona-cursor-pointer persona-border-none\"\n      : \"persona-flex persona-w-full persona-items-center persona-justify-between persona-gap-3 persona-bg-transparent persona-px-4 persona-py-3 persona-text-left persona-cursor-default persona-border-none\"\n  ) as HTMLButtonElement;\n  header.type = \"button\";\n  if (expandable) {\n    header.setAttribute(\"aria-expanded\", expanded ? \"true\" : \"false\");\n    header.setAttribute(\"data-expand-header\", \"true\");\n  }\n  header.setAttribute(\"data-bubble-type\", \"tool\");\n\n  // Apply header styles\n  if (toolCallConfig.headerBackgroundColor) {\n    header.style.backgroundColor = toolCallConfig.headerBackgroundColor;\n  }\n  if (toolCallConfig.headerPaddingX) {\n    header.style.paddingLeft = toolCallConfig.headerPaddingX;\n    header.style.paddingRight = toolCallConfig.headerPaddingX;\n  }\n  if (toolCallConfig.headerPaddingY) {\n    header.style.paddingTop = toolCallConfig.headerPaddingY;\n    header.style.paddingBottom = toolCallConfig.headerPaddingY;\n  }\n\n  const headerContent = createElement(\"div\", \"persona-flex persona-flex-col persona-text-left\");\n  const title = createElement(\"span\", \"persona-text-xs persona-text-persona-primary\");\n  if (toolCallConfig.headerTextColor) {\n    title.style.color = toolCallConfig.headerTextColor;\n  }\n\n  // Elapsed helpers: defined early so they're available to renderCollapsedSummary\n  const startedAt = String(tool.startedAt ?? Date.now());\n\n  // Helper: build a <span data-tool-elapsed> that the global timer in ui.ts updates\n  const createElapsedSpan = (): HTMLElement => {\n    const span = createElement(\"span\", \"\");\n    span.setAttribute(\"data-tool-elapsed\", startedAt);\n    span.textContent = computeToolElapsed(tool);\n    return span;\n  };\n\n  const customSummary = toolCallConfig.renderCollapsedSummary?.({\n    message,\n    toolCall: tool,\n    defaultSummary: summary,\n    previewText,\n    collapsedMode: toolDisplayConfig.collapsedMode ?? \"tool-call\",\n    isActive,\n    config: config ?? {},\n    elapsed: computeToolElapsed(tool),\n    createElapsedElement: createElapsedSpan,\n  });\n  if (typeof customSummary === \"string\" && customSummary.trim()) {\n    title.textContent = customSummary;\n    headerContent.appendChild(title);\n  } else if (customSummary instanceof HTMLElement) {\n    headerContent.appendChild(customSummary);\n  } else {\n    title.textContent = summary;\n    headerContent.appendChild(title);\n  }\n\n  // Apply loading animation when tool is active and no custom HTMLElement was provided\n  const loadingAnimation = toolDisplayConfig.loadingAnimation ?? \"none\";\n  const activeTemplate = toolCallConfig.activeTextTemplate;\n  const completeTemplate = toolCallConfig.completeTextTemplate;\n  const currentTemplate = isActive ? activeTemplate : completeTemplate;\n  const skipCustomElement = customSummary instanceof HTMLElement;\n\n  // Helper: append text as individual animated character spans\n  const appendCharSpans = (container: HTMLElement, text: string, startIndex: number): number => {\n    let idx = startIndex;\n    for (const char of text) {\n      const span = createElement(\"span\", \"persona-tool-char\");\n      span.style.setProperty(\"--char-index\", String(idx));\n      span.textContent = char === \" \" ? \"\\u00A0\" : char;\n      container.appendChild(span);\n      idx++;\n    }\n    return idx;\n  };\n\n  /**\n   * Renders a template into the title element, handling:\n   * - Inline formatting markers: **bold**, *italic*, ~dim~\n   * - {duration} as a live-updating elapsed span (active) or static text (complete)\n   * - Character-by-character animation wrapping when `animated` is true\n   */\n  const renderFormattedTitle = (template: string, animated: boolean) => {\n    title.textContent = \"\";\n    const toolName = tool.name?.trim() || \"tool\";\n    const segments = parseFormattedTemplate(template, toolName);\n    let charIndex = 0;\n\n    for (const seg of segments) {\n      // Determine parent: wrap in a styled span if formatting is present\n      const parent = seg.styles.length > 0\n        ? (() => {\n            const w = createElement(\"span\", seg.styles.map(s => `persona-tool-text-${s}`).join(\" \"));\n            title.appendChild(w);\n            return w;\n          })()\n        : title;\n\n      if (seg.isDuration && isActive) {\n        // Live-updating elapsed span for active tools\n        parent.appendChild(createElapsedSpan());\n      } else {\n        // Static text (or resolved duration for completed tools)\n        const text = seg.isDuration ? computeToolElapsed(tool) : seg.text;\n        if (animated) {\n          charIndex = appendCharSpans(parent, text, charIndex);\n        } else {\n          parent.appendChild(document.createTextNode(text));\n        }\n      }\n    }\n  };\n\n  if (!skipCustomElement) {\n    if (isActive && loadingAnimation !== \"none\") {\n      const animDuration = toolCallConfig.loadingAnimationDuration ?? 2000;\n      title.setAttribute(\"data-preserve-animation\", \"true\");\n\n      if (loadingAnimation === \"pulse\") {\n        title.classList.add(\"persona-tool-loading-pulse\");\n        title.style.setProperty(\"--persona-tool-anim-duration\", `${animDuration}ms`);\n        if (currentTemplate) {\n          renderFormattedTitle(currentTemplate, false);\n        }\n      } else {\n        // Character-by-character modes: shimmer, shimmer-color, rainbow\n        title.classList.add(`persona-tool-loading-${loadingAnimation}`);\n        title.style.setProperty(\"--persona-tool-anim-duration\", `${animDuration}ms`);\n\n        if (loadingAnimation === \"shimmer-color\") {\n          if (toolCallConfig.loadingAnimationColor) {\n            title.style.setProperty(\"--persona-tool-anim-color\", toolCallConfig.loadingAnimationColor);\n          }\n          if (toolCallConfig.loadingAnimationSecondaryColor) {\n            title.style.setProperty(\"--persona-tool-anim-secondary-color\", toolCallConfig.loadingAnimationSecondaryColor);\n          }\n        }\n\n        if (currentTemplate) {\n          renderFormattedTitle(currentTemplate, true);\n        } else {\n          const text = title.textContent || summary;\n          title.textContent = \"\";\n          appendCharSpans(title, text, 0);\n        }\n      }\n    } else if (currentTemplate) {\n      // Template with formatting but no animation (or completed tool)\n      renderFormattedTitle(currentTemplate, false);\n    }\n  }\n\n  let toggleIcon: HTMLElement | null = null;\n  if (expandable) {\n    toggleIcon = createElement(\"div\", \"persona-flex persona-items-center\");\n    // Default the toggle chevron to the tool-call title color so it stays\n    // readable on whatever surface the title does. The title falls back to\n    // `.persona-text-persona-primary` (var(--persona-primary)) when no\n    // `headerTextColor` is set, so mirror that here instead of `currentColor`.\n    const iconColor =\n      toolCallConfig.toggleTextColor ||\n      toolCallConfig.headerTextColor ||\n      \"var(--persona-primary, #171717)\";\n    const chevronIcon = renderLucideIcon(expanded ? \"chevron-up\" : \"chevron-down\", 16, iconColor, 2);\n    if (chevronIcon) {\n      toggleIcon.appendChild(chevronIcon);\n    } else {\n      toggleIcon.textContent = expanded ? \"Hide\" : \"Show\";\n    }\n\n    const headerMeta = createElement(\"div\", \"persona-flex persona-items-center persona-gap-2 persona-ml-auto\");\n    headerMeta.append(toggleIcon);\n    header.append(headerContent, headerMeta);\n  } else {\n    header.append(headerContent);\n  }\n\n  const collapsedPreview = createElement(\n    \"div\",\n    \"persona-px-4 persona-py-3 persona-text-xs persona-leading-snug persona-text-persona-muted\"\n  );\n  collapsedPreview.setAttribute(\"data-persona-collapsed-preview\", \"tool\");\n  collapsedPreview.style.display = \"none\";\n  collapsedPreview.style.whiteSpace = \"pre-wrap\";\n\n  if (\n    !expanded &&\n    isActive &&\n    toolDisplayConfig.activePreview &&\n    previewText\n  ) {\n    const renderedPreview = toolCallConfig.renderCollapsedPreview?.({\n      message,\n      toolCall: tool,\n      defaultPreview: previewText,\n      isActive,\n      config: config ?? {},\n    });\n    if (!appendRenderedValue(collapsedPreview, renderedPreview)) {\n      collapsedPreview.textContent = previewText;\n    }\n    collapsedPreview.style.display = \"\";\n  }\n\n  if (!expanded && isActive && toolDisplayConfig.activeMinHeight) {\n    bubble.style.minHeight = toolDisplayConfig.activeMinHeight;\n  }\n\n  if (!expandable) {\n    bubble.append(header, collapsedPreview);\n    return bubble;\n  }\n\n  const content = createElement(\n    \"div\",\n    \"persona-border-t persona-border-gray-200 persona-bg-gray-50 persona-space-y-3 persona-px-4 persona-py-3\"\n  );\n  content.style.display = expanded ? \"\" : \"none\";\n\n  // Apply content styles\n  if (toolCallConfig.contentBackgroundColor) {\n    content.style.backgroundColor = toolCallConfig.contentBackgroundColor;\n  }\n  if (toolCallConfig.contentTextColor) {\n    content.style.color = toolCallConfig.contentTextColor;\n  }\n  if (toolCallConfig.contentPaddingX) {\n    content.style.paddingLeft = toolCallConfig.contentPaddingX;\n    content.style.paddingRight = toolCallConfig.contentPaddingX;\n  }\n  if (toolCallConfig.contentPaddingY) {\n    content.style.paddingTop = toolCallConfig.contentPaddingY;\n    content.style.paddingBottom = toolCallConfig.contentPaddingY;\n  }\n\n  // Add tool name at the top of content\n  if (tool.name) {\n    const toolName = createElement(\"div\", \"persona-text-xs persona-text-persona-muted persona-italic\");\n    if (toolCallConfig.contentTextColor) {\n      toolName.style.color = toolCallConfig.contentTextColor;\n    } else if (toolCallConfig.headerTextColor) {\n      toolName.style.color = toolCallConfig.headerTextColor;\n    }\n    toolName.textContent = tool.name;\n    content.appendChild(toolName);\n  }\n\n  if (tool.args !== undefined) {\n    const argsBlock = createElement(\"div\", \"persona-space-y-1\");\n    const argsLabel = createElement(\n      \"div\",\n      \"persona-text-xs persona-text-persona-muted\"\n    );\n    if (toolCallConfig.labelTextColor) {\n      argsLabel.style.color = toolCallConfig.labelTextColor;\n    }\n    argsLabel.textContent = \"Arguments\";\n    const argsPre = createElement(\n      \"pre\",\n      \"persona-max-h-48 persona-overflow-auto persona-whitespace-pre-wrap persona-rounded-lg persona-border persona-px-3 persona-py-2 persona-text-xs\"\n    );\n    // Ensure font size matches header text (0.75rem / 12px)\n    argsPre.style.fontSize = \"0.75rem\";\n    argsPre.style.lineHeight = \"1rem\";\n    applyToolCodeBlockColors(argsPre, toolCallConfig);\n    argsPre.textContent = formatUnknownValue(tool.args);\n    argsBlock.append(argsLabel, argsPre);\n    content.appendChild(argsBlock);\n  }\n\n  if (tool.chunks && tool.chunks.length) {\n    const logsBlock = createElement(\"div\", \"persona-space-y-1\");\n    const logsLabel = createElement(\n      \"div\",\n      \"persona-text-xs persona-text-persona-muted\"\n    );\n    if (toolCallConfig.labelTextColor) {\n      logsLabel.style.color = toolCallConfig.labelTextColor;\n    }\n    logsLabel.textContent = \"Activity\";\n    const logsPre = createElement(\n      \"pre\",\n      \"persona-max-h-48 persona-overflow-auto persona-whitespace-pre-wrap persona-rounded-lg persona-border persona-px-3 persona-py-2 persona-text-xs\"\n    );\n    // Ensure font size matches header text (0.75rem / 12px)\n    logsPre.style.fontSize = \"0.75rem\";\n    logsPre.style.lineHeight = \"1rem\";\n    applyToolCodeBlockColors(logsPre, toolCallConfig);\n    logsPre.textContent = tool.chunks.join(\"\");\n    logsBlock.append(logsLabel, logsPre);\n    content.appendChild(logsBlock);\n  }\n\n  if (tool.status === \"complete\" && tool.result !== undefined) {\n    const resultBlock = createElement(\"div\", \"persona-space-y-1\");\n    const resultLabel = createElement(\n      \"div\",\n      \"persona-text-xs persona-text-persona-muted\"\n    );\n    if (toolCallConfig.labelTextColor) {\n      resultLabel.style.color = toolCallConfig.labelTextColor;\n    }\n    resultLabel.textContent = \"Result\";\n    const resultPre = createElement(\n      \"pre\",\n      \"persona-max-h-48 persona-overflow-auto persona-whitespace-pre-wrap persona-rounded-lg persona-border persona-px-3 persona-py-2 persona-text-xs\"\n    );\n    // Ensure font size matches header text (0.75rem / 12px)\n    resultPre.style.fontSize = \"0.75rem\";\n    resultPre.style.lineHeight = \"1rem\";\n    applyToolCodeBlockColors(resultPre, toolCallConfig);\n    resultPre.textContent = formatUnknownValue(tool.result);\n    resultBlock.append(resultLabel, resultPre);\n    content.appendChild(resultBlock);\n  }\n\n  if (tool.status === \"complete\" && typeof tool.duration === \"number\") {\n    const duration = createElement(\n      \"div\",\n      \"persona-text-xs persona-text-persona-muted\"\n    );\n    if (toolCallConfig.contentTextColor) {\n      duration.style.color = toolCallConfig.contentTextColor;\n    }\n    duration.textContent = `Duration: ${tool.duration}ms`;\n    content.appendChild(duration);\n  }\n\n  const applyToolExpansion = () => {\n    header.setAttribute(\"aria-expanded\", expanded ? \"true\" : \"false\");\n    if (toggleIcon) {\n      toggleIcon.innerHTML = \"\";\n      // Default the toggle chevron to the tool-call title color so it stays\n      // readable on whatever surface the title does. The title falls back to\n      // `.persona-text-persona-primary` (var(--persona-primary)) when no\n      // `headerTextColor` is set, so mirror that here instead of `currentColor`.\n      const iconColor =\n        toolCallConfig.toggleTextColor ||\n        toolCallConfig.headerTextColor ||\n        \"var(--persona-primary, #171717)\";\n      const chevronIcon = renderLucideIcon(expanded ? \"chevron-up\" : \"chevron-down\", 16, iconColor, 2);\n      if (chevronIcon) {\n        toggleIcon.appendChild(chevronIcon);\n      } else {\n        toggleIcon.textContent = expanded ? \"Hide\" : \"Show\";\n      }\n    }\n    content.style.display = expanded ? \"\" : \"none\";\n    collapsedPreview.style.display = expanded\n      ? \"none\"\n      : ((collapsedPreview.textContent || collapsedPreview.childNodes.length) ? \"\" : \"none\");\n  };\n\n  applyToolExpansion();\n\n  bubble.append(header, collapsedPreview, content);\n  return bubble;\n};\n\n\n\n","import { createElement } from \"../utils/dom\";\nimport { AgentWidgetMessage, AgentWidgetConfig } from \"../types\";\nimport { formatUnknownValue } from \"../utils/formatting\";\nimport { renderLucideIcon } from \"../utils/icons\";\nimport { WEBMCP_TOOL_PREFIX, getWebMcpToolDisplayTitle } from \"../webmcp-bridge\";\n\n/**\n * Per-message expanded/collapsed state for the technical-details section.\n * Absent means \"use the config default\" (`approval.detailsDisplay`). Lives at\n * module scope so the choice survives idiomorph re-renders, mirroring\n * `toolExpansionState` in tool-bubble.ts.\n */\nexport const approvalDetailsExpansionState = new Map<string, boolean>();\n\n/**\n * Turn a wire tool name into a user-facing label: strips the `webmcp:`\n * prefix and splits snake_case / kebab-case / camelCase into a sentence\n * (`add_to_cart` → \"Add to cart\"). Falls back to the input when nothing\n * word-like remains.\n */\nexport const humanizeToolName = (toolName: string): string => {\n  const bare = toolName.startsWith(WEBMCP_TOOL_PREFIX)\n    ? toolName.slice(WEBMCP_TOOL_PREFIX.length)\n    : toolName;\n  const words = bare\n    .replace(/([a-z0-9])([A-Z])/g, \"$1 $2\")\n    .split(/[_\\-\\s.]+/)\n    .filter(Boolean);\n  if (words.length === 0) return toolName;\n  const sentence = words.join(\" \").toLowerCase();\n  return sentence.charAt(0).toUpperCase() + sentence.slice(1);\n};\n\nconst resolveApprovalConfig = (config?: AgentWidgetConfig) =>\n  config?.approval !== false ? config?.approval : undefined;\n\nconst isDetailsExpanded = (\n  messageId: string,\n  config?: AgentWidgetConfig\n): boolean => {\n  const detailsMode = resolveApprovalConfig(config)?.detailsDisplay ?? \"collapsed\";\n  return approvalDetailsExpansionState.get(messageId) ?? detailsMode === \"expanded\";\n};\n\nconst applyDetailsToggleState = (\n  toggle: HTMLElement,\n  expanded: boolean,\n  config?: AgentWidgetConfig\n): void => {\n  const approvalConfig = resolveApprovalConfig(config);\n  toggle.setAttribute(\"aria-expanded\", expanded ? \"true\" : \"false\");\n  const label = toggle.querySelector(\"[data-approval-details-label]\") as HTMLElement | null;\n  if (label) {\n    label.textContent = expanded\n      ? (approvalConfig?.hideDetailsLabel ?? \"Hide details\")\n      : (approvalConfig?.showDetailsLabel ?? \"Show details\");\n  }\n  const chevronHolder = toggle.querySelector(\"[data-approval-details-chevron]\") as HTMLElement | null;\n  if (chevronHolder) {\n    chevronHolder.innerHTML = \"\";\n    const chevron = renderLucideIcon(expanded ? \"chevron-up\" : \"chevron-down\", 14, \"currentColor\", 2);\n    if (chevron) {\n      chevronHolder.appendChild(chevron);\n    }\n  }\n};\n\n/**\n * Sync the technical-details section (toggle label/chevron + visibility) with\n * `approvalDetailsExpansionState`. Called from the ui.ts expansion event\n * delegation after a toggle click.\n */\nexport const updateApprovalDetailsUI = (\n  messageId: string,\n  bubble: HTMLElement,\n  config?: AgentWidgetConfig\n): void => {\n  const toggle = bubble.querySelector('button[data-bubble-type=\"approval\"]') as HTMLElement | null;\n  const details = bubble.querySelector(\"[data-approval-details]\") as HTMLElement | null;\n  if (!toggle || !details) return;\n  const expanded = isDetailsExpanded(messageId, config);\n  applyDetailsToggleState(toggle, expanded, config);\n  details.style.display = expanded ? \"\" : \"none\";\n};\n\n/**\n * Update an existing approval bubble's UI after status changes.\n */\nexport const updateApprovalBubbleUI = (\n  _messageId: string,\n  bubble: HTMLElement,\n  config?: AgentWidgetConfig,\n  approval?: AgentWidgetMessage[\"approval\"]\n): void => {\n  if (!approval) return;\n\n  const approvalConfig = config?.approval !== false ? config?.approval : undefined;\n\n  // Update status badge\n  const statusBadge = bubble.querySelector('[data-approval-status]') as HTMLElement;\n  if (statusBadge) {\n    statusBadge.textContent = approval.status === \"approved\" ? \"Approved\"\n      : approval.status === \"denied\" ? \"Denied\"\n      : approval.status === \"timeout\" ? \"Timeout\"\n      : \"Pending\";\n\n    // Update badge color\n    if (approval.status === \"approved\") {\n      statusBadge.className = \"persona-inline-flex persona-items-center persona-px-2 persona-py-0.5 persona-rounded-full persona-text-xs persona-font-medium\";\n      statusBadge.style.backgroundColor = \"var(--persona-palette-colors-success-100, #dcfce7)\";\n      statusBadge.style.color = \"var(--persona-palette-colors-success-700, #15803d)\";\n    } else if (approval.status === \"denied\") {\n      statusBadge.className = \"persona-inline-flex persona-items-center persona-px-2 persona-py-0.5 persona-rounded-full persona-text-xs persona-font-medium\";\n      statusBadge.style.backgroundColor = \"var(--persona-palette-colors-error-100, #fee2e2)\";\n      statusBadge.style.color = \"var(--persona-palette-colors-error-700, #b91c1c)\";\n    } else if (approval.status === \"timeout\") {\n      statusBadge.className = \"persona-inline-flex persona-items-center persona-px-2 persona-py-0.5 persona-rounded-full persona-text-xs persona-font-medium\";\n      statusBadge.style.backgroundColor = \"var(--persona-palette-colors-warning-100, #fef3c7)\";\n      statusBadge.style.color = \"var(--persona-palette-colors-warning-700, #b45309)\";\n    }\n    statusBadge.setAttribute(\"data-approval-status\", approval.status);\n  }\n\n  // Update icon\n  const iconContainer = bubble.querySelector('[data-approval-icon]') as HTMLElement;\n  if (iconContainer) {\n    iconContainer.innerHTML = \"\";\n    const iconName = approval.status === \"denied\" ? \"shield-x\"\n      : approval.status === \"timeout\" ? \"shield-alert\"\n      : \"shield-check\";\n    const iconColor = approval.status === \"approved\" ? \"var(--persona-feedback-success, #16a34a)\"\n      : approval.status === \"denied\" ? \"var(--persona-feedback-error, #dc2626)\"\n      : approval.status === \"timeout\" ? \"var(--persona-feedback-warning, #ca8a04)\"\n      : (approvalConfig?.titleColor ?? \"currentColor\");\n    const icon = renderLucideIcon(iconName, 20, iconColor, 2);\n    if (icon) {\n      iconContainer.appendChild(icon);\n    }\n  }\n\n  // Show/hide buttons based on status\n  const buttonsContainer = bubble.querySelector('[data-approval-buttons]') as HTMLElement;\n  if (buttonsContainer) {\n    buttonsContainer.style.display = approval.status === \"pending\" ? \"\" : \"none\";\n  }\n};\n\n/**\n * Create an approval bubble element for inline display in the chat.\n */\nexport const createApprovalBubble = (\n  message: AgentWidgetMessage,\n  config?: AgentWidgetConfig\n): HTMLElement => {\n  const approval = message.approval;\n  const approvalConfig = config?.approval !== false ? config?.approval : undefined;\n  const isPending = approval?.status === \"pending\";\n\n  const bubble = createElement(\n    \"div\",\n    [\n      \"persona-approval-bubble\",\n      \"persona-w-full\",\n      \"persona-max-w-[85%]\",\n      \"persona-rounded-2xl\",\n      \"persona-border\",\n      \"persona-shadow-sm\",\n      \"persona-overflow-hidden\",\n    ].join(\" \")\n  );\n\n  // Set id for idiomorph matching\n  bubble.id = `bubble-${message.id}`;\n  bubble.setAttribute(\"data-message-id\", message.id);\n\n  // Apply styling: use semantic tokens with config overrides\n  bubble.style.backgroundColor = approvalConfig?.backgroundColor ?? \"var(--persona-approval-bg, #fefce8)\";\n  bubble.style.borderColor = approvalConfig?.borderColor ?? \"var(--persona-approval-border, #fef08a)\";\n  bubble.style.boxShadow =\n    approvalConfig?.shadow !== undefined\n      ? (approvalConfig.shadow.trim() === \"\" ? \"none\" : approvalConfig.shadow)\n      : \"var(--persona-approval-shadow, 0 5px 15px rgba(15, 23, 42, 0.08))\";\n\n  if (!approval) {\n    return bubble;\n  }\n\n  // Header section with icon, title, and status badge\n  const header = createElement(\n    \"div\",\n    \"persona-flex persona-items-start persona-gap-3 persona-px-4 persona-py-3\"\n  );\n\n  // Icon container\n  const iconContainer = createElement(\"div\", \"persona-flex-shrink-0 persona-mt-0.5\");\n  iconContainer.setAttribute(\"data-approval-icon\", \"true\");\n  const iconName = approval.status === \"denied\" ? \"shield-x\"\n    : approval.status === \"timeout\" ? \"shield-alert\"\n    : \"shield-check\";\n  const iconColor = approval.status === \"approved\" ? \"var(--persona-feedback-success, #16a34a)\"\n    : approval.status === \"denied\" ? \"var(--persona-feedback-error, #dc2626)\"\n    : approval.status === \"timeout\" ? \"var(--persona-feedback-warning, #ca8a04)\"\n    : (approvalConfig?.titleColor ?? \"currentColor\");\n  const icon = renderLucideIcon(iconName, 20, iconColor, 2);\n  if (icon) {\n    iconContainer.appendChild(icon);\n  }\n\n  // Content area\n  const content = createElement(\"div\", \"persona-flex-1 persona-min-w-0\");\n\n  // Title row with status badge\n  const titleRow = createElement(\"div\", \"persona-flex persona-items-center persona-gap-2\");\n  const title = createElement(\"span\", \"persona-text-sm persona-font-medium persona-text-persona-primary\");\n  if (approvalConfig?.titleColor) {\n    title.style.color = approvalConfig.titleColor;\n  }\n  title.textContent = approvalConfig?.title ?? \"Approval Required\";\n  titleRow.appendChild(title);\n\n  // Status badge (shown when resolved)\n  if (!isPending) {\n    const badge = createElement(\"span\", \"persona-inline-flex persona-items-center persona-px-2 persona-py-0.5 persona-rounded-full persona-text-xs persona-font-medium\");\n    badge.setAttribute(\"data-approval-status\", approval.status);\n    if (approval.status === \"approved\") {\n      badge.style.backgroundColor = \"var(--persona-palette-colors-success-100, #dcfce7)\";\n      badge.style.color = \"var(--persona-palette-colors-success-700, #15803d)\";\n      badge.textContent = \"Approved\";\n    } else if (approval.status === \"denied\") {\n      badge.style.backgroundColor = \"var(--persona-palette-colors-error-100, #fee2e2)\";\n      badge.style.color = \"var(--persona-palette-colors-error-700, #b91c1c)\";\n      badge.textContent = \"Denied\";\n    } else if (approval.status === \"timeout\") {\n      badge.style.backgroundColor = \"var(--persona-palette-colors-warning-100, #fef3c7)\";\n      badge.style.color = \"var(--persona-palette-colors-warning-700, #b45309)\";\n      badge.textContent = \"Timeout\";\n    }\n    titleRow.appendChild(badge);\n  }\n\n  content.appendChild(titleRow);\n\n  // User-facing summary line. The wire `description` is the tool's\n  // agent-facing description (prompt prose, usage rules), so it is not shown\n  // here: it lives in the collapsible details section below. Label priority:\n  // formatDescription → declared WebMCP `title` → humanized tool name →\n  // raw description (no tool name at all).\n  const isWebMcpTool =\n    approval.toolType === \"webmcp\" ||\n    approval.toolName.startsWith(WEBMCP_TOOL_PREFIX);\n  const declaredTitle = isWebMcpTool\n    ? getWebMcpToolDisplayTitle(approval.toolName)\n    : undefined;\n  const summaryFromConfig = approvalConfig?.formatDescription?.({\n    toolName: approval.toolName,\n    toolType: approval.toolType,\n    description: approval.description,\n    parameters: approval.parameters,\n    ...(declaredTitle ? { displayTitle: declaredTitle } : {}),\n    ...(approval.reason ? { reason: approval.reason } : {}),\n  });\n  const summaryFallsBackToDescription = !approval.toolName;\n  const summaryText =\n    summaryFromConfig ||\n    (summaryFallsBackToDescription\n      ? approval.description\n      : `The assistant wants to use “${declaredTitle ?? humanizeToolName(approval.toolName)}”.`);\n\n  const summary = createElement(\"p\", \"persona-text-sm persona-mt-0.5 persona-text-persona-muted\");\n  summary.setAttribute(\"data-approval-summary\", \"true\");\n  if (approvalConfig?.descriptionColor) {\n    summary.style.color = approvalConfig.descriptionColor;\n  }\n  summary.textContent = summaryText;\n  content.appendChild(summary);\n\n  // Agent-authored justification for this specific call. It is the agent's\n  // own claim about its intent (attacker-writable under prompt injection), so\n  // it is rendered as plain text via textContent, never markdown/HTML, and\n  // explicitly attributed to the agent rather than spoken in system voice.\n  if (approval.reason) {\n    const reasonLine = createElement(\"p\", \"persona-text-sm persona-mt-1 persona-text-persona-muted\");\n    reasonLine.setAttribute(\"data-approval-reason\", \"true\");\n    if (approvalConfig?.reasonColor) {\n      reasonLine.style.color = approvalConfig.reasonColor;\n    } else if (approvalConfig?.descriptionColor) {\n      reasonLine.style.color = approvalConfig.descriptionColor;\n    }\n    const reasonLabel = createElement(\"span\", \"persona-font-medium\");\n    reasonLabel.textContent = `${approvalConfig?.reasonLabel ?? \"Agent's stated reason:\"} `;\n    reasonLine.appendChild(reasonLabel);\n    reasonLine.appendChild(document.createTextNode(approval.reason));\n    content.appendChild(reasonLine);\n  }\n\n  // Technical details: agent-facing description + raw parameters JSON,\n  // collapsed behind a toggle by default (`approval.detailsDisplay`).\n  const detailsMode = approvalConfig?.detailsDisplay ?? \"collapsed\";\n  const showDescriptionInDetails = Boolean(approval.description) && !summaryFallsBackToDescription;\n  const hasDetails = showDescriptionInDetails || Boolean(approval.parameters);\n  if (detailsMode !== \"hidden\" && hasDetails) {\n    const expanded = isDetailsExpanded(message.id, config);\n\n    const toggle = createElement(\n      \"button\",\n      \"persona-inline-flex persona-items-center persona-gap-1 persona-mt-1 persona-p-0 persona-border-none persona-bg-transparent persona-text-xs persona-font-medium persona-cursor-pointer persona-text-persona-muted\"\n    ) as HTMLButtonElement;\n    toggle.type = \"button\";\n    toggle.setAttribute(\"data-expand-header\", \"true\");\n    toggle.setAttribute(\"data-bubble-type\", \"approval\");\n    if (approvalConfig?.descriptionColor) {\n      toggle.style.color = approvalConfig.descriptionColor;\n    }\n    const toggleLabel = createElement(\"span\");\n    toggleLabel.setAttribute(\"data-approval-details-label\", \"true\");\n    const chevronHolder = createElement(\"span\", \"persona-inline-flex persona-items-center\");\n    chevronHolder.setAttribute(\"data-approval-details-chevron\", \"true\");\n    toggle.append(toggleLabel, chevronHolder);\n    applyDetailsToggleState(toggle, expanded, config);\n    content.appendChild(toggle);\n\n    const details = createElement(\"div\");\n    details.setAttribute(\"data-approval-details\", \"true\");\n    details.style.display = expanded ? \"\" : \"none\";\n\n    if (showDescriptionInDetails) {\n      const description = createElement(\"p\", \"persona-text-sm persona-mt-1 persona-text-persona-muted\");\n      if (approvalConfig?.descriptionColor) {\n        description.style.color = approvalConfig.descriptionColor;\n      }\n      description.textContent = approval.description;\n      details.appendChild(description);\n    }\n\n    if (approval.parameters) {\n      const paramsPre = createElement(\n        \"pre\",\n        \"persona-mt-2 persona-text-xs persona-p-2 persona-rounded persona-overflow-x-auto persona-max-h-32 persona-bg-persona-container persona-text-persona-primary\"\n      );\n      if (approvalConfig?.parameterBackgroundColor) {\n        paramsPre.style.backgroundColor = approvalConfig.parameterBackgroundColor;\n      }\n      if (approvalConfig?.parameterTextColor) {\n        paramsPre.style.color = approvalConfig.parameterTextColor;\n      }\n      paramsPre.style.fontSize = \"0.75rem\";\n      paramsPre.style.lineHeight = \"1rem\";\n      paramsPre.textContent = formatUnknownValue(approval.parameters);\n      details.appendChild(paramsPre);\n    }\n\n    content.appendChild(details);\n  }\n\n  // Action buttons (only shown when pending)\n  if (isPending) {\n    const buttonsContainer = createElement(\"div\", \"persona-flex persona-gap-2 persona-mt-2\");\n    buttonsContainer.setAttribute(\"data-approval-buttons\", \"true\");\n\n    // Approve button\n    const approveBtn = createElement(\"button\", \"persona-inline-flex persona-items-center persona-px-3 persona-py-1.5 persona-rounded-md persona-text-xs persona-font-medium persona-border-none persona-cursor-pointer\") as HTMLButtonElement;\n    approveBtn.type = \"button\";\n    approveBtn.style.backgroundColor = approvalConfig?.approveButtonColor ?? \"var(--persona-approval-approve-bg, #22c55e)\";\n    approveBtn.style.color = approvalConfig?.approveButtonTextColor ?? \"#ffffff\";\n    approveBtn.setAttribute(\"data-approval-action\", \"approve\");\n    const approveIcon = renderLucideIcon(\"shield-check\", 14, approvalConfig?.approveButtonTextColor ?? \"#ffffff\", 2);\n    if (approveIcon) {\n      approveIcon.style.marginRight = \"4px\";\n      approveBtn.appendChild(approveIcon);\n    }\n    const approveLabel = document.createTextNode(approvalConfig?.approveLabel ?? \"Approve\");\n    approveBtn.appendChild(approveLabel);\n\n    // Deny button\n    const denyBtn = createElement(\"button\", \"persona-inline-flex persona-items-center persona-px-3 persona-py-1.5 persona-rounded-md persona-text-xs persona-font-medium persona-cursor-pointer\") as HTMLButtonElement;\n    denyBtn.type = \"button\";\n    denyBtn.style.backgroundColor = approvalConfig?.denyButtonColor ?? \"transparent\";\n    denyBtn.style.color = approvalConfig?.denyButtonTextColor ?? \"var(--persona-feedback-error, #dc2626)\";\n    denyBtn.style.border = `1px solid ${approvalConfig?.denyButtonTextColor ? approvalConfig.denyButtonTextColor : \"var(--persona-palette-colors-error-200, #fca5a5)\"}`;\n    denyBtn.setAttribute(\"data-approval-action\", \"deny\");\n    const denyIcon = renderLucideIcon(\"shield-x\", 14, approvalConfig?.denyButtonTextColor ?? \"var(--persona-feedback-error, #dc2626)\", 2);\n    if (denyIcon) {\n      denyIcon.style.marginRight = \"4px\";\n      denyBtn.appendChild(denyIcon);\n    }\n    const denyLabel = document.createTextNode(approvalConfig?.denyLabel ?? \"Deny\");\n    denyBtn.appendChild(denyLabel);\n\n    buttonsContainer.append(approveBtn, denyBtn);\n    content.appendChild(buttonsContainer);\n  }\n\n  header.append(iconContainer, content);\n  bubble.appendChild(header);\n\n  return bubble;\n};\n","// Plugin Kit: small, dependency-free utilities for authoring Persona plugins.\n//\n// Plugin render hooks (`renderApproval`, `renderAskUserQuestion`, `renderMessage`,\n// …) return a detached `HTMLElement` that the widget morphs into the transcript.\n// Two needs come up again and again when that element is more than static markup:\n//\n//   1. Injecting the plugin's own CSS so it survives the widget's Shadow-DOM mode\n//      (a plain `document.head` <style> does NOT pierce a shadow root).\n//   2. Floating UI, dropdowns, menus, tooltips, that must overlay the rest of\n//      the widget and not be clipped by the transcript's scroll container.\n//\n// Both are easy to get subtly wrong, so they live here as a supported, optional\n// subpath: `@runtypelabs/persona/plugin-kit`. Importing it costs nothing unless\n// you use it, and nothing here touches the widget's core bundle.\n//\n// ```ts\n// import { injectStyles, createPopover, isEditableEventTarget } from\n//   \"@runtypelabs/persona/plugin-kit\";\n// ```\n\n/* ============================================================\n   Shadow-safe style injection\n   ============================================================ */\n\n/**\n * Resolve the root a node's styles should live in.\n *\n * Returns the node's `ShadowRoot` when it (or an ancestor) is shadowed, the\n * owning `Document` otherwise. A `<style>` placed in this root reaches the node\n * regardless of whether the widget runs in light or shadow DOM. For a detached\n * node (one not yet mounted) this falls back to the owning document.\n */\nexport function getStyleRoot(node: Node): Document | ShadowRoot {\n  const root = node.getRootNode?.();\n  if (root instanceof ShadowRoot) return root;\n  if (root instanceof Document) return root;\n  return node.ownerDocument ?? document;\n}\n\nfunction injectInto(root: Document | ShadowRoot, id: string, css: string): void {\n  const container: Document | ShadowRoot | HTMLHeadElement =\n    root instanceof Document ? root.head : root;\n  const escaped = id.replace(/[\"\\\\]/g, \"\\\\$&\");\n  if (container.querySelector(`style[data-persona-plugin-style=\"${escaped}\"]`)) {\n    return; // Already present in this root: idempotent.\n  }\n  const doc = root instanceof Document ? root : root.ownerDocument ?? document;\n  const style = doc.createElement(\"style\");\n  style.setAttribute(\"data-persona-plugin-style\", id);\n  style.textContent = css;\n  container.appendChild(style);\n}\n\n/**\n * Inject a plugin's CSS once into the correct root: the widget's shadow root\n * when shadowed, the document head otherwise. Idempotent: keyed by `id`, so it\n * is safe to call on every render.\n *\n * Pass the element you're about to return from a render hook. While building, an\n * element is detached and its eventual root is unknown, so this injects into the\n * owning document immediately (covering the default light-DOM case with no\n * flash) and then, on the next microtask: after the widget has mounted the\n * element: re-resolves and also injects into its shadow root if it landed in\n * one. You may also pass an explicit `Document` or `ShadowRoot`.\n *\n * @example\n * ```ts\n * renderApproval: ({ message, approve, deny }) => {\n *   const card = buildCard(message.approval, approve, deny);\n *   injectStyles(card, \"my-approval-plugin\", CSS);\n *   return card;\n * }\n * ```\n */\nexport function injectStyles(\n  target: Node | Document | ShadowRoot,\n  id: string,\n  css: string\n): void {\n  if (target instanceof Document || target instanceof ShadowRoot) {\n    injectInto(target, id, css);\n    return;\n  }\n  const node = target;\n  if (node.isConnected) {\n    injectInto(getStyleRoot(node), id, css);\n    return;\n  }\n  // Detached (built but not yet mounted): inject into the owning document now,\n  // then re-resolve after mount so shadow-DOM widgets also get the <style>.\n  const doc = node.ownerDocument ?? document;\n  injectInto(doc, id, css);\n  queueMicrotask(() => {\n    const root = getStyleRoot(node);\n    if (root !== doc) injectInto(root, id, css);\n  });\n}\n\n/* ============================================================\n   Floating popover\n   ============================================================ */\n\nexport type PopoverPlacement =\n  | \"bottom-start\"\n  | \"bottom-end\"\n  | \"top-start\"\n  | \"top-end\";\n\nexport interface PopoverOptions {\n  /** Element the popover is positioned against. */\n  anchor: HTMLElement;\n  /** The floating element (menu, tooltip, panel). Built detached; mounted on `open`. */\n  content: HTMLElement;\n  /** Where to place `content` relative to `anchor`. Default `\"bottom-start\"`. */\n  placement?: PopoverPlacement;\n  /** Gap in px between anchor and content. Default `6`. */\n  offset?: number;\n  /** Set `content`'s `min-width` to the anchor's width. Default `false`. */\n  matchAnchorWidth?: boolean;\n  /**\n   * Inline `z-index` for `content`. Default `2147483000` so it overlays the rest\n   * of the widget. Pass `null` to leave z-index to your own CSS.\n   */\n  zIndex?: number | null;\n  /**\n   * Where to mount `content`. Defaults to the anchor's shadow root (when\n   * shadowed) or `document.body`: keeping it inside the same style + stacking\n   * scope as the anchor while escaping the transcript's scroll clipping.\n   */\n  container?: HTMLElement | ShadowRoot;\n  onOpen?: () => void;\n  /** Fired when the popover closes by itself (outside click or the anchor leaving the DOM). */\n  onDismiss?: (reason: \"outside\" | \"anchor-removed\") => void;\n}\n\nexport interface PopoverHandle {\n  readonly isOpen: boolean;\n  open(): void;\n  close(): void;\n  toggle(): void;\n  /** Recompute position from the current anchor rect (called automatically on scroll/resize). */\n  reposition(): void;\n  /** Close and release all listeners; the handle is inert afterward. */\n  destroy(): void;\n}\n\nfunction defaultContainer(anchor: HTMLElement): HTMLElement | ShadowRoot {\n  const root = anchor.getRootNode?.();\n  if (root instanceof ShadowRoot) return root;\n  return (anchor.ownerDocument ?? document).body;\n}\n\n/**\n * A floating popover anchored to an element: `fixed`-positioned (so it overlays\n * the rest of the widget and isn't clipped by scroll containers), dismissed on\n * outside pointerdown, repositioned on scroll/resize, and auto-closed if the\n * anchor leaves the DOM. Mount your styles with {@link injectStyles}.\n *\n * @example\n * ```ts\n * const popover = createPopover({\n *   anchor: splitButton,\n *   content: menu,\n *   placement: \"bottom-start\",\n *   matchAnchorWidth: true,\n * });\n * caret.addEventListener(\"click\", () => popover.toggle());\n * // on teardown: popover.destroy();\n * ```\n */\nexport function createPopover(options: PopoverOptions): PopoverHandle {\n  const {\n    anchor,\n    content,\n    placement = \"bottom-start\",\n    offset = 6,\n    matchAnchorWidth = false,\n    zIndex = 2147483000,\n    onOpen,\n    onDismiss,\n  } = options;\n\n  const container = options.container ?? defaultContainer(anchor);\n  let open = false;\n  let detach: (() => void) | null = null;\n\n  const reposition = (): void => {\n    if (!open) return;\n    const rect = anchor.getBoundingClientRect();\n    content.style.position = \"fixed\";\n    if (matchAnchorWidth) content.style.minWidth = `${rect.width}px`;\n\n    const top =\n      placement === \"top-start\" || placement === \"top-end\"\n        ? rect.top - offset - content.getBoundingClientRect().height\n        : rect.bottom + offset;\n\n    const left =\n      placement === \"bottom-end\" || placement === \"top-end\"\n        ? rect.right - content.getBoundingClientRect().width\n        : rect.left;\n\n    content.style.top = `${top}px`;\n    content.style.left = `${left}px`;\n  };\n\n  const close = (): void => {\n    if (!open) return;\n    open = false;\n    if (detach) {\n      detach();\n      detach = null;\n    }\n    content.remove();\n  };\n\n  const doOpen = (): void => {\n    if (open) return;\n    open = true;\n    if (zIndex != null) content.style.zIndex = String(zIndex);\n    container.appendChild(content);\n    reposition();\n\n    const ownerWindow = (anchor.ownerDocument ?? document).defaultView ?? window;\n    const ownerDocument = anchor.ownerDocument ?? document;\n\n    const onReposition = (): void => {\n      if (!anchor.isConnected) {\n        close();\n        onDismiss?.(\"anchor-removed\");\n        return;\n      }\n      reposition();\n    };\n    const onOutside = (event: Event): void => {\n      const path =\n        typeof event.composedPath === \"function\" ? event.composedPath() : [];\n      if (path.includes(content) || path.includes(anchor)) return;\n      close();\n      onDismiss?.(\"outside\");\n    };\n\n    // Defer arming so the click that opened the popover doesn't dismiss it.\n    const armId = ownerWindow.setTimeout(() => {\n      ownerDocument.addEventListener(\"pointerdown\", onOutside, true);\n    }, 0);\n    ownerWindow.addEventListener(\"scroll\", onReposition, true);\n    ownerWindow.addEventListener(\"resize\", onReposition);\n\n    detach = () => {\n      ownerWindow.clearTimeout(armId);\n      ownerDocument.removeEventListener(\"pointerdown\", onOutside, true);\n      ownerWindow.removeEventListener(\"scroll\", onReposition, true);\n      ownerWindow.removeEventListener(\"resize\", onReposition);\n    };\n\n    onOpen?.();\n  };\n\n  return {\n    get isOpen() {\n      return open;\n    },\n    open: doOpen,\n    close,\n    toggle: () => (open ? close() : doOpen()),\n    reposition,\n    destroy: close,\n  };\n}\n\n/* ============================================================\n   Keyboard helpers\n   ============================================================ */\n\n/**\n * Whether an event originated from an editable element (`<input>`, `<textarea>`,\n * or `contenteditable`). Use it to avoid hijacking keys like Enter/Escape while\n * the user is typing in the composer.\n *\n * Inspects the composed path, so it works for events that cross the widget's\n * Shadow-DOM boundary (where `event.target` is retargeted to the host).\n */\nexport function isEditableEventTarget(event: Event): boolean {\n  const path =\n    typeof event.composedPath === \"function\" ? event.composedPath() : [];\n  return path.some(\n    (el) =>\n      el instanceof HTMLElement &&\n      (el.tagName === \"INPUT\" ||\n        el.tagName === \"TEXTAREA\" ||\n        el.isContentEditable)\n  );\n}\n","/**\n * Built-in default approval renderer.\n *\n * Renders the neutral \"permission card\": a tool icon, a \"The assistant wants to\n * use <tool>\" title, the call arguments collapsed behind a \"show more\" header\n * chevron, and an action row. By default the row is a single \"Allow\" (allow\n * once) + \"Deny\". When `config.approval.enableAlwaysAllow` is true it becomes a\n * split \"Always allow ⏎\" primary with an \"Allow once ⌘⏎\" dropdown plus\n * \"Deny Esc\", with keyboard shortcuts — that affordance is opt-in because\n * \"Always allow\" only means anything if the integrator persists the policy via\n * `onDecision`'s `remember` flag (needs a backend).\n *\n * Installed as an internal `renderApproval` plugin (see ui.ts) so it rides the\n * existing plugin stub-and-hydrate + teardown path and survives idiomorph\n * re-renders. A user-supplied `renderApproval` plugin still fully overrides it.\n *\n * Resolved states mirror the example plugin: approved → render nothing (the\n * tool call takes over the transcript); denied/timeout → a subtle one-line\n * trace.\n */\nimport { createElement } from \"../utils/dom\";\nimport { renderLucideIcon } from \"../utils/icons\";\nimport { formatUnknownValue } from \"../utils/formatting\";\nimport { WEBMCP_TOOL_PREFIX, getWebMcpToolDisplayTitle } from \"../webmcp-bridge\";\nimport { createPopover, isEditableEventTarget, type PopoverHandle } from \"../plugin-kit\";\nimport type {\n  AgentWidgetMessage,\n  AgentWidgetConfig,\n  AgentWidgetApprovalConfig,\n} from \"../types\";\nimport type { AgentWidgetPlugin } from \"../plugins/types\";\nimport { humanizeToolName, approvalDetailsExpansionState } from \"./approval-bubble\";\n\ntype Approval = NonNullable<AgentWidgetMessage[\"approval\"]>;\ntype Decide = (options?: { remember?: boolean }) => void;\n\n/**\n * Per-widget-instance runtime state. The document `keydown` handler (re-bound on\n * each render to the freshest approve/deny closures) and the \"Allow once\"\n * popover are torn down when the approval resolves, the bubble rebuilds, or the\n * widget is destroyed. This lives on the plugin instance (NOT module scope) so\n * tearing down one widget never clears listeners/popovers another widget on the\n * same page still owns.\n */\ninterface InstanceState {\n  keyHandlers: Map<string, (e: KeyboardEvent) => void>;\n  popovers: Map<string, PopoverHandle>;\n  // Order in which approvals FIRST became pending — not re-render order. The\n  // newest pending approval (bottom of the thread) owns the keyboard shortcuts,\n  // so Enter/Esc don't fire on every pending card at once, and an older card\n  // re-rendering can't steal ownership from a newer one.\n  pendingOrder: string[];\n  latestPendingApprovalId: string | null;\n}\n\nconst createInstanceState = (): InstanceState => ({\n  keyHandlers: new Map(),\n  popovers: new Map(),\n  pendingOrder: [],\n  latestPendingApprovalId: null,\n});\n\n// Drop a message's transient listener + popover WITHOUT touching its place in\n// the pending order. Used when a still-pending card re-renders and rebinds fresh\n// closures (so a rebuild of an older card doesn't reorder it to \"newest\").\nconst detachMessage = (state: InstanceState, messageId: string): void => {\n  const prevKey = state.keyHandlers.get(messageId);\n  if (prevKey) {\n    document.removeEventListener(\"keydown\", prevKey);\n    state.keyHandlers.delete(messageId);\n  }\n  const popover = state.popovers.get(messageId);\n  if (popover) {\n    popover.destroy();\n    state.popovers.delete(messageId);\n  }\n};\n\n// Fully retire a message (resolved, denied, or widget destroyed): detach its\n// listener/popover AND remove it from the pending order. If the keyboard owner\n// just went away, promote the newest remaining pending approval so Enter/Esc\n// keep working instead of going dead while another pending card remains.\nconst teardownMessage = (state: InstanceState, messageId: string): void => {\n  detachMessage(state, messageId);\n  const idx = state.pendingOrder.indexOf(messageId);\n  if (idx !== -1) state.pendingOrder.splice(idx, 1);\n  if (state.latestPendingApprovalId === messageId) {\n    state.latestPendingApprovalId = state.pendingOrder.length\n      ? state.pendingOrder[state.pendingOrder.length - 1]\n      : null;\n  }\n};\n\nconst resolveApprovalConfig = (\n  config?: AgentWidgetConfig\n): AgentWidgetApprovalConfig | undefined =>\n  config?.approval !== false ? config?.approval : undefined;\n\nconst isDetailsExpanded = (\n  messageId: string,\n  approvalConfig?: AgentWidgetApprovalConfig\n): boolean => {\n  const mode = approvalConfig?.detailsDisplay ?? \"collapsed\";\n  return approvalDetailsExpansionState.get(messageId) ?? mode === \"expanded\";\n};\n\nconst kbd = (label: string): HTMLElement => {\n  const el = createElement(\"span\", \"persona-approval-kbd\");\n  el.textContent = label;\n  return el;\n};\n\n// Title: \"The assistant wants to use <tool>\" with an optional \"from <source>\"\n// when a non-WebMCP `toolType` source label is present. `formatDescription`\n// fully overrides the line. Mirrors the built-in bubble's label priority\n// (formatDescription → declared WebMCP title → humanized tool name).\nconst buildTitle = (\n  approval: Approval,\n  approvalConfig?: AgentWidgetApprovalConfig\n): HTMLElement => {\n  const title = createElement(\"span\", \"persona-approval-title\");\n  if (approvalConfig?.titleColor) title.style.color = approvalConfig.titleColor;\n\n  const isWebMcp =\n    approval.toolType === \"webmcp\" || approval.toolName.startsWith(WEBMCP_TOOL_PREFIX);\n  const declaredTitle = isWebMcp\n    ? getWebMcpToolDisplayTitle(approval.toolName)\n    : undefined;\n\n  const custom = approvalConfig?.formatDescription?.({\n    toolName: approval.toolName,\n    toolType: approval.toolType,\n    description: approval.description ?? \"\",\n    parameters: approval.parameters,\n    ...(declaredTitle ? { displayTitle: declaredTitle } : {}),\n    ...(approval.reason ? { reason: approval.reason } : {}),\n  });\n  if (custom) {\n    title.textContent = custom;\n    return title;\n  }\n\n  const toolDisplay = declaredTitle ?? humanizeToolName(approval.toolName);\n  const source =\n    approval.toolType && approval.toolType !== \"webmcp\" ? approval.toolType : null;\n  title.append(\"The assistant wants to use \");\n  const toolStrong = document.createElement(\"strong\");\n  toolStrong.textContent = toolDisplay;\n  title.appendChild(toolStrong);\n  if (source) {\n    title.append(\" from \");\n    const srcStrong = document.createElement(\"strong\");\n    srcStrong.textContent = source;\n    title.appendChild(srcStrong);\n  }\n  return title;\n};\n\nconst buildResolvedTrace = (approval: Approval): HTMLElement => {\n  const row = createElement(\"div\", \"persona-approval-resolved\");\n  const icon = renderLucideIcon(\"ban\", 15, \"currentColor\", 2);\n  if (icon) row.appendChild(icon);\n  const name = createElement(\"span\", \"persona-approval-resolved-name\");\n  name.textContent = approval.toolName ? humanizeToolName(approval.toolName) : \"Tool\";\n  row.append(name, document.createTextNode(approval.status === \"timeout\" ? \" timed out\" : \" denied\"));\n  return row;\n};\n\nconst buildPending = (\n  state: InstanceState,\n  message: AgentWidgetMessage,\n  approval: Approval,\n  approvalConfig: AgentWidgetApprovalConfig | undefined,\n  approve: Decide,\n  deny: Decide,\n  enableAlways: boolean\n): HTMLElement => {\n  const card = createElement(\"div\", \"persona-approval-card persona-shadow-sm\");\n  card.id = `bubble-${message.id}`;\n  card.setAttribute(\"data-message-id\", message.id);\n  card.setAttribute(\"data-bubble-type\", \"approval\");\n  if (approvalConfig?.backgroundColor) card.style.background = approvalConfig.backgroundColor;\n  if (approvalConfig?.borderColor) card.style.borderColor = approvalConfig.borderColor;\n  if (approvalConfig?.shadow !== undefined) {\n    card.style.boxShadow = approvalConfig.shadow.trim() === \"\" ? \"none\" : approvalConfig.shadow;\n  }\n\n  const detailsMode = approvalConfig?.detailsDisplay ?? \"collapsed\";\n  // The disclosure surfaces the agent-facing description (prompt prose, usage\n  // rules) and the raw call parameters. `buildTitle` never falls back to the\n  // raw description (it uses formatDescription → declared title → humanized\n  // name), so the description is only ever visible here. Mirrors the legacy\n  // bubble, which also opened a disclosure when only a description was present.\n  const hasDescription = Boolean(approval.description) && detailsMode !== \"hidden\";\n  const hasParams = approval.parameters != null && detailsMode !== \"hidden\";\n  const hasDetails = hasDescription || hasParams;\n  const expanded = hasDetails && isDetailsExpanded(message.id, approvalConfig);\n\n  // The card uses the whole header as the disclosure toggle (chevron-only, no\n  // separate text button), but the legacy `showDetailsLabel`/`hideDetailsLabel`\n  // strings are still honored as the toggle's accessible label so integrators\n  // who localized them keep a meaningful, customizable name on the control.\n  const showDetailsLabel = approvalConfig?.showDetailsLabel ?? \"Show details\";\n  const hideDetailsLabel = approvalConfig?.hideDetailsLabel ?? \"Hide details\";\n\n  // Header. When a disclosure exists, the whole header toggles its visibility.\n  const head = createElement(\"button\", \"persona-approval-head\") as HTMLButtonElement;\n  head.type = \"button\";\n  if (hasDetails) {\n    head.setAttribute(\"data-action\", \"toggle-params\");\n    head.setAttribute(\"aria-expanded\", expanded ? \"true\" : \"false\");\n    head.setAttribute(\"aria-label\", expanded ? hideDetailsLabel : showDetailsLabel);\n  } else {\n    head.setAttribute(\"data-static\", \"true\");\n  }\n\n  const logo = createElement(\"span\", \"persona-approval-logo\");\n  const glyph = renderLucideIcon(\"shield-check\", 16, \"currentColor\", 2);\n  if (glyph) logo.appendChild(glyph);\n  head.appendChild(logo);\n\n  const title = buildTitle(approval, approvalConfig);\n  if (hasDetails) {\n    const toggle = createElement(\"span\", \"persona-approval-toggle\");\n    toggle.setAttribute(\"aria-hidden\", \"true\");\n    const chevron = renderLucideIcon(\"chevron-down\", 14, \"currentColor\", 2);\n    if (chevron) toggle.appendChild(chevron);\n    title.append(\" \");\n    title.appendChild(toggle);\n  }\n  head.appendChild(title);\n  card.appendChild(head);\n\n  const body = createElement(\"div\", \"persona-approval-body\");\n\n  if (hasDetails) {\n    const details = createElement(\"div\", \"persona-approval-details\");\n    details.setAttribute(\"data-role\", \"params\");\n    details.hidden = !expanded;\n\n    if (hasDescription) {\n      const desc = createElement(\"p\", \"persona-approval-desc\");\n      if (approvalConfig?.descriptionColor) desc.style.color = approvalConfig.descriptionColor;\n      desc.textContent = approval.description as string;\n      details.appendChild(desc);\n    }\n\n    if (hasParams) {\n      const pre = createElement(\"pre\", \"persona-approval-params\");\n      if (approvalConfig?.parameterBackgroundColor) pre.style.background = approvalConfig.parameterBackgroundColor;\n      if (approvalConfig?.parameterTextColor) pre.style.color = approvalConfig.parameterTextColor;\n      pre.textContent = formatUnknownValue(approval.parameters);\n      details.appendChild(pre);\n    }\n\n    body.appendChild(details);\n  }\n\n  // Agent-authored justification: attacker-writable, so plain text + attributed.\n  if (approval.reason) {\n    const reasonLine = createElement(\"p\", \"persona-approval-reason\");\n    if (approvalConfig?.reasonColor) reasonLine.style.color = approvalConfig.reasonColor;\n    else if (approvalConfig?.descriptionColor) reasonLine.style.color = approvalConfig.descriptionColor;\n    const label = createElement(\"span\", \"persona-approval-reason-label\");\n    label.textContent = `${approvalConfig?.reasonLabel ?? \"Agent's stated reason:\"} `;\n    reasonLine.append(label, document.createTextNode(approval.reason));\n    body.appendChild(reasonLine);\n  }\n\n  const actions = createElement(\"div\", \"persona-approval-actions\");\n  let popover: PopoverHandle | null = null;\n\n  const applyPrimaryColors = (el: HTMLElement): void => {\n    if (approvalConfig?.approveButtonColor) el.style.background = approvalConfig.approveButtonColor;\n    if (approvalConfig?.approveButtonTextColor) el.style.color = approvalConfig.approveButtonTextColor;\n  };\n  const denyBtn = createElement(\"button\", \"persona-approval-deny\") as HTMLButtonElement;\n  denyBtn.type = \"button\";\n  denyBtn.setAttribute(\"data-action\", \"deny\");\n  if (approvalConfig?.denyButtonColor) denyBtn.style.background = approvalConfig.denyButtonColor;\n  if (approvalConfig?.denyButtonTextColor) denyBtn.style.color = approvalConfig.denyButtonTextColor;\n  denyBtn.append(approvalConfig?.denyLabel ?? \"Deny\");\n\n  if (enableAlways) {\n    const split = createElement(\"div\", \"persona-approval-split\");\n    const primary = createElement(\"button\", \"persona-approval-primary\") as HTMLButtonElement;\n    primary.type = \"button\";\n    primary.setAttribute(\"data-action\", \"always\");\n    applyPrimaryColors(primary);\n    primary.append(approvalConfig?.approveLabel ?? \"Always allow\", kbd(\"⏎\"));\n\n    const caret = createElement(\"button\", \"persona-approval-caret\") as HTMLButtonElement;\n    caret.type = \"button\";\n    caret.setAttribute(\"data-action\", \"toggle-menu\");\n    caret.setAttribute(\"aria-label\", \"More options\");\n    applyPrimaryColors(caret);\n    const caretIcon = renderLucideIcon(\"chevron-down\", 15, \"currentColor\", 2);\n    if (caretIcon) caret.appendChild(caretIcon);\n\n    split.append(primary, caret);\n    actions.append(split, denyBtn);\n    denyBtn.append(kbd(\"Esc\"));\n\n    // \"Allow once\" menu, portaled out of the transcript by createPopover so it\n    // overlays the rest of the UI and isn't clipped by the scroll container.\n    const menu = createElement(\"div\", \"persona-approval-menu\");\n    const once = createElement(\"button\", \"persona-approval-menu-item\") as HTMLButtonElement;\n    once.type = \"button\";\n    once.append(\"Allow once\", kbd(\"⌘⏎\"));\n    menu.appendChild(once);\n    popover = createPopover({\n      anchor: split,\n      content: menu,\n      placement: \"bottom-start\",\n      matchAnchorWidth: true,\n    });\n    state.popovers.set(message.id, popover);\n    once.addEventListener(\"click\", () => {\n      teardownMessage(state, message.id);\n      approve(); // Allow once\n    });\n  } else {\n    const allow = createElement(\n      \"button\",\n      \"persona-approval-primary persona-approval-primary--solo\"\n    ) as HTMLButtonElement;\n    allow.type = \"button\";\n    allow.setAttribute(\"data-action\", \"allow\");\n    applyPrimaryColors(allow);\n    allow.append(approvalConfig?.approveLabel ?? \"Allow\");\n    actions.append(allow, denyBtn);\n  }\n\n  body.appendChild(actions);\n  card.appendChild(body);\n\n  // Single delegated click listener; survives morph via the plugin hydrate path.\n  card.addEventListener(\"click\", (e) => {\n    const target = e.target instanceof Element ? e.target.closest(\"[data-action]\") : null;\n    if (!target) return;\n    const action = target.getAttribute(\"data-action\");\n    if (action === \"toggle-params\") {\n      const pre = card.querySelector<HTMLElement>('[data-role=\"params\"]');\n      if (pre) {\n        const willOpen = pre.hidden;\n        pre.hidden = !willOpen;\n        head.setAttribute(\"aria-expanded\", willOpen ? \"true\" : \"false\");\n        head.setAttribute(\"aria-label\", willOpen ? hideDetailsLabel : showDetailsLabel);\n        approvalDetailsExpansionState.set(message.id, willOpen);\n      }\n      return;\n    }\n    if (action === \"toggle-menu\") {\n      popover?.toggle();\n      return;\n    }\n    if (action === \"always\") {\n      teardownMessage(state, message.id);\n      approve({ remember: true });\n      return;\n    }\n    if (action === \"allow\") {\n      teardownMessage(state, message.id);\n      approve();\n      return;\n    }\n    if (action === \"deny\") {\n      teardownMessage(state, message.id);\n      deny();\n      return;\n    }\n  });\n\n  return card;\n};\n\n/**\n * The built-in approval renderer, shaped as a plugin so ui.ts can run it through\n * the same `renderApproval` pipeline as user plugins (which still take\n * precedence). Reads `config` from the render context, so a single plugin\n * serves every render for one widget instance.\n *\n * Returns the plugin alongside a `teardown` the host pushes into its destroy\n * callbacks — both close over the SAME per-instance state, so destroying one\n * widget never disturbs another widget's open approvals on the same page.\n */\nexport const createBuiltInApprovalPlugin = (): {\n  plugin: AgentWidgetPlugin;\n  teardown: () => void;\n} => {\n  const state = createInstanceState();\n\n  const plugin: AgentWidgetPlugin = {\n    id: \"persona-built-in-approval\",\n    renderApproval: ({ message, approve, deny, config }) => {\n      const approval = message?.approval;\n      if (!approval) return null;\n      const approvalConfig = resolveApprovalConfig(config);\n\n      if (approval.status !== \"pending\") {\n        teardownMessage(state, message.id);\n        // Approved → render nothing; the tool call takes over the transcript.\n        // (An empty hidden element, not null, suppresses the legacy fallback.)\n        if (approval.status === \"approved\") {\n          const hidden = document.createElement(\"div\");\n          hidden.style.display = \"none\";\n          return hidden;\n        }\n        return buildResolvedTrace(approval);\n      }\n\n      // Rebuild: drop any prior listener/popover before (re)binding fresh\n      // closures, but KEEP this approval's place in the pending order so a\n      // re-render of an older card doesn't reorder it ahead of a newer one.\n      detachMessage(state, message.id);\n      const enableAlways = approvalConfig?.enableAlwaysAllow === true;\n      const card = buildPending(state, message, approval, approvalConfig, approve, deny, enableAlways);\n\n      if (enableAlways) {\n        if (!state.pendingOrder.includes(message.id)) state.pendingOrder.push(message.id);\n        // The newest first-seen pending approval owns the shortcuts, regardless\n        // of which card happens to be re-rendering right now.\n        state.latestPendingApprovalId = state.pendingOrder[state.pendingOrder.length - 1];\n        const onKeydown = (e: KeyboardEvent): void => {\n          if (isEditableEventTarget(e)) return;\n          if (message.id !== state.latestPendingApprovalId) return;\n          if (e.key !== \"Escape\" && e.key !== \"Enter\") return;\n          e.preventDefault();\n          // Resolving here promotes the next-newest pending approval to owner.\n          // Stop immediate propagation so the SAME keypress doesn't then reach\n          // that freshly-promoted card's listener and resolve it too — one\n          // keypress resolves exactly one approval; the next owner waits for the\n          // next press.\n          e.stopImmediatePropagation();\n          teardownMessage(state, message.id);\n          if (e.key === \"Escape\") {\n            deny();\n          } else if (e.metaKey || e.ctrlKey) {\n            approve(); // Allow once\n          } else {\n            approve({ remember: true }); // Always allow\n          }\n        };\n        state.keyHandlers.set(message.id, onKeydown);\n        document.addEventListener(\"keydown\", onKeydown);\n      }\n\n      return card;\n    },\n  };\n\n  // Release every pending approval's global listener + popover for THIS widget.\n  const teardown = (): void => {\n    for (const id of [...state.keyHandlers.keys(), ...state.popovers.keys()]) {\n      teardownMessage(state, id);\n    }\n    state.latestPendingApprovalId = null;\n  };\n\n  return { plugin, teardown };\n};\n","import { createElement } from \"../utils/dom\";\nimport { AgentWidgetSession } from \"../session\";\nimport { AgentWidgetMessage, AgentWidgetSuggestionChipsConfig } from \"../types\";\n\nexport interface SuggestionButtons {\n  buttons: HTMLButtonElement[];\n  render: (\n    chips: string[] | undefined,\n    session: AgentWidgetSession,\n    textarea: HTMLTextAreaElement,\n    messages?: AgentWidgetMessage[],\n    config?: AgentWidgetSuggestionChipsConfig,\n    opts?: SuggestionRenderOptions\n  ) => void;\n}\n\nexport interface SuggestionRenderOptions {\n  /**\n   * Chips pushed by the agent's `suggest_replies` tool rather than the\n   * static `suggestionChips` config. Skips the before-first-user-message\n   * gate (the caller already applied the latest-turn visibility rule) and\n   * dispatches `persona:suggestReplies:*` DOM events.\n   */\n  agentPushed?: boolean;\n}\n\nexport const createSuggestions = (container: HTMLElement): SuggestionButtons => {\n  const suggestionButtons: HTMLButtonElement[] = [];\n  // render() runs on every message change; only announce agent-pushed chips\n  // when the visible set actually changes, not on each re-render pass.\n  let lastAgentShownKey: string | null = null;\n\n  const render = (\n    chips: string[] | undefined,\n    session: AgentWidgetSession,\n    textarea: HTMLTextAreaElement,\n    messages?: AgentWidgetMessage[],\n    chipsConfig?: AgentWidgetSuggestionChipsConfig,\n    opts?: SuggestionRenderOptions\n  ) => {\n    container.innerHTML = \"\";\n    suggestionButtons.length = 0;\n    const agentPushed = opts?.agentPushed === true;\n    if (!agentPushed) lastAgentShownKey = null;\n    if (!chips || !chips.length) return;\n\n    // Hide config suggestions after the first user message is sent.\n    // Agent-pushed chips skip this gate: their visibility is the caller's\n    // latest-turn rule (last suggest_replies call with no user message after).\n    // Use provided messages or get from session\n    if (!agentPushed) {\n      const messagesToCheck = messages ?? (session ? session.getMessages() : []);\n      const hasUserMessage = messagesToCheck.some((msg) => msg.role === \"user\");\n      if (hasUserMessage) return;\n    }\n\n    const fragment = document.createDocumentFragment();\n    const streaming = session ? session.isStreaming() : false;\n\n    // Get font family mapping function\n    const getFontFamilyValue = (family: \"sans-serif\" | \"serif\" | \"mono\"): string => {\n      switch (family) {\n        case \"serif\":\n          return 'Georgia, \"Times New Roman\", Times, serif';\n        case \"mono\":\n          return '\"Courier New\", Courier, \"Lucida Console\", Monaco, monospace';\n        case \"sans-serif\":\n        default:\n          return '-apple-system, BlinkMacSystemFont, \"Segoe UI\", \"Helvetica Neue\", Arial, sans-serif';\n      }\n    };\n\n    chips.forEach((chip) => {\n      const btn = createElement(\n        \"button\",\n        \"persona-rounded-button persona-bg-persona-surface persona-px-3 persona-py-1.5 persona-text-xs persona-font-medium persona-text-persona-primary hover:persona-opacity-80 persona-cursor-pointer persona-border persona-border-persona-border\"\n      ) as HTMLButtonElement;\n      btn.type = \"button\";\n      btn.textContent = chip;\n      btn.disabled = streaming;\n\n      // Apply typography settings\n      if (chipsConfig?.fontFamily) {\n        btn.style.fontFamily = getFontFamilyValue(chipsConfig.fontFamily);\n      }\n      if (chipsConfig?.fontWeight) {\n        btn.style.fontWeight = chipsConfig.fontWeight;\n      }\n\n      // Apply padding settings\n      if (chipsConfig?.paddingX) {\n        btn.style.paddingLeft = chipsConfig.paddingX;\n        btn.style.paddingRight = chipsConfig.paddingX;\n      }\n      if (chipsConfig?.paddingY) {\n        btn.style.paddingTop = chipsConfig.paddingY;\n        btn.style.paddingBottom = chipsConfig.paddingY;\n      }\n\n      btn.addEventListener(\"click\", () => {\n        if (!session || session.isStreaming()) return;\n        textarea.value = \"\";\n        if (agentPushed) {\n          container.dispatchEvent(\n            new CustomEvent(\"persona:suggestReplies:selected\", {\n              detail: { suggestion: chip },\n              bubbles: true,\n              composed: true,\n            })\n          );\n        }\n        session.sendMessage(chip);\n      });\n      fragment.appendChild(btn);\n      suggestionButtons.push(btn);\n    });\n    container.appendChild(fragment);\n    if (agentPushed) {\n      const shownKey = JSON.stringify(chips);\n      if (shownKey !== lastAgentShownKey) {\n        lastAgentShownKey = shownKey;\n        container.dispatchEvent(\n          new CustomEvent(\"persona:suggestReplies:shown\", {\n            detail: { suggestions: [...chips] },\n            bubbles: true,\n            composed: true,\n          })\n        );\n      }\n    }\n  };\n\n  return {\n    buttons: suggestionButtons,\n    render\n  };\n};\n\n\n\n","import type { SSEEventRecord } from \"../types\";\nimport type { EventStreamStore } from \"./event-stream-store\";\n\nexport class EventStreamBuffer {\n  private buffer: SSEEventRecord[];\n  private head = 0;\n  private count = 0;\n  private totalCaptured = 0;\n  private eventTypesSet = new Set<string>();\n  private readonly maxSize: number;\n  private readonly store: EventStreamStore | null;\n\n  constructor(maxSize = 2000, store: EventStreamStore | null = null) {\n    this.maxSize = maxSize;\n    this.buffer = new Array(maxSize);\n    this.store = store;\n  }\n\n  push(event: SSEEventRecord): void {\n    this.buffer[this.head] = event;\n    this.head = (this.head + 1) % this.maxSize;\n    if (this.count < this.maxSize) {\n      this.count++;\n    }\n    this.totalCaptured++;\n    this.eventTypesSet.add(event.type);\n    this.store?.put(event);\n  }\n\n  getAll(): SSEEventRecord[] {\n    if (this.count === 0) return [];\n    if (this.count < this.maxSize) {\n      return this.buffer.slice(0, this.count);\n    }\n    // Buffer is full, items wrap around\n    return [\n      ...this.buffer.slice(this.head, this.maxSize),\n      ...this.buffer.slice(0, this.head)\n    ];\n  }\n\n  async restore(): Promise<number> {\n    if (!this.store) return 0;\n    const events = await this.store.getAll();\n    if (events.length === 0) return 0;\n\n    // Take the most recent maxSize events (store returns sorted by timestamp)\n    const toLoad = events.length > this.maxSize\n      ? events.slice(events.length - this.maxSize)\n      : events;\n\n    // Populate the ring buffer without writing back to the store\n    for (const event of toLoad) {\n      this.buffer[this.head] = event;\n      this.head = (this.head + 1) % this.maxSize;\n      if (this.count < this.maxSize) {\n        this.count++;\n      }\n      this.eventTypesSet.add(event.type);\n    }\n    this.totalCaptured = events.length;\n\n    return toLoad.length;\n  }\n\n  getAllFromStore(): Promise<SSEEventRecord[]> {\n    if (this.store) {\n      return this.store.getAll();\n    }\n    return Promise.resolve(this.getAll());\n  }\n\n  getRecent(count: number): SSEEventRecord[] {\n    const all = this.getAll();\n    if (count >= all.length) return all;\n    return all.slice(all.length - count);\n  }\n\n  getSize(): number {\n    return this.count;\n  }\n\n  getTotalCaptured(): number {\n    return this.totalCaptured;\n  }\n\n  getEvictedCount(): number {\n    return this.totalCaptured - this.count;\n  }\n\n  clear(): void {\n    this.buffer = new Array(this.maxSize);\n    this.head = 0;\n    this.count = 0;\n    this.totalCaptured = 0;\n    this.eventTypesSet.clear();\n    this.store?.clear();\n  }\n\n  destroy(): void {\n    this.buffer = [];\n    this.head = 0;\n    this.count = 0;\n    this.totalCaptured = 0;\n    this.eventTypesSet.clear();\n    this.store?.destroy();\n  }\n\n  getEventTypes(): string[] {\n    return Array.from(this.eventTypesSet);\n  }\n}\n","import type { SSEEventRecord } from \"../types\";\n\nexport class EventStreamStore {\n  private db: IDBDatabase | null = null;\n  private pendingWrites: SSEEventRecord[] = [];\n  private flushScheduled = false;\n  private isDestroyed = false;\n  private readonly dbName: string;\n  private readonly storeName: string;\n\n  constructor(dbName = \"persona-event-stream\", storeName = \"events\") {\n    this.dbName = dbName;\n    this.storeName = storeName;\n  }\n\n  open(): Promise<void> {\n    return new Promise((resolve, reject) => {\n      try {\n        const request = indexedDB.open(this.dbName, 1);\n\n        request.onupgradeneeded = () => {\n          const db = request.result;\n          if (!db.objectStoreNames.contains(this.storeName)) {\n            const store = db.createObjectStore(this.storeName, { keyPath: \"id\" });\n            store.createIndex(\"timestamp\", \"timestamp\", { unique: false });\n          }\n        };\n\n        request.onsuccess = () => {\n          this.db = request.result;\n          resolve();\n        };\n\n        request.onerror = () => {\n          reject(request.error);\n        };\n      } catch (err) {\n        reject(err);\n      }\n    });\n  }\n\n  put(event: SSEEventRecord): void {\n    if (!this.db || this.isDestroyed) return;\n    this.pendingWrites.push(event);\n    if (!this.flushScheduled) {\n      this.flushScheduled = true;\n      queueMicrotask(() => this.flushWrites());\n    }\n  }\n\n  putBatch(events: SSEEventRecord[]): void {\n    if (!this.db || this.isDestroyed || events.length === 0) return;\n    try {\n      const tx = this.db.transaction(this.storeName, \"readwrite\");\n      const store = tx.objectStore(this.storeName);\n      for (const event of events) {\n        store.put(event);\n      }\n    } catch {\n      // Silently fail - IndexedDB writes are best-effort\n    }\n  }\n\n  getAll(): Promise<SSEEventRecord[]> {\n    return new Promise((resolve, reject) => {\n      if (!this.db) {\n        resolve([]);\n        return;\n      }\n      try {\n        const tx = this.db.transaction(this.storeName, \"readonly\");\n        const store = tx.objectStore(this.storeName);\n        const index = store.index(\"timestamp\");\n        const request = index.getAll();\n\n        request.onsuccess = () => {\n          resolve(request.result as SSEEventRecord[]);\n        };\n\n        request.onerror = () => {\n          reject(request.error);\n        };\n      } catch (err) {\n        reject(err);\n      }\n    });\n  }\n\n  getCount(): Promise<number> {\n    return new Promise((resolve, reject) => {\n      if (!this.db) {\n        resolve(0);\n        return;\n      }\n      try {\n        const tx = this.db.transaction(this.storeName, \"readonly\");\n        const store = tx.objectStore(this.storeName);\n        const request = store.count();\n\n        request.onsuccess = () => {\n          resolve(request.result);\n        };\n\n        request.onerror = () => {\n          reject(request.error);\n        };\n      } catch (err) {\n        reject(err);\n      }\n    });\n  }\n\n  clear(): Promise<void> {\n    return new Promise((resolve, reject) => {\n      if (!this.db) {\n        resolve();\n        return;\n      }\n      this.pendingWrites = [];\n      try {\n        const tx = this.db.transaction(this.storeName, \"readwrite\");\n        const store = tx.objectStore(this.storeName);\n        const request = store.clear();\n\n        request.onsuccess = () => {\n          resolve();\n        };\n\n        request.onerror = () => {\n          reject(request.error);\n        };\n      } catch (err) {\n        reject(err);\n      }\n    });\n  }\n\n  close(): void {\n    if (this.db) {\n      this.db.close();\n      this.db = null;\n    }\n  }\n\n  destroy(): Promise<void> {\n    this.isDestroyed = true;\n    this.pendingWrites = [];\n    this.close();\n    return new Promise((resolve, reject) => {\n      try {\n        const request = indexedDB.deleteDatabase(this.dbName);\n\n        request.onsuccess = () => {\n          resolve();\n        };\n\n        request.onerror = () => {\n          reject(request.error);\n        };\n      } catch (err) {\n        reject(err);\n      }\n    });\n  }\n\n  private flushWrites(): void {\n    this.flushScheduled = false;\n    if (!this.db || this.isDestroyed || this.pendingWrites.length === 0) return;\n    const toWrite = this.pendingWrites;\n    this.pendingWrites = [];\n    try {\n      const tx = this.db.transaction(this.storeName, \"readwrite\");\n      const store = tx.objectStore(this.storeName);\n      for (const event of toWrite) {\n        store.put(event);\n      }\n    } catch {\n      // Silently fail - IndexedDB writes are best-effort\n    }\n  }\n}\n","// ============================================================================\n// Output Throughput Tracker\n// ============================================================================\n//\n// Derives an output tokens-per-second metric from the widget's existing SSE\n// event stream, for display in the Events diagnostics screen. This is a passive\n// consumer: it never mutates dispatch payloads, never forces debug mode, and\n// never changes the wire contract: it only inspects the `(type, payload)`\n// events that already flow through the SSE tap.\n//\n// Throughput is estimated live from visible text deltas while a run streams,\n// then prefers exact provider usage (output tokens) when terminal events carry\n// it. A run starts when the stream starts (or lazily on the first visible\n// delta), stays \"running\" across intermediate step/turn completions, and only\n// finalizes on terminal `flow_complete` / `agent_complete`. Stream errors mark\n// the metric unavailable rather than leaving it stuck \"running\".\n\nexport type ThroughputMetricStatus = \"idle\" | \"running\" | \"complete\" | \"error\";\n\nexport type ThroughputMetricSource = \"usage\" | \"estimate\";\n\nexport interface ThroughputMetric {\n  status: ThroughputMetricStatus;\n  /** Output tokens per second, when computable. */\n  tokensPerSecond?: number;\n  /** Output tokens counted/estimated for the run. */\n  outputTokens?: number;\n  /** Duration window the rate was computed over (ms). */\n  durationMs?: number;\n  /** Whether `outputTokens` came from provider usage or a text estimate. */\n  source?: ThroughputMetricSource;\n}\n\ninterface ThroughputRunStats {\n  startedAt: number;\n  firstDeltaAt?: number;\n  /**\n   * Running character count of accumulated visible text, estimated via the\n   * ~4 chars/token heuristic. Tracked as a counter (not the concatenated\n   * string) so the estimate stays O(1) per delta over a long stream.\n   */\n  visibleCharCount: number;\n  exactOutputTokens: number;\n}\n\n// Below this streamed window we don't trust the rate; fall back to provider\n// execution time or whole-request duration instead.\nconst THROUGHPUT_MIN_DURATION_MS = 250;\n\n// Request-level lifecycle events: each marks the beginning of a NEW request.\n// The SSE tap fires for every payload type regardless of whether the client has\n// a handler for it, so any of these that the server emits starts the run with\n// an accurate `startedAt` (capturing time-to-first-token). These RESET any run\n// already in progress, so a prior stream that ended without a terminal/error\n// frame (e.g. `session.cancel()`) doesn't bleed its tokens into the next one.\nconst REQUEST_START_EVENTS = new Set([\n  \"flow_start\",\n  \"flow_run_start\",\n  \"agent_start\",\n  \"dispatch_start\",\n  \"run_start\",\n]);\n\n// Per-step markers that fire repeatedly WITHIN a single request (a flow emits\n// one per step). These only lazily begin a run: they must never reset, or a\n// multi-step response would restart the metric between steps. If no request- or\n// step-start event is emitted, the first visible delta lazily starts the run.\nconst STEP_START_EVENTS = new Set([\"step_start\", \"execution_start\"]);\n\nconst VISIBLE_DELTA_EVENTS = new Set([\n  \"step_delta\",\n  \"step_chunk\",\n  \"chunk\",\n  \"agent_turn_delta\",\n]);\n\nconst INTERMEDIATE_COMPLETE_EVENTS = new Set([\n  \"step_complete\",\n  \"agent_turn_complete\",\n]);\n\nconst TERMINAL_COMPLETE_EVENTS = new Set([\"flow_complete\", \"agent_complete\"]);\n\nconst ERROR_EVENTS = new Set([\n  \"step_error\",\n  \"flow_error\",\n  \"agent_error\",\n  \"dispatch_error\",\n  \"error\",\n]);\n\nconst isRecord = (value: unknown): value is Record<string, unknown> =>\n  typeof value === \"object\" && value !== null && !Array.isArray(value);\n\nconst toFiniteNumber = (value: unknown): number | undefined =>\n  typeof value === \"number\" && Number.isFinite(value) ? value : undefined;\n\nconst getRecord = (\n  value: Record<string, unknown>,\n  key: string\n): Record<string, unknown> | undefined => {\n  const nested = value[key];\n  return isRecord(nested) ? nested : undefined;\n};\n\n/** Token estimate from a character count: ~4 chars/token, floor of 1. */\nfunction estimateTokensFromCharCount(charCount: number): number {\n  return charCount > 0 ? Math.max(1, Math.ceil(charCount / 4)) : 0;\n}\n\n/** Simple token estimate matching the dashboard heuristic: ~4 chars/token. */\nexport function estimateOutputTokens(text: string): number {\n  return estimateTokensFromCharCount(text.trim().length);\n}\n\nfunction calculateTokensPerSecond(\n  outputTokens: number,\n  durationMs: number | undefined\n): number | undefined {\n  if (\n    outputTokens <= 0 ||\n    durationMs === undefined ||\n    durationMs < THROUGHPUT_MIN_DURATION_MS\n  ) {\n    return undefined;\n  }\n  return outputTokens / (durationMs / 1000);\n}\n\nfunction resolveEventType(\n  eventType: string,\n  payload: Record<string, unknown>\n): string {\n  return typeof payload.type === \"string\" ? payload.type : eventType;\n}\n\nfunction getTextDelta(payload: Record<string, unknown>): string {\n  if (typeof payload.text === \"string\") return payload.text;\n  if (typeof payload.delta === \"string\") return payload.delta;\n  if (typeof payload.content === \"string\") return payload.content;\n  if (typeof payload.chunk === \"string\") return payload.chunk;\n  return \"\";\n}\n\n/**\n * Only count visible model output.\n *\n * For `agent_turn_delta`, count only a contentType of exactly `text` as\n * visible, matching the client renderer (which appends streaming assistant\n * text only when `contentType === \"text\"`); `thinking`, `tool_input`, any\n * other value, and a missing contentType are ignored so throughput never\n * includes deltas the chat UI doesn't render.\n *\n * For `step_delta` / `step_chunk`, skip tool and context steps: those carry\n * tool I/O, not model-visible text: mirroring the widget's own renderer.\n */\nfunction isVisibleTextDelta(\n  type: string,\n  payload: Record<string, unknown>\n): boolean {\n  if (type === \"step_delta\" || type === \"step_chunk\") {\n    return payload.stepType !== \"tool\" && payload.executionType !== \"context\";\n  }\n\n  if (type !== \"agent_turn_delta\") return true;\n\n  const contentType =\n    typeof payload.contentType === \"string\"\n      ? payload.contentType\n      : typeof payload.content_type === \"string\"\n        ? payload.content_type\n        : undefined;\n\n  return contentType === \"text\";\n}\n\n/** Extract exact output tokens from a variety of usage payload shapes. */\nfunction getOutputTokens(payload: Record<string, unknown>): number | undefined {\n  const result = getRecord(payload, \"result\");\n  const candidates = [\n    getRecord(payload, \"tokens\"),\n    getRecord(payload, \"totalTokens\"),\n    result ? getRecord(result, \"tokens\") : undefined,\n    getRecord(payload, \"usage\"),\n    result ? getRecord(result, \"usage\") : undefined,\n  ];\n\n  for (const candidate of candidates) {\n    if (!candidate) continue;\n    const outputTokens =\n      toFiniteNumber(candidate.output) ??\n      toFiniteNumber(candidate.outputTokens) ??\n      toFiniteNumber(candidate.completionTokens);\n    if (outputTokens !== undefined) return outputTokens;\n  }\n\n  return (\n    toFiniteNumber(payload.outputTokens) ??\n    toFiniteNumber(payload.completionTokens) ??\n    (result\n      ? (toFiniteNumber(result.outputTokens) ??\n        toFiniteNumber(result.completionTokens))\n      : undefined)\n  );\n}\n\n/** Extract provider execution time (ms) from a variety of payload shapes. */\nfunction getExecutionTimeMs(\n  payload: Record<string, unknown>\n): number | undefined {\n  const result = getRecord(payload, \"result\");\n  return (\n    toFiniteNumber(payload.executionTime) ??\n    toFiniteNumber(payload.executionTimeMs) ??\n    toFiniteNumber(payload.execution_time) ??\n    toFiniteNumber(payload.duration) ??\n    (result\n      ? (toFiniteNumber(result.executionTime) ??\n        toFiniteNumber(result.executionTimeMs))\n      : undefined)\n  );\n}\n\nfunction defaultClock(): number {\n  if (\n    typeof performance !== \"undefined\" &&\n    typeof performance.now === \"function\"\n  ) {\n    return performance.now();\n  }\n  return Date.now();\n}\n\n/**\n * Tracks output throughput across one streamed run at a time. Feed it every SSE\n * event via {@link processEvent}; read the current state via {@link getMetric}.\n */\nexport class ThroughputTracker {\n  private metric: ThroughputMetric = { status: \"idle\" };\n  private run: ThroughputRunStats | null = null;\n  private readonly now: () => number;\n\n  constructor(now: () => number = defaultClock) {\n    this.now = now;\n  }\n\n  getMetric(): ThroughputMetric {\n    // While a run is streaming, recompute the elapsed window (and rate) from the\n    // clock on each read. The view polls this every ~200ms, so without this a\n    // pause between deltas would keep showing the stale rate from the last\n    // event; recomputing lets the displayed tok/s decay as time passes.\n    const run = this.run;\n    if (\n      run &&\n      this.metric.status === \"running\" &&\n      run.firstDeltaAt !== undefined &&\n      this.metric.outputTokens !== undefined\n    ) {\n      const durationMs = this.now() - run.firstDeltaAt;\n      return {\n        ...this.metric,\n        durationMs,\n        tokensPerSecond: calculateTokensPerSecond(\n          this.metric.outputTokens,\n          durationMs\n        ),\n      };\n    }\n    return this.metric;\n  }\n\n  /** Reset back to idle (e.g. when the chat is cleared). */\n  reset(): void {\n    this.run = null;\n    this.metric = { status: \"idle\" };\n  }\n\n  private startRun(now: number): void {\n    this.run = {\n      startedAt: now,\n      visibleCharCount: 0,\n      exactOutputTokens: 0,\n    };\n    this.metric = { status: \"running\" };\n  }\n\n  processEvent(eventType: string, payload: unknown): void {\n    if (!isRecord(payload)) {\n      // Non-object payloads can still signal lifecycle (e.g. bare \"error\").\n      if (ERROR_EVENTS.has(eventType) && this.run) {\n        this.run = null;\n        this.metric = { status: \"error\" };\n      }\n      return;\n    }\n\n    const type = resolveEventType(eventType, payload);\n    const now = this.now();\n\n    if (REQUEST_START_EVENTS.has(type)) {\n      // New request: start fresh, discarding any incomplete prior run.\n      this.startRun(now);\n      return;\n    }\n\n    if (STEP_START_EVENTS.has(type)) {\n      // Mid-request step marker: only begin a run if none is active.\n      if (!this.run) this.startRun(now);\n      return;\n    }\n\n    if (VISIBLE_DELTA_EVENTS.has(type)) {\n      if (!isVisibleTextDelta(type, payload)) return;\n      const text = getTextDelta(payload);\n      if (!text) return;\n\n      // Lazily start a run if the stream began without a recognized start event.\n      if (!this.run) this.startRun(now);\n      const stats = this.run!;\n\n      stats.firstDeltaAt ??= now;\n      stats.visibleCharCount += text.length;\n\n      // Add the live char estimate of the CURRENT (not-yet-completed) step on\n      // top of any exact usage already booked from completed steps, so the\n      // count only grows: it never drops back to a bare estimate mid-run.\n      const outputTokens =\n        stats.exactOutputTokens +\n        estimateTokensFromCharCount(stats.visibleCharCount);\n      const durationMs = now - stats.firstDeltaAt;\n      this.metric = {\n        status: \"running\",\n        tokensPerSecond: calculateTokensPerSecond(outputTokens, durationMs),\n        outputTokens,\n        durationMs,\n        source: stats.exactOutputTokens > 0 ? \"usage\" : \"estimate\",\n      };\n      return;\n    }\n\n    if (INTERMEDIATE_COMPLETE_EVENTS.has(type)) {\n      // Accumulate exact usage but keep the run going: these fire per\n      // step/turn, not at the end of the whole run.\n      if (!this.run) return;\n      const stats = this.run;\n      const exact = getOutputTokens(payload);\n      if (exact !== undefined) {\n        stats.exactOutputTokens += exact;\n        // This step's visible text is now represented exactly by provider\n        // usage: drop it from the running char estimate so the two don't\n        // double-count once the next step starts streaming.\n        stats.visibleCharCount = 0;\n      }\n\n      const usingExact = stats.exactOutputTokens > 0;\n      const outputTokens =\n        stats.exactOutputTokens +\n        estimateTokensFromCharCount(stats.visibleCharCount);\n      const durationMs = this.resolveDuration(stats, payload, now);\n      this.metric = {\n        status: \"running\",\n        tokensPerSecond: calculateTokensPerSecond(outputTokens, durationMs),\n        outputTokens,\n        durationMs,\n        source: usingExact ? \"usage\" : \"estimate\",\n      };\n      return;\n    }\n\n    if (TERMINAL_COMPLETE_EVENTS.has(type)) {\n      if (!this.run) return;\n      const stats = this.run;\n      // Prefer exact output tokens from this terminal event, else accumulated\n      // usage from intermediate completes, else the text estimate.\n      const terminalExact = getOutputTokens(payload);\n      // Prefer a total from the terminal event; otherwise sum exact usage booked\n      // from intermediate completes plus the estimate of any still-streamed text\n      // not yet covered by a usage report.\n      const outputTokens =\n        terminalExact ??\n        stats.exactOutputTokens +\n          estimateTokensFromCharCount(stats.visibleCharCount);\n      const source: ThroughputMetricSource =\n        terminalExact !== undefined || stats.exactOutputTokens > 0\n          ? \"usage\"\n          : \"estimate\";\n      const durationMs = this.resolveDuration(stats, payload, now);\n      this.metric = {\n        status: \"complete\",\n        tokensPerSecond: calculateTokensPerSecond(outputTokens, durationMs),\n        outputTokens,\n        durationMs,\n        source,\n      };\n      this.run = null;\n      return;\n    }\n\n    if (ERROR_EVENTS.has(type)) {\n      if (!this.run) return;\n      this.run = null;\n      this.metric = { status: \"error\" };\n    }\n  }\n\n  /**\n   * Prefer the streamed visible-output window when it clears the minimum\n   * threshold; otherwise fall back to provider execution time, then to the\n   * whole-request duration.\n   */\n  private resolveDuration(\n    stats: ThroughputRunStats,\n    payload: Record<string, unknown>,\n    now: number\n  ): number {\n    const streamedDurationMs =\n      stats.firstDeltaAt !== undefined ? now - stats.firstDeltaAt : undefined;\n    if (\n      streamedDurationMs !== undefined &&\n      streamedDurationMs >= THROUGHPUT_MIN_DURATION_MS\n    ) {\n      return streamedDurationMs;\n    }\n    const executionTimeMs = getExecutionTimeMs(payload);\n    return executionTimeMs ?? now - stats.startedAt;\n  }\n}\n","import { createElement } from \"../utils/dom\";\nimport { renderLucideIcon } from \"../utils/icons\";\nimport {\n  createFollowStateController,\n  isElementNearBottom,\n  resolveFollowStateFromScroll,\n  resolveFollowStateFromWheel\n} from \"../utils/auto-follow\";\nimport type { EventStreamBuffer } from \"../utils/event-stream-buffer\";\nimport type { ThroughputMetric } from \"../utils/throughput-tracker\";\nimport type {\n  SSEEventRecord,\n  AgentWidgetConfig,\n  EventStreamConfig,\n  EventStreamBadgeColor,\n} from \"../types\";\nimport type { AgentWidgetPlugin } from \"../plugins/types\";\n\n// ============================================================================\n// Helpers\n// ============================================================================\n\n/** Append custom class names to an element if provided. */\nfunction applyCustomClasses(el: HTMLElement, classes?: string): void {\n  if (classes) {\n    classes.split(/\\s+/).forEach((c) => c && el.classList.add(c));\n  }\n}\n\n// ============================================================================\n// Constants\n// ============================================================================\n\nconst DEFAULT_BADGE_COLORS: Record<string, EventStreamBadgeColor> = {\n  flow_: { bg: \"var(--persona-palette-colors-success-100, #dcfce7)\", text: \"var(--persona-palette-colors-success-700, #166534)\" },\n  step_: { bg: \"var(--persona-palette-colors-primary-100, #f5f5f5)\", text: \"var(--persona-palette-colors-primary-700, #0a0a0a)\" },\n  reason_: { bg: \"var(--persona-palette-colors-warning-100, #ffedd5)\", text: \"var(--persona-palette-colors-warning-700, #9a3412)\" },\n  tool_: { bg: \"var(--persona-palette-colors-purple-100, #f3e8ff)\", text: \"var(--persona-palette-colors-purple-700, #6b21a8)\" },\n  agent_: { bg: \"var(--persona-palette-colors-teal-100, #ccfbf1)\", text: \"var(--persona-palette-colors-teal-700, #115e59)\" },\n  error: { bg: \"var(--persona-palette-colors-error-100, #fecaca)\", text: \"var(--persona-palette-colors-error-700, #991b1b)\" },\n};\nconst DEFAULT_BADGE_COLOR: EventStreamBadgeColor = {\n  bg: \"var(--persona-palette-colors-gray-100, #f3f4f6)\",\n  text: \"var(--persona-palette-colors-gray-600, #4b5563)\",\n};\n\nconst DEFAULT_DESCRIPTION_FIELDS = [\n  \"flowName\",\n  \"stepName\",\n  \"reasoningText\",\n  \"text\",\n  \"name\",\n  \"tool\",\n  \"toolName\",\n];\n\n// Minimum interval between renders (ms)\nconst UPDATE_THROTTLE_MS = 100;\n\n// ============================================================================\n// Helper Functions\n// ============================================================================\n\nfunction getBadgeColor(\n  eventType: string,\n  customColors?: Record<string, EventStreamBadgeColor>\n): EventStreamBadgeColor {\n  const allColors = { ...DEFAULT_BADGE_COLORS, ...customColors };\n  // Exact match first\n  if (allColors[eventType]) return allColors[eventType];\n  // Prefix match (keys ending with \"_\")\n  for (const prefix of Object.keys(allColors)) {\n    if (prefix.endsWith(\"_\") && eventType.startsWith(prefix)) {\n      return allColors[prefix];\n    }\n  }\n  return DEFAULT_BADGE_COLOR;\n}\n\nfunction formatRelativeTimestamp(ms: number, firstEventMs: number): string {\n  const delta = (ms - firstEventMs) / 1000;\n  return `+${delta.toFixed(3)}s`;\n}\n\nfunction formatAbsoluteTimestamp(ms: number): string {\n  const d = new Date(ms);\n  const hh = String(d.getHours()).padStart(2, \"0\");\n  const mm = String(d.getMinutes()).padStart(2, \"0\");\n  const ss = String(d.getSeconds()).padStart(2, \"0\");\n  const mmm = String(d.getMilliseconds()).padStart(3, \"0\");\n  return `${hh}:${mm}:${ss}.${mmm}`;\n}\n\nfunction extractDescription(\n  payload: string,\n  fields: string[]\n): string | null {\n  try {\n    const obj = JSON.parse(payload) as Record<string, unknown>;\n    if (typeof obj !== \"object\" || obj === null) return null;\n    for (const field of fields) {\n      const parts = field.split(\".\");\n      let current: unknown = obj;\n      for (const part of parts) {\n        if (current && typeof current === \"object\" && current !== null) {\n          current = (current as Record<string, unknown>)[part];\n        } else {\n          current = undefined;\n          break;\n        }\n      }\n      if (typeof current === \"string\" && current.trim()) return current.trim();\n    }\n  } catch {\n    // Not JSON, no description\n  }\n  return null;\n}\n\nfunction copyToClipboard(text: string): Promise<void> {\n  if (navigator.clipboard?.writeText) {\n    return navigator.clipboard.writeText(text);\n  }\n  return new Promise((resolve) => {\n    const textarea = document.createElement(\"textarea\");\n    textarea.value = text;\n    textarea.style.position = \"fixed\";\n    textarea.style.opacity = \"0\";\n    document.body.appendChild(textarea);\n    textarea.select();\n    document.execCommand(\"copy\");\n    document.body.removeChild(textarea);\n    resolve();\n  });\n}\n\nfunction formatEventForCopy(event: SSEEventRecord): string {\n  let formattedPayload: unknown;\n  try {\n    formattedPayload = JSON.parse(event.payload);\n  } catch {\n    formattedPayload = event.payload;\n  }\n  return JSON.stringify(\n    {\n      type: event.type,\n      timestamp: new Date(event.timestamp).toISOString(),\n      payload: formattedPayload,\n    },\n    null,\n    2\n  );\n}\n\n// ============================================================================\n// Output Throughput Summary\n// ============================================================================\n\n/** Format the headline value, e.g. `23.9 tok/s` or `-- tok/s` when unavailable. */\nfunction formatThroughputValue(metric: ThroughputMetric): string {\n  if (\n    metric.tokensPerSecond === undefined ||\n    !Number.isFinite(metric.tokensPerSecond)\n  ) {\n    return \"-- tok/s\";\n  }\n  return `${metric.tokensPerSecond.toFixed(1)} tok/s`;\n}\n\n/** Compact supporting details: output tokens, duration, and usage/estimate source. */\nfunction formatThroughputMeta(metric: ThroughputMetric): string {\n  const parts: string[] = [];\n  if (metric.outputTokens !== undefined) {\n    parts.push(`${metric.outputTokens.toLocaleString()} tok`);\n  }\n  if (metric.durationMs !== undefined) {\n    parts.push(`${(metric.durationMs / 1000).toFixed(2)}s`);\n  }\n  if (metric.source) {\n    parts.push(metric.source);\n  }\n  return parts.join(\" · \");\n}\n\n// ============================================================================\n// Inline Payload Component\n// ============================================================================\n\nfunction renderInlinePayload(\n  event: SSEEventRecord,\n  plugins: AgentWidgetPlugin[],\n  config: AgentWidgetConfig | undefined\n): HTMLElement {\n  let formattedPayload: string;\n  let parsedPayload: unknown;\n  try {\n    parsedPayload = JSON.parse(event.payload);\n    formattedPayload = JSON.stringify(parsedPayload, null, 2);\n  } catch {\n    parsedPayload = event.payload;\n    formattedPayload = event.payload;\n  }\n\n  // Plugin hook: renderEventStreamPayload\n  const payloadPlugin = plugins.find((p) => p.renderEventStreamPayload);\n  if (payloadPlugin?.renderEventStreamPayload && config) {\n    const customPayload = payloadPlugin.renderEventStreamPayload({\n      event,\n      config,\n      defaultRenderer: () => renderDefaultPayload(),\n      parsedPayload,\n    });\n    if (customPayload) return customPayload;\n  }\n\n  return renderDefaultPayload();\n\n  function renderDefaultPayload(): HTMLElement {\n    const payloadContainer = createElement(\n      \"div\",\n      \"persona-bg-persona-container persona-border-t persona-border-persona-divider persona-px-3 persona-py-2 persona-ml-4 persona-mr-3 persona-mb-1 persona-rounded-b persona-overflow-auto persona-max-h-[300px]\"\n    );\n\n    const payloadContent = createElement(\n      \"pre\",\n      \"persona-m-0 persona-whitespace-pre-wrap persona-break-all persona-text-[11px] persona-text-persona-secondary persona-font-mono\"\n    );\n    payloadContent.textContent = formattedPayload;\n\n    payloadContainer.appendChild(payloadContent);\n    return payloadContainer;\n  }\n}\n\n// ============================================================================\n// Event Row Component\n// ============================================================================\n\nfunction renderEventRow(\n  event: SSEEventRecord,\n  index: number,\n  firstTimestamp: number,\n  esConfig: EventStreamConfig,\n  expandedSet: Set<string>,\n  onToggleExpand: (eventId: string) => void,\n  plugins: AgentWidgetPlugin[],\n  config: AgentWidgetConfig | undefined\n): HTMLElement {\n  const isExpanded = expandedSet.has(event.id);\n  const wrapper = createElement(\n    \"div\",\n    \"persona-border-b persona-border-persona-divider persona-text-xs\"\n  );\n  applyCustomClasses(wrapper, esConfig.classNames?.eventRow);\n\n  // Plugin hook: renderEventStreamRow\n  const rowPlugin = plugins.find((p) => p.renderEventStreamRow);\n  if (rowPlugin?.renderEventStreamRow && config) {\n    const customRow = rowPlugin.renderEventStreamRow({\n      event,\n      index,\n      config,\n      defaultRenderer: () => buildDefaultRowContent(),\n      isExpanded,\n      onToggleExpand: () => onToggleExpand(event.id),\n    });\n    if (customRow) {\n      wrapper.appendChild(customRow);\n      return wrapper;\n    }\n  }\n\n  wrapper.appendChild(buildDefaultRowContent());\n  return wrapper;\n\n  function buildDefaultRowContent(): HTMLElement {\n    const container = createElement(\"div\", \"\");\n\n    // Main row line\n    const row = createElement(\n      \"div\",\n      \"persona-flex persona-items-center persona-gap-2 persona-px-3 persona-py-3 hover:persona-bg-persona-container persona-cursor-pointer persona-group\"\n    );\n    row.setAttribute(\"data-event-id\", event.id);\n\n    // 1. Chevron (expand/collapse)\n    const chevron = createElement(\n      \"span\",\n      \"persona-flex-shrink-0 persona-text-persona-muted persona-w-4 persona-text-center persona-flex persona-items-center persona-justify-center\"\n    );\n    const chevronIcon = renderLucideIcon(\n      isExpanded ? \"chevron-down\" : \"chevron-right\",\n      \"14px\",\n      \"currentColor\",\n      2\n    );\n    if (chevronIcon) chevron.appendChild(chevronIcon);\n\n    // 2. Timestamp\n    const timestamp = createElement(\n      \"span\",\n      \"persona-text-[11px] persona-text-persona-muted persona-whitespace-nowrap persona-flex-shrink-0 persona-font-mono persona-w-[70px]\"\n    );\n    const tsFormat = esConfig.timestampFormat ?? \"relative\";\n    timestamp.textContent =\n      tsFormat === \"relative\"\n        ? formatRelativeTimestamp(event.timestamp, firstTimestamp)\n        : formatAbsoluteTimestamp(event.timestamp);\n\n    // 3. Sequence number\n    let seqNum: HTMLElement | null = null;\n    if (esConfig.showSequenceNumbers !== false) {\n      seqNum = createElement(\n        \"span\",\n        \"persona-text-[11px] persona-text-persona-muted persona-font-mono persona-flex-shrink-0 persona-w-[28px] persona-text-right\"\n      );\n      seqNum.textContent = String(index + 1);\n    }\n\n    // 4. Color-coded type badge\n    const badgeColor = getBadgeColor(event.type, esConfig.badgeColors);\n    const badge = createElement(\n      \"span\",\n      \"persona-inline-flex persona-items-center persona-px-2 persona-py-0.5 persona-rounded persona-text-[11px] persona-font-mono persona-font-medium persona-whitespace-nowrap persona-flex-shrink-0 persona-border\"\n    );\n    badge.style.backgroundColor = badgeColor.bg;\n    badge.style.color = badgeColor.text;\n    badge.style.borderColor = badgeColor.text + \"50\";\n    badge.textContent = event.type;\n\n    // 5. Description (extracted from payload)\n    const descFields =\n      esConfig.descriptionFields ?? DEFAULT_DESCRIPTION_FIELDS;\n    const desc = extractDescription(event.payload, descFields);\n    let descEl: HTMLElement | null = null;\n    if (desc) {\n      descEl = createElement(\n        \"span\",\n        \"persona-text-[11px] persona-text-persona-secondary persona-truncate persona-min-w-0\"\n      );\n      descEl.textContent = desc;\n    }\n\n    // 6. Spacer\n    const spacer = createElement(\"div\", \"persona-flex-1 persona-min-w-0\");\n\n    // 7. Copy button\n    const copyBtn = createElement(\n      \"button\",\n      \"persona-text-persona-muted hover:persona-text-persona-primary persona-cursor-pointer persona-flex-shrink-0 persona-border-none persona-bg-transparent persona-p-0\"\n    );\n    const clipIcon = renderLucideIcon(\n      \"clipboard\",\n      \"12px\",\n      \"currentColor\",\n      1.5\n    );\n    if (clipIcon) copyBtn.appendChild(clipIcon);\n    copyBtn.addEventListener(\"click\", async (e: Event) => {\n      e.stopPropagation();\n      await copyToClipboard(formatEventForCopy(event));\n      // Visual feedback\n      copyBtn.innerHTML = \"\";\n      const checkIcon = renderLucideIcon(\n        \"check\",\n        \"12px\",\n        \"currentColor\",\n        1.5\n      );\n      if (checkIcon) copyBtn.appendChild(checkIcon);\n      setTimeout(() => {\n        copyBtn.innerHTML = \"\";\n        const restoreIcon = renderLucideIcon(\n          \"clipboard\",\n          \"12px\",\n          \"currentColor\",\n          1.5\n        );\n        if (restoreIcon) copyBtn.appendChild(restoreIcon);\n      }, 1500);\n    });\n\n    // Assemble row\n    row.appendChild(chevron);\n    row.appendChild(timestamp);\n    if (seqNum) row.appendChild(seqNum);\n    row.appendChild(badge);\n    if (descEl) row.appendChild(descEl);\n    row.appendChild(spacer);\n    row.appendChild(copyBtn);\n\n    container.appendChild(row);\n\n    // Expanded payload (inline)\n    if (isExpanded) {\n      container.appendChild(\n        renderInlinePayload(event, plugins, config)\n      );\n    }\n\n    return container;\n  }\n}\n\n// ============================================================================\n// Main View\n// ============================================================================\n\nexport type EventStreamViewOptions = {\n  buffer: EventStreamBuffer;\n  getFullHistory?: () => Promise<SSEEventRecord[]>;\n  onClose?: () => void;\n  config?: AgentWidgetConfig;\n  plugins?: AgentWidgetPlugin[];\n  /**\n   * Optional accessor for the current output-throughput metric, derived from\n   * the same SSE event stream. When provided, a compact \"Output throughput\"\n   * summary row is rendered and refreshed on each update.\n   */\n  getThroughput?: () => ThroughputMetric;\n};\n\nexport function createEventStreamView(\n  options: EventStreamViewOptions\n): {\n  element: HTMLElement;\n  update: () => void;\n  destroy: () => void;\n} {\n  const {\n    buffer,\n    getFullHistory,\n    onClose,\n    config,\n    plugins = [],\n    getThroughput,\n  } = options;\n  const scrollToBottomConfig = config?.features?.scrollToBottom;\n  const scrollToBottomEnabled = scrollToBottomConfig?.enabled !== false;\n  const scrollToBottomIconName = scrollToBottomConfig?.iconName ?? \"arrow-down\";\n  const scrollToBottomLabel = scrollToBottomConfig?.label ?? \"\";\n\n  const esConfig: EventStreamConfig = config?.features?.eventStream ?? {};\n\n  // --- Plugin hook: renderEventStreamView (replace entire view) ---\n  const viewPlugin = plugins.find((p) => p.renderEventStreamView);\n  if (viewPlugin?.renderEventStreamView && config) {\n    const customView = viewPlugin.renderEventStreamView({\n      config,\n      events: buffer.getAll(),\n      defaultRenderer: () => buildDefaultView().element,\n      onClose,\n    });\n    if (customView) {\n      return {\n        element: customView,\n        update: () => {\n          // Plugin manages its own updates\n        },\n        destroy: () => {\n          // Plugin manages its own cleanup\n        },\n      };\n    }\n  }\n\n  return buildDefaultView();\n\n  function buildDefaultView(): {\n    element: HTMLElement;\n    update: () => void;\n    destroy: () => void;\n  } {\n    const customClasses = esConfig.classNames;\n    const container = createElement(\n      \"div\",\n      \"persona-event-stream-view persona-flex persona-flex-col persona-flex-1 persona-min-h-0\"\n    );\n    applyCustomClasses(container, customClasses?.panel);\n\n    // State\n    let filteredEvents: SSEEventRecord[] = [];\n    let selectedType = \"\";\n    let searchTerm = \"\";\n    let searchTimeout: ReturnType<typeof setTimeout> | null = null;\n    let lastKnownTypes: string[] = [];\n    let lastTypeCounts: Record<string, number> = {};\n    let lastFilteredCount = 0;\n    const autoFollow = createFollowStateController();\n    let newEventsSincePause = 0;\n    let lastRenderTime = 0;\n    let pendingUpdate = false;\n    let pendingRafId: number | null = null;\n    let suppressScrollHandler = false;\n    let lastScrollTop = 0;\n    const expandedSet = new Set<string>();\n\n    // Incremental rendering state\n    const rowElements = new Map<string, HTMLElement>();\n    let lastRenderedFilter = \"\";\n    let lastRenderedSearch = \"\";\n    let dirtyExpandId: string | null = null;\n\n    // ========================================================================\n    // Toolbar: Header Bar + Search Bar\n    // ========================================================================\n\n    // Elements we need references to across functions\n    // These are assigned inside buildDefaultToolbar() which always runs\n    let filterSelect!: HTMLSelectElement;\n    let copyAllBtn!: HTMLButtonElement;\n    let searchInput!: HTMLInputElement;\n    let searchClearBtn!: HTMLButtonElement;\n    // Inline \"Throughput <tok/s>\" group, rendered into the header bar next to\n    // the \"Events\" count when getThroughput is provided. The detailed\n    // breakdown is revealed on hover via the native title tooltip.\n    let throughputValueEl: HTMLElement | null = null;\n    let throughputContainer: HTMLElement | null = null;\n    let throughputTooltipEl: HTMLElement | null = null;\n\n    function buildDefaultToolbar(): HTMLElement {\n      const toolbarOuter = createElement(\n        \"div\",\n        \"persona-event-toolbar persona-relative persona-flex persona-flex-col persona-flex-shrink-0\"\n      );\n\n      // --- Header bar ---\n      const headerBar = createElement(\n        \"div\",\n        \"persona-flex persona-items-center persona-gap-2 persona-px-4 persona-py-3 persona-pb-0 persona-border-persona-divider persona-bg-persona-surface persona-overflow-hidden\"\n      );\n      applyCustomClasses(headerBar, customClasses?.headerBar);\n\n      // The header leads straight into the controls. Context already names\n      // itself: the \"All events (N)\" filter option carries the total, the search\n      // placeholder names event payloads, and every row is an event.\n\n      // Inline throughput value, e.g. \"146.3 tok/s\". The \"tok/s\" unit is\n      // self-describing for this developer-facing inspector, so the value stands\n      // alone; its accessible name (\"Throughput: <value>\") and detailed breakdown\n      // live on the container's aria-label / hover tooltip (see updateThroughputSummary).\n      if (getThroughput) {\n        throughputContainer = createElement(\n          \"div\",\n          \"persona-relative persona-flex persona-items-center persona-gap-1.5 persona-whitespace-nowrap\"\n        );\n        throughputContainer.style.cursor = \"help\";\n        throughputValueEl = createElement(\n          \"span\",\n          \"persona-text-[11px] persona-font-mono persona-bg-persona-container persona-text-persona-muted persona-px-2 persona-py-0.5 persona-rounded persona-border persona-border-persona-border persona-tabular-nums\"\n        );\n        throughputValueEl.textContent = \"-- tok/s\";\n\n        // Custom hover tooltip: appears instantly (no native title delay).\n        // Appended to the (non-clipping) toolbar wrapper rather than the header\n        // bar, which has overflow-hidden and would clip a dropdown. Position is\n        // measured on hover so it sits just under the throughput group.\n        throughputTooltipEl = createElement(\n          \"div\",\n          \"persona-absolute persona-z-50 persona-whitespace-nowrap persona-rounded persona-border persona-border-persona-border persona-bg-persona-container persona-text-persona-primary persona-text-[11px] persona-font-mono persona-px-2 persona-py-1 persona-shadow\"\n        );\n        throughputTooltipEl.style.display = \"none\";\n        throughputTooltipEl.style.pointerEvents = \"none\";\n        const group = throughputContainer;\n        const tooltip = throughputTooltipEl;\n        const showTooltip = () => {\n          if (!tooltip.textContent) return;\n          const gRect = group.getBoundingClientRect();\n          const pRect = toolbarOuter.getBoundingClientRect();\n          tooltip.style.left = `${gRect.left - pRect.left}px`;\n          tooltip.style.top = `${gRect.bottom - pRect.top + 4}px`;\n          tooltip.style.display = \"block\";\n        };\n        const hideTooltip = () => {\n          tooltip.style.display = \"none\";\n        };\n        throughputContainer.addEventListener(\"mouseenter\", showTooltip);\n        throughputContainer.addEventListener(\"mouseleave\", hideTooltip);\n\n        throughputContainer.appendChild(throughputValueEl);\n      }\n\n      const headerSpacer = createElement(\"div\", \"persona-flex-1\");\n\n      // Filter dropdown\n      filterSelect = createElement(\n        \"select\",\n        \"persona-text-xs persona-bg-persona-surface persona-border persona-border-persona-border persona-rounded persona-px-2.5 persona-py-1 persona-text-persona-primary persona-cursor-pointer\"\n      ) as HTMLSelectElement;\n      const allOption = createElement(\"option\", \"\") as HTMLOptionElement;\n      allOption.value = \"\";\n      allOption.textContent = \"All events (0)\";\n      filterSelect.appendChild(allOption);\n\n      // Copy All button\n      const iconBtnClass =\n        \"persona-inline-flex persona-items-center persona-gap-1.5 persona-rounded persona-text-xs persona-text-persona-muted hover:persona-bg-persona-container hover:persona-text-persona-primary persona-cursor-pointer persona-border persona-border-persona-border persona-bg-persona-surface persona-flex-shrink-0 persona-px-2.5 persona-py-1\";\n\n      copyAllBtn = createElement(\n        \"button\",\n        iconBtnClass\n      ) as HTMLButtonElement;\n      copyAllBtn.type = \"button\";\n      copyAllBtn.title = \"Copy All\";\n      const copyAllIcon = renderLucideIcon(\n        \"clipboard-copy\",\n        \"12px\",\n        \"currentColor\",\n        1.5\n      );\n      if (copyAllIcon) copyAllBtn.appendChild(copyAllIcon);\n      const copyAllLabel = createElement(\n        \"span\",\n        \"persona-event-copy-all persona-text-xs\"\n      );\n      copyAllLabel.textContent = \"Copy All\";\n      copyAllBtn.appendChild(copyAllLabel);\n\n      if (throughputContainer) headerBar.appendChild(throughputContainer);\n      headerBar.appendChild(headerSpacer);\n      headerBar.appendChild(filterSelect);\n      headerBar.appendChild(copyAllBtn);\n\n      // --- Search bar ---\n      const searchBar = createElement(\n        \"div\",\n        \"persona-relative persona-px-4 persona-py-2.5 persona-border-b persona-border-persona-divider persona-bg-persona-surface\"\n      );\n      applyCustomClasses(searchBar, customClasses?.searchBar);\n\n      // Search icon\n      const searchIcon = renderLucideIcon(\n        \"search\",\n        \"14px\",\n        \"var(--persona-muted, #9ca3af)\",\n        1.5\n      );\n      const searchIconWrapper = createElement(\n        \"span\",\n        \"persona-absolute persona-left-6 persona-top-1/2 persona--translate-y-1/2 persona-pointer-events-none persona-flex persona-items-center\"\n      );\n      if (searchIcon) searchIconWrapper.appendChild(searchIcon);\n\n      searchInput = createElement(\n        \"input\",\n        \"persona-text-sm persona-bg-persona-surface persona-border persona-border-persona-border persona-rounded-md persona-pl-8 persona-pr-3 persona-py-1 persona-w-full persona-text-persona-primary\"\n      ) as HTMLInputElement;\n      applyCustomClasses(searchInput, customClasses?.searchInput);\n      searchInput.type = \"text\";\n      searchInput.placeholder = \"Search event payloads...\";\n\n      searchClearBtn = createElement(\n        \"button\",\n        \"persona-absolute persona-right-5 persona-top-1/2 persona--translate-y-1/2 persona-text-persona-muted hover:persona-text-persona-primary persona-cursor-pointer persona-border-none persona-bg-transparent persona-p-0 persona-leading-none\"\n      ) as HTMLButtonElement;\n      searchClearBtn.type = \"button\";\n      searchClearBtn.style.display = \"none\";\n      const clearSearchIcon = renderLucideIcon(\n        \"x\",\n        \"12px\",\n        \"currentColor\",\n        2\n      );\n      if (clearSearchIcon) searchClearBtn.appendChild(clearSearchIcon);\n\n      searchBar.appendChild(searchIconWrapper);\n      searchBar.appendChild(searchInput);\n      searchBar.appendChild(searchClearBtn);\n\n      toolbarOuter.appendChild(headerBar);\n      toolbarOuter.appendChild(searchBar);\n      if (throughputTooltipEl) toolbarOuter.appendChild(throughputTooltipEl);\n      return toolbarOuter;\n    }\n\n    // Build toolbar (with plugin hook)\n    let toolbar: HTMLElement;\n    const toolbarPlugin = plugins.find((p) => p.renderEventStreamToolbar);\n    if (toolbarPlugin?.renderEventStreamToolbar && config) {\n      const customToolbar = toolbarPlugin.renderEventStreamToolbar({\n        config,\n        defaultRenderer: () => buildDefaultToolbar(),\n        eventCount: buffer.getSize(),\n        filteredCount: 0,\n        onFilterChange: (type: string) => {\n          selectedType = type;\n          resetScrollState();\n          updateNow();\n        },\n        onSearchChange: (term: string) => {\n          searchTerm = term;\n          resetScrollState();\n          updateNow();\n        },\n      });\n      toolbar = customToolbar ?? buildDefaultToolbar();\n    } else {\n      toolbar = buildDefaultToolbar();\n    }\n\n    // ========================================================================\n    // Truncation Banner\n    // ========================================================================\n\n    const truncationBanner = createElement(\n      \"div\",\n      \"persona-text-xs persona-text-persona-muted persona-text-center persona-py-0.5 persona-px-4 persona-bg-persona-container persona-border-b persona-border-persona-divider persona-italic persona-flex-shrink-0\"\n    );\n    truncationBanner.style.display = \"none\";\n\n    // Refresh the inline header throughput value + hover tooltip. The elements\n    // live in the header bar (built by buildDefaultToolbar); this is a no-op\n    // when getThroughput is absent or a plugin replaced the toolbar.\n    function updateThroughputSummary(): void {\n      if (!getThroughput || !throughputValueEl || !throughputContainer) return;\n      const metric = getThroughput();\n      const value = formatThroughputValue(metric);\n      throughputValueEl.textContent = value;\n      // There's no visible \"Throughput\" label, so the accessible name carries it\n      // (plus the value). The detailed breakdown is revealed on hover via the\n      // custom tooltip and appended to the aria-label for assistive tech; when\n      // there's nothing to show, hide the tooltip so an empty box never flashes.\n      const meta = formatThroughputMeta(metric);\n      if (throughputTooltipEl) {\n        throughputTooltipEl.textContent = meta;\n        if (!meta) throughputTooltipEl.style.display = \"none\";\n      }\n      throughputContainer.setAttribute(\n        \"aria-label\",\n        meta ? `Throughput: ${value}, ${meta}` : `Throughput: ${value}`\n      );\n    }\n\n    // ========================================================================\n    // Events List (simple DOM, no virtual scroller)\n    // ========================================================================\n\n    const eventsListWrapper = createElement(\n      \"div\",\n      \"persona-flex-1 persona-min-h-0 persona-relative\"\n    );\n\n    const eventsList = createElement(\n      \"div\",\n      \"persona-event-stream-list persona-overflow-y-auto persona-min-h-0\"\n    );\n    eventsList.style.height = \"100%\";\n\n    // Scroll-to-bottom indicator\n    const scrollIndicator = createElement(\n      \"div\",\n      \"persona-scroll-to-bottom-indicator persona-absolute persona-bottom-3 persona-left-1/2 persona-transform persona--translate-x-1/2 persona-cursor-pointer persona-z-10 persona-text-xs\"\n    );\n    applyCustomClasses(scrollIndicator, customClasses?.scrollIndicator);\n    scrollIndicator.style.display = \"none\";\n    scrollIndicator.setAttribute(\n      \"data-persona-scroll-to-bottom-has-label\",\n      scrollToBottomLabel ? \"true\" : \"false\"\n    );\n    const arrowIcon = renderLucideIcon(\n      scrollToBottomIconName,\n      \"14px\",\n      \"currentColor\",\n      2\n    );\n    if (arrowIcon) scrollIndicator.appendChild(arrowIcon);\n    const indicatorText = createElement(\"span\", \"\");\n    indicatorText.textContent = scrollToBottomLabel;\n    scrollIndicator.appendChild(indicatorText);\n\n    // No matching events message\n    const noResultsMsg = createElement(\n      \"div\",\n      \"persona-flex persona-items-center persona-justify-center persona-h-full persona-text-sm persona-text-persona-muted\"\n    );\n    noResultsMsg.style.display = \"none\";\n\n    eventsListWrapper.appendChild(eventsList);\n    eventsListWrapper.appendChild(noResultsMsg);\n    eventsListWrapper.appendChild(scrollIndicator);\n\n    // ========================================================================\n    // Assemble container\n    // ========================================================================\n\n    container.setAttribute(\"tabindex\", \"0\");\n    container.appendChild(toolbar);\n    container.appendChild(truncationBanner);\n    container.appendChild(eventsListWrapper);\n\n    // ========================================================================\n    // Filtering & Search Logic\n    // ========================================================================\n\n    function updateFilterOptions() {\n      const allEvents = buffer.getAll();\n      const typeCounts: Record<string, number> = {};\n      for (const e of allEvents) {\n        typeCounts[e.type] = (typeCounts[e.type] || 0) + 1;\n      }\n      const types = Object.keys(typeCounts).sort();\n\n      const typesChanged =\n        types.length !== lastKnownTypes.length ||\n        !types.every((t, i) => t === lastKnownTypes[i]);\n      const countsChanged =\n        !typesChanged &&\n        types.some((t) => typeCounts[t] !== lastTypeCounts[t]);\n      const totalChanged =\n        allEvents.length !==\n        Object.values(lastTypeCounts).reduce((a, b) => a + b, 0);\n\n      if (!typesChanged && !countsChanged && !totalChanged) return;\n\n      lastKnownTypes = types;\n      lastTypeCounts = typeCounts;\n\n      if (!filterSelect) return;\n\n      const currentValue = filterSelect.value;\n\n      // Update \"All events\" option. The total is carried here (like each type\n      // option carries its own count) so a narrow panel can hide the standalone\n      // \"Events\" label + count badge without losing the total.\n      filterSelect.options[0].textContent = `All events (${allEvents.length})`;\n\n      if (typesChanged) {\n        while (filterSelect.options.length > 1) {\n          filterSelect.remove(1);\n        }\n        for (const type of types) {\n          const opt = createElement(\"option\", \"\") as HTMLOptionElement;\n          opt.value = type;\n          opt.textContent = `${type} (${typeCounts[type] || 0})`;\n          filterSelect.appendChild(opt);\n        }\n        if (currentValue && types.includes(currentValue)) {\n          filterSelect.value = currentValue;\n        } else if (currentValue) {\n          filterSelect.value = \"\";\n          selectedType = \"\";\n        }\n      } else {\n        for (let i = 1; i < filterSelect.options.length; i++) {\n          const opt = filterSelect.options[i];\n          opt.textContent = `${opt.value} (${typeCounts[opt.value] || 0})`;\n        }\n      }\n    }\n\n    function getFilteredEvents(): SSEEventRecord[] {\n      let events = buffer.getAll();\n      if (selectedType) {\n        events = events.filter((e) => e.type === selectedType);\n      }\n      if (searchTerm) {\n        const lower = searchTerm.toLowerCase();\n        events = events.filter(\n          (e) =>\n            e.type.toLowerCase().includes(lower) ||\n            e.payload.toLowerCase().includes(lower)\n        );\n      }\n      return events;\n    }\n\n    function hasActiveFilters(): boolean {\n      return selectedType !== \"\" || searchTerm !== \"\";\n    }\n\n    function resetScrollState() {\n      lastFilteredCount = 0;\n      newEventsSincePause = 0;\n      autoFollow.resume();\n      scrollIndicator.style.display = \"none\";\n    }\n\n    function toggleExpand(eventId: string) {\n      if (expandedSet.has(eventId)) {\n        expandedSet.delete(eventId);\n      } else {\n        expandedSet.add(eventId);\n      }\n      dirtyExpandId = eventId;\n      // Save scroll position: user-initiated expand/collapse should not auto-scroll\n      const savedScrollTop = eventsList.scrollTop;\n      const wasAutoFollowing = autoFollow.isFollowing();\n      suppressScrollHandler = true;\n      autoFollow.pause(); // prevent auto-scroll during re-render\n      updateNow();\n      eventsList.scrollTop = savedScrollTop;\n      if (wasAutoFollowing) {\n        autoFollow.resume();\n      }\n      suppressScrollHandler = false;\n    }\n\n    // ========================================================================\n    // Render Logic\n    // ========================================================================\n\n    function isNearBottom(): boolean {\n      return isElementNearBottom(eventsList, 50);\n    }\n\n    function updateNow() {\n      lastRenderTime = Date.now();\n      pendingUpdate = false;\n\n      updateThroughputSummary();\n      updateFilterOptions();\n\n      // Truncation banner\n      const evictedCount = buffer.getEvictedCount();\n      if (evictedCount > 0) {\n        truncationBanner.textContent = `${evictedCount.toLocaleString()} older events truncated`;\n        truncationBanner.style.display = \"\";\n      } else {\n        truncationBanner.style.display = \"none\";\n      }\n\n      filteredEvents = getFilteredEvents();\n      const newCount = filteredEvents.length;\n      const bufferHasEvents = buffer.getSize() > 0;\n\n      // Show/hide no-results message\n      if (newCount === 0 && bufferHasEvents && hasActiveFilters()) {\n        noResultsMsg.textContent = searchTerm\n          ? `No events matching '${searchTerm}'`\n          : \"No events matching filter\";\n        noResultsMsg.style.display = \"\";\n        eventsList.style.display = \"none\";\n      } else {\n        noResultsMsg.style.display = \"none\";\n        eventsList.style.display = \"\";\n      }\n\n      // Update Copy All button title\n      if (copyAllBtn) {\n        copyAllBtn.title = hasActiveFilters()\n          ? `Copy Filtered (${newCount})`\n          : \"Copy All\";\n      }\n\n      // Track new events since user scrolled up\n      if (scrollToBottomEnabled && !autoFollow.isFollowing() && newCount > lastFilteredCount) {\n        newEventsSincePause += newCount - lastFilteredCount;\n        indicatorText.textContent = scrollToBottomLabel\n          ? `${scrollToBottomLabel}${newEventsSincePause > 0 ? ` (${newEventsSincePause})` : \"\"}`\n          : \"\";\n        scrollIndicator.style.display = \"\";\n      }\n      lastFilteredCount = newCount;\n\n      // Get first event timestamp for relative time calculations\n      // Use the unfiltered first event for consistent time references\n      const allEvents = buffer.getAll();\n      const firstTimestamp =\n        allEvents.length > 0 ? allEvents[0].timestamp : 0;\n\n      // Clean up expanded state for evicted events\n      const currentIds = new Set(filteredEvents.map((e) => e.id));\n      for (const id of expandedSet) {\n        if (!currentIds.has(id)) expandedSet.delete(id);\n      }\n\n      // Determine which rendering path to use\n      const filterChanged =\n        selectedType !== lastRenderedFilter ||\n        searchTerm !== lastRenderedSearch;\n      const isFirstRender = rowElements.size === 0 && filteredEvents.length > 0;\n\n      if (filterChanged || isFirstRender || filteredEvents.length === 0) {\n        // Path A: Full rebuild (filter/search changed, first render, or empty list)\n        eventsList.innerHTML = \"\";\n        rowElements.clear();\n        const fragment = document.createDocumentFragment();\n        for (let i = 0; i < filteredEvents.length; i++) {\n          const row = renderEventRow(\n            filteredEvents[i],\n            i,\n            firstTimestamp,\n            esConfig,\n            expandedSet,\n            toggleExpand,\n            plugins,\n            config\n          );\n          rowElements.set(filteredEvents[i].id, row);\n          fragment.appendChild(row);\n        }\n        eventsList.appendChild(fragment);\n        lastRenderedFilter = selectedType;\n        lastRenderedSearch = searchTerm;\n        dirtyExpandId = null;\n      } else {\n        // Path B: Single row replace (expand/collapse)\n        if (dirtyExpandId !== null) {\n          const oldRow = rowElements.get(dirtyExpandId);\n          if (oldRow && oldRow.parentNode === eventsList) {\n            // Find the index of this event in filteredEvents for correct seq number\n            const idx = filteredEvents.findIndex(\n              (e) => e.id === dirtyExpandId\n            );\n            if (idx >= 0) {\n              const newRow = renderEventRow(\n                filteredEvents[idx],\n                idx,\n                firstTimestamp,\n                esConfig,\n                expandedSet,\n                toggleExpand,\n                plugins,\n                config\n              );\n              eventsList.insertBefore(newRow, oldRow);\n              oldRow.remove();\n              rowElements.set(dirtyExpandId, newRow);\n            }\n          }\n          dirtyExpandId = null;\n        }\n\n        // Path C: Incremental append (default streaming path)\n        // Remove evicted rows\n        const activeIds = new Set(filteredEvents.map((e) => e.id));\n        for (const [id, el] of rowElements) {\n          if (!activeIds.has(id)) {\n            el.remove();\n            rowElements.delete(id);\n          }\n        }\n        // Append new rows (events not yet in rowElements)\n        for (let i = 0; i < filteredEvents.length; i++) {\n          const evt = filteredEvents[i];\n          if (!rowElements.has(evt.id)) {\n            const row = renderEventRow(\n              evt,\n              i,\n              firstTimestamp,\n              esConfig,\n              expandedSet,\n              toggleExpand,\n              plugins,\n              config\n            );\n            rowElements.set(evt.id, row);\n            eventsList.appendChild(row);\n          }\n        }\n      }\n\n      // Auto-scroll if user hasn't scrolled up\n      if (autoFollow.isFollowing()) {\n        eventsList.scrollTop = eventsList.scrollHeight;\n      }\n    }\n\n    function update() {\n      const now = Date.now();\n      const elapsed = now - lastRenderTime;\n\n      if (elapsed >= UPDATE_THROTTLE_MS) {\n        if (pendingRafId !== null) {\n          cancelAnimationFrame(pendingRafId);\n          pendingRafId = null;\n        }\n        updateNow();\n        return;\n      }\n\n      if (!pendingUpdate) {\n        pendingUpdate = true;\n        pendingRafId = requestAnimationFrame(() => {\n          pendingRafId = null;\n          updateNow();\n        });\n      }\n    }\n\n    // ========================================================================\n    // Event Handlers\n    // ========================================================================\n\n    const swapCopyAllIcon = (\n      iconName: string,\n      restoreAfterMs: number\n    ) => {\n      if (!copyAllBtn) return;\n      copyAllBtn.innerHTML = \"\";\n      const icon = renderLucideIcon(\n        iconName,\n        \"12px\",\n        \"currentColor\",\n        1.5\n      );\n      if (icon) copyAllBtn.appendChild(icon);\n      const label = createElement(\"span\", \"persona-text-xs\");\n      label.textContent = \"Copy All\";\n      copyAllBtn.appendChild(label);\n      setTimeout(() => {\n        copyAllBtn.innerHTML = \"\";\n        const original = renderLucideIcon(\n          \"clipboard-copy\",\n          \"12px\",\n          \"currentColor\",\n          1.5\n        );\n        if (original) copyAllBtn.appendChild(original);\n        const restoreLabel = createElement(\"span\", \"persona-text-xs\");\n        restoreLabel.textContent = \"Copy All\";\n        copyAllBtn.appendChild(restoreLabel);\n        copyAllBtn.disabled = false;\n      }, restoreAfterMs);\n    };\n\n    const handleCopyAll = async () => {\n      if (!copyAllBtn) return;\n      copyAllBtn.disabled = true;\n      try {\n        let events: SSEEventRecord[];\n        if (hasActiveFilters()) {\n          events = filteredEvents;\n        } else {\n          if (getFullHistory) {\n            events = await getFullHistory();\n            if (events.length === 0) events = buffer.getAll();\n          } else {\n            events = buffer.getAll();\n          }\n        }\n        const parsed = events.map((e) => {\n          try {\n            return JSON.parse(e.payload);\n          } catch {\n            return e.payload;\n          }\n        });\n        await navigator.clipboard.writeText(\n          JSON.stringify(parsed, null, 2)\n        );\n        swapCopyAllIcon(\"check\", 1500);\n      } catch {\n        swapCopyAllIcon(\"x\", 1500);\n      }\n    };\n\n    const handleFilterChange = () => {\n      if (!filterSelect) return;\n      selectedType = filterSelect.value;\n      resetScrollState();\n      updateNow();\n    };\n\n    const handleSearchInput = () => {\n      if (!searchInput || !searchClearBtn) return;\n      searchClearBtn.style.display = searchInput.value ? \"\" : \"none\";\n      if (searchTimeout) clearTimeout(searchTimeout);\n      searchTimeout = setTimeout(() => {\n        searchTerm = searchInput.value;\n        resetScrollState();\n        updateNow();\n      }, 150);\n    };\n\n    const handleSearchClear = () => {\n      if (!searchInput || !searchClearBtn) return;\n      searchInput.value = \"\";\n      searchTerm = \"\";\n      searchClearBtn.style.display = \"none\";\n      if (searchTimeout) clearTimeout(searchTimeout);\n      resetScrollState();\n      updateNow();\n    };\n\n    const handleListScroll = () => {\n      if (suppressScrollHandler) return;\n      const currentScrollTop = eventsList.scrollTop;\n      const { action, nextLastScrollTop } = resolveFollowStateFromScroll({\n        following: autoFollow.isFollowing(),\n        currentScrollTop,\n        lastScrollTop,\n        nearBottom: isNearBottom(),\n        userScrollThreshold: 1,\n        resumeRequiresDownwardScroll: true\n      });\n      lastScrollTop = nextLastScrollTop;\n\n      if (action === \"resume\") {\n        autoFollow.resume();\n        newEventsSincePause = 0;\n        scrollIndicator.style.display = \"none\";\n      } else if (action === \"pause\") {\n        autoFollow.pause();\n        if (scrollToBottomEnabled) {\n          indicatorText.textContent = scrollToBottomLabel;\n          scrollIndicator.style.display = \"\";\n        }\n      }\n    };\n\n    // Wheel events fire synchronously before rAF callbacks, so we can\n    // detect upward scroll intent before the next updateNow() auto-scrolls.\n    const handleWheel = (e: WheelEvent) => {\n      const action = resolveFollowStateFromWheel({\n        following: autoFollow.isFollowing(),\n        deltaY: e.deltaY,\n        nearBottom: isNearBottom(),\n        resumeWhenNearBottom: true\n      });\n\n      if (action === \"pause\") {\n        autoFollow.pause();\n        if (scrollToBottomEnabled) {\n          indicatorText.textContent = scrollToBottomLabel;\n          scrollIndicator.style.display = \"\";\n        }\n      } else if (action === \"resume\") {\n        autoFollow.resume();\n        newEventsSincePause = 0;\n        scrollIndicator.style.display = \"none\";\n      }\n    };\n\n    const handleScrollIndicatorClick = () => {\n      if (!scrollToBottomEnabled) return;\n      eventsList.scrollTop = eventsList.scrollHeight;\n      autoFollow.resume();\n      newEventsSincePause = 0;\n      scrollIndicator.style.display = \"none\";\n    };\n\n    // Delegated click handler for expand/collapse (survives DOM rebuilds)\n    const handleRowClick = (e: Event) => {\n      const target = e.target as Element;\n      if (!target) return;\n      // Skip if clicking copy button or its children\n      if (target.closest(\"button\")) return;\n      // Find the closest row with an event ID\n      const row = target.closest(\"[data-event-id]\") as HTMLElement | null;\n      if (!row) return;\n      const eventId = row.getAttribute(\"data-event-id\");\n      if (eventId) toggleExpand(eventId);\n    };\n\n    // Keyboard shortcuts\n    const handleKeyDown = (e: KeyboardEvent) => {\n      const isMod = e.metaKey || e.ctrlKey;\n\n      if (isMod && e.key === \"f\") {\n        e.preventDefault();\n        searchInput?.focus();\n        searchInput?.select();\n        return;\n      }\n\n      if (e.key === \"Escape\") {\n        if (\n          searchInput &&\n          document.activeElement === searchInput\n        ) {\n          handleSearchClear();\n          searchInput.blur();\n          container.focus();\n        } else if (onClose) {\n          onClose();\n        }\n      }\n    };\n\n    // ========================================================================\n    // Wire up event listeners\n    // ========================================================================\n\n    if (copyAllBtn) copyAllBtn.addEventListener(\"click\", handleCopyAll);\n    if (filterSelect)\n      filterSelect.addEventListener(\"change\", handleFilterChange);\n    if (searchInput)\n      searchInput.addEventListener(\"input\", handleSearchInput);\n    if (searchClearBtn)\n      searchClearBtn.addEventListener(\"click\", handleSearchClear);\n    eventsList.addEventListener(\"scroll\", handleListScroll);\n    eventsList.addEventListener(\"wheel\", handleWheel, { passive: true });\n    eventsList.addEventListener(\"click\", handleRowClick);\n    scrollIndicator.addEventListener(\"click\", handleScrollIndicatorClick);\n    container.addEventListener(\"keydown\", handleKeyDown);\n\n    // ========================================================================\n    // Destroy / Cleanup\n    // ========================================================================\n\n    function destroy() {\n      if (searchTimeout) clearTimeout(searchTimeout);\n      if (pendingRafId !== null) {\n        cancelAnimationFrame(pendingRafId);\n        pendingRafId = null;\n      }\n      pendingUpdate = false;\n      rowElements.clear();\n      if (copyAllBtn)\n        copyAllBtn.removeEventListener(\"click\", handleCopyAll);\n      if (filterSelect)\n        filterSelect.removeEventListener(\"change\", handleFilterChange);\n      if (searchInput)\n        searchInput.removeEventListener(\"input\", handleSearchInput);\n      if (searchClearBtn)\n        searchClearBtn.removeEventListener(\"click\", handleSearchClear);\n      eventsList.removeEventListener(\"scroll\", handleListScroll);\n      eventsList.removeEventListener(\"wheel\", handleWheel);\n      eventsList.removeEventListener(\"click\", handleRowClick);\n      scrollIndicator.removeEventListener(\n        \"click\",\n        handleScrollIndicatorClick\n      );\n      container.removeEventListener(\"keydown\", handleKeyDown);\n    }\n\n    return { element: container, update, destroy };\n  }\n}\n","import type { ComponentContext, ComponentRenderer } from \"./registry\";\n\n/**\n * Default artifact card renderer.\n * Builds the compact clickable card shown in the chat thread.\n */\nfunction renderDefaultArtifactCard(\n  props: Record<string, unknown>,\n  _context: ComponentContext\n): HTMLElement {\n  const title =\n    typeof props.title === \"string\" && props.title\n      ? props.title\n      : \"Untitled artifact\";\n  const artifactId =\n    typeof props.artifactId === \"string\" ? props.artifactId : \"\";\n  const status = props.status === \"streaming\" ? \"streaming\" : \"complete\";\n  const artifactType =\n    typeof props.artifactType === \"string\" ? props.artifactType : \"markdown\";\n  const subtitle =\n    artifactType === \"component\" ? \"Component\" : \"Document\";\n\n  const root = document.createElement(\"div\");\n  root.className =\n    \"persona-flex persona-w-full persona-max-w-full persona-items-center persona-gap-3 persona-rounded-xl persona-px-4 persona-py-3\";\n  root.style.border = \"1px solid var(--persona-border, #e5e7eb)\";\n  root.style.backgroundColor = \"var(--persona-surface, #ffffff)\";\n  root.style.cursor = \"pointer\";\n  root.tabIndex = 0;\n  root.setAttribute(\"role\", \"button\");\n  root.setAttribute(\"aria-label\", `Open ${title} in artifact panel`);\n  if (artifactId) {\n    root.setAttribute(\"data-open-artifact\", artifactId);\n  }\n\n  // Document icon\n  const iconBox = document.createElement(\"div\");\n  iconBox.className =\n    \"persona-flex persona-h-10 persona-w-10 persona-flex-shrink-0 persona-items-center persona-justify-center persona-rounded-lg\";\n  iconBox.style.border = \"1px solid var(--persona-border, #e5e7eb)\";\n  iconBox.style.color = \"var(--persona-muted, #9ca3af)\";\n  iconBox.innerHTML = `<svg xmlns=\"http://www.w3.org/2000/svg\" width=\"20\" height=\"20\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"1.75\" stroke-linecap=\"round\" stroke-linejoin=\"round\" aria-hidden=\"true\"><path d=\"M14.5 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V7.5L14.5 2z\"/><polyline points=\"14 2 14 8 20 8\"/></svg>`;\n\n  // Title and subtitle\n  const meta = document.createElement(\"div\");\n  meta.className =\n    \"persona-min-w-0 persona-flex-1 persona-flex persona-flex-col persona-gap-0.5\";\n\n  const titleEl = document.createElement(\"div\");\n  titleEl.className = \"persona-truncate persona-text-sm persona-font-medium\";\n  titleEl.style.color = \"var(--persona-text, #1f2937)\";\n  titleEl.textContent = title;\n\n  const subtitleEl = document.createElement(\"div\");\n  subtitleEl.className = \"persona-text-xs persona-flex persona-items-center persona-gap-1.5\";\n  subtitleEl.style.color = \"var(--persona-muted, #9ca3af)\";\n\n  if (status === \"streaming\") {\n    // Pulsing dot for streaming status\n    const dot = document.createElement(\"span\");\n    dot.className = \"persona-inline-block persona-w-1.5 persona-h-1.5 persona-rounded-full\";\n    dot.style.backgroundColor = \"var(--persona-primary, #171717)\";\n    dot.style.animation = \"persona-pulse 1.5s ease-in-out infinite\";\n    subtitleEl.appendChild(dot);\n\n    const statusText = document.createElement(\"span\");\n    statusText.textContent = `Generating ${subtitle.toLowerCase()}...`;\n    subtitleEl.appendChild(statusText);\n  } else {\n    subtitleEl.textContent = subtitle;\n  }\n\n  meta.append(titleEl, subtitleEl);\n  root.append(iconBox, meta);\n\n  // Download button (visible when artifact is complete)\n  if (status === \"complete\") {\n    const dl = document.createElement(\"button\");\n    dl.type = \"button\";\n    dl.textContent = \"Download\";\n    dl.title = `Download ${title}`;\n    dl.className =\n      \"persona-flex-shrink-0 persona-rounded-md persona-px-3 persona-py-1.5 persona-text-xs persona-font-medium\";\n    dl.style.border = \"1px solid var(--persona-border, #e5e7eb)\";\n    dl.style.color = \"var(--persona-text, #1f2937)\";\n    dl.style.backgroundColor = \"transparent\";\n    dl.style.cursor = \"pointer\";\n    dl.setAttribute(\"data-download-artifact\", artifactId);\n    root.append(dl);\n  }\n\n  return root;\n}\n\n/**\n * Built-in artifact reference card component.\n * Renders a compact clickable card in the chat thread that links to an artifact.\n * Uses `data-open-artifact` attribute for click delegation (handled in ui.ts).\n *\n * Supports a custom `renderCard` callback via `config.features.artifacts.renderCard`\n * that can override the default card rendering.\n */\nexport const PersonaArtifactCard: ComponentRenderer = (props, context) => {\n  const customRenderer = context?.config?.features?.artifacts?.renderCard;\n  if (customRenderer) {\n    const title =\n      typeof props.title === \"string\" && props.title\n        ? props.title\n        : \"Untitled artifact\";\n    const artifactId =\n      typeof props.artifactId === \"string\" ? props.artifactId : \"\";\n    const status = props.status === \"streaming\" ? \"streaming\" : \"complete\";\n    const artifactType =\n      typeof props.artifactType === \"string\" ? props.artifactType : \"markdown\";\n\n    const result = customRenderer({\n      artifact: { artifactId, title, artifactType, status },\n      config: context.config,\n      defaultRenderer: () => renderDefaultArtifactCard(props, context),\n    });\n    if (result) return result;\n  }\n\n  return renderDefaultArtifactCard(props, context);\n};\n","import { AgentWidgetConfig, AgentWidgetMessage } from \"../types\";\nimport { PersonaArtifactCard } from \"./artifact-card\";\n\n/**\n * Context provided to component renderers\n */\nexport interface ComponentContext {\n  message: AgentWidgetMessage;\n  config: AgentWidgetConfig;\n  /**\n   * Update component props during streaming\n   */\n  updateProps: (newProps: Record<string, unknown>) => void;\n}\n\n/**\n * Component renderer function signature\n */\nexport type ComponentRenderer = (\n  props: Record<string, unknown>,\n  context: ComponentContext\n) => HTMLElement;\n\n/**\n * Component registry for managing custom components\n */\nclass ComponentRegistry {\n  private components: Map<string, ComponentRenderer> = new Map();\n\n  /**\n   * Register a custom component\n   */\n  register(name: string, renderer: ComponentRenderer): void {\n    if (this.components.has(name)) {\n      console.warn(`[ComponentRegistry] Component \"${name}\" is already registered. Overwriting.`);\n    }\n    this.components.set(name, renderer);\n  }\n\n  /**\n   * Unregister a component\n   */\n  unregister(name: string): void {\n    this.components.delete(name);\n  }\n\n  /**\n   * Get a component renderer by name\n   */\n  get(name: string): ComponentRenderer | undefined {\n    return this.components.get(name);\n  }\n\n  /**\n   * Check if a component is registered\n   */\n  has(name: string): boolean {\n    return this.components.has(name);\n  }\n\n  /**\n   * Get all registered component names\n   */\n  getAllNames(): string[] {\n    return Array.from(this.components.keys());\n  }\n\n  /**\n   * Clear all registered components\n   */\n  clear(): void {\n    this.components.clear();\n  }\n\n  /**\n   * Register multiple components at once\n   */\n  registerAll(components: Record<string, ComponentRenderer>): void {\n    Object.entries(components).forEach(([name, renderer]) => {\n      this.register(name, renderer);\n    });\n  }\n}\n\n/**\n * Global component registry instance\n */\nexport const componentRegistry = new ComponentRegistry();\n\n// Register built-in components\ncomponentRegistry.register(\"PersonaArtifactCard\", PersonaArtifactCard);\n","import { createElement } from \"../utils/dom\";\nimport type { AgentWidgetConfig, AgentWidgetMessage, PersonaArtifactRecord } from \"../types\";\nimport { escapeHtml, createMarkdownProcessorFromConfig } from \"../postprocessors\";\nimport { resolveSanitizer } from \"../utils/sanitize\";\nimport { componentRegistry, type ComponentContext } from \"./registry\";\nimport { renderLucideIcon } from \"../utils/icons\";\nimport { createDropdownMenu, type DropdownMenuHandle } from \"../utils/dropdown\";\nimport { createIconButton, createLabelButton } from \"../utils/buttons\";\n\nexport type ArtifactPaneApi = {\n  element: HTMLElement;\n  /** Backdrop for mobile drawer (optional separate layer) */\n  backdrop: HTMLElement | null;\n  update: (state: { artifacts: PersonaArtifactRecord[]; selectedId: string | null }) => void;\n  setMobileOpen: (open: boolean) => void;\n};\n\nfunction fallbackComponentCard(sel: PersonaArtifactRecord): HTMLElement {\n  const card = createElement(\n    \"div\",\n    \"persona-rounded-lg persona-border persona-border-persona-border persona-p-3 persona-text-persona-primary\"\n  );\n  const title = createElement(\"div\", \"persona-font-semibold persona-text-sm persona-mb-2\");\n  title.textContent = sel.component ? `Component: ${sel.component}` : \"Component\";\n  const pre = createElement(\"pre\", \"persona-font-mono persona-text-xs persona-whitespace-pre-wrap persona-overflow-x-auto\");\n  pre.textContent = JSON.stringify(sel.props ?? {}, null, 2);\n  card.appendChild(title);\n  card.appendChild(pre);\n  return card;\n}\n\n/**\n * Right-hand artifact sidebar / mobile drawer content.\n */\nexport function createArtifactPane(\n  config: AgentWidgetConfig,\n  options: {\n    onSelect: (id: string) => void;\n    /** User closed the pane (mobile drawer or split sidebar): parent should persist “hidden until reopened”. */\n    onDismiss?: () => void;\n  }\n): ArtifactPaneApi {\n  const layout = config.features?.artifacts?.layout;\n  const toolbarPreset = layout?.toolbarPreset ?? \"default\";\n  const documentChrome = toolbarPreset === \"document\";\n  const panePadding = layout?.panePadding?.trim();\n\n  const md = config.markdown ? createMarkdownProcessorFromConfig(config.markdown) : null;\n  const sanitize = resolveSanitizer(config.sanitize);\n  const toHtml = (text: string) => {\n    const raw = md ? md(text) : escapeHtml(text);\n    return sanitize ? sanitize(raw) : raw;\n  };\n\n  const backdrop =\n    typeof document !== \"undefined\"\n      ? createElement(\n          \"div\",\n          \"persona-artifact-backdrop persona-fixed persona-inset-0 persona-z-[55] persona-bg-black/30 persona-hidden md:persona-hidden\"\n        )\n      : null;\n  const dismissLocalUi = () => {\n    backdrop?.classList.add(\"persona-hidden\");\n    shell.classList.remove(\"persona-artifact-drawer-open\");\n    // Hide portaled copy menu\n    copyMenuDropdown?.hide();\n  };\n\n  if (backdrop) {\n    backdrop.addEventListener(\"click\", () => {\n      dismissLocalUi();\n      options.onDismiss?.();\n    });\n  }\n\n  const shell = createElement(\n    \"aside\",\n    \"persona-artifact-pane persona-flex persona-flex-col persona-min-h-0 persona-min-w-0 persona-bg-persona-surface persona-text-persona-primary persona-border-l persona-border-persona-border\"\n  );\n  shell.setAttribute(\"data-persona-theme-zone\", \"artifact-pane\");\n  if (documentChrome) {\n    shell.classList.add(\"persona-artifact-pane-document\");\n  }\n\n  const toolbar = createElement(\n    \"div\",\n    \"persona-artifact-toolbar persona-flex persona-items-center persona-justify-between persona-gap-2 persona-px-2 persona-py-2 persona-border-b persona-border-persona-border persona-shrink-0\"\n  );\n  toolbar.setAttribute(\"data-persona-theme-zone\", \"artifact-toolbar\");\n  if (documentChrome) {\n    toolbar.classList.add(\"persona-artifact-toolbar-document\");\n  }\n  const titleEl = createElement(\"span\", \"persona-text-xs persona-font-medium persona-truncate\");\n  titleEl.textContent = \"Artifacts\";\n\n  const closeBtn = createElement(\n    \"button\",\n    \"persona-rounded-md persona-border persona-border-persona-border persona-px-2 persona-py-1 persona-text-xs persona-bg-persona-surface\"\n  );\n  closeBtn.type = \"button\";\n  closeBtn.textContent = \"Close\";\n  closeBtn.setAttribute(\"aria-label\", \"Close artifacts panel\");\n  closeBtn.addEventListener(\"click\", () => {\n    dismissLocalUi();\n    options.onDismiss?.();\n  });\n\n  /** Document preset: view vs raw source */\n  let viewMode: \"rendered\" | \"source\" = \"rendered\";\n  const leftTools = createElement(\"div\", \"persona-flex persona-items-center persona-gap-1 persona-shrink-0 persona-artifact-toggle-group\");\n  const viewBtn = documentChrome\n    ? createIconButton({ icon: \"eye\", label: \"Rendered view\", className: \"persona-artifact-doc-icon-btn persona-artifact-view-btn\" })\n    : createIconButton({ icon: \"eye\", label: \"Rendered view\" });\n  const codeBtn = documentChrome\n    ? createIconButton({ icon: \"code-2\", label: \"Source\", className: \"persona-artifact-doc-icon-btn persona-artifact-code-btn\" })\n    : createIconButton({ icon: \"code-2\", label: \"Source\" });\n  const actionsRight = createElement(\"div\", \"persona-flex persona-items-center persona-gap-1 persona-shrink-0\");\n  const showCopyLabel = layout?.documentToolbarShowCopyLabel === true;\n  const showCopyChevron = layout?.documentToolbarShowCopyChevron === true;\n  const copyMenuItems = layout?.documentToolbarCopyMenuItems;\n  const showCopyMenu = Boolean(showCopyChevron && copyMenuItems && copyMenuItems.length > 0);\n\n  let copyWrap: HTMLElement | null = null;\n  let copyBtn: HTMLButtonElement;\n  let copyMenuChevronBtn: HTMLButtonElement | null = null;\n  let copyMenuDropdown: DropdownMenuHandle | null = null;\n\n  if (documentChrome && (showCopyLabel || showCopyChevron) && !showCopyMenu) {\n    copyBtn = showCopyLabel\n      ? createLabelButton({ icon: \"copy\", label: \"Copy\", iconSize: 14, className: \"persona-artifact-doc-copy-btn\" })\n      : createIconButton({ icon: \"copy\", label: \"Copy\", className: \"persona-artifact-doc-copy-btn\" });\n    if (showCopyChevron) {\n      const chev = renderLucideIcon(\"chevron-down\", 14, \"currentColor\", 2);\n      if (chev) copyBtn.appendChild(chev);\n    }\n  } else if (documentChrome && showCopyMenu) {\n    copyWrap = createElement(\n      \"div\",\n      \"persona-relative persona-inline-flex persona-items-center persona-gap-0 persona-rounded-md\"\n    );\n    copyBtn = showCopyLabel\n      ? createLabelButton({ icon: \"copy\", label: \"Copy\", iconSize: 14, className: \"persona-artifact-doc-copy-btn\" })\n      : createIconButton({ icon: \"copy\", label: \"Copy\", className: \"persona-artifact-doc-copy-btn\" });\n    copyMenuChevronBtn = createIconButton({\n      icon: \"chevron-down\",\n      label: \"More copy options\",\n      size: 14,\n      className: \"persona-artifact-doc-copy-menu-chevron persona-artifact-doc-icon-btn\",\n      aria: { \"aria-haspopup\": \"true\", \"aria-expanded\": \"false\" }\n    });\n    copyWrap.append(copyBtn, copyMenuChevronBtn);\n  } else if (documentChrome) {\n    copyBtn = createIconButton({ icon: \"copy\", label: \"Copy\", className: \"persona-artifact-doc-icon-btn\" });\n  } else {\n    copyBtn = createIconButton({ icon: \"copy\", label: \"Copy\" });\n  }\n\n  const refreshBtn = documentChrome\n    ? createIconButton({ icon: \"refresh-cw\", label: \"Refresh\", className: \"persona-artifact-doc-icon-btn\" })\n    : createIconButton({ icon: \"refresh-cw\", label: \"Refresh\" });\n  const closeIconBtn = documentChrome\n    ? createIconButton({ icon: \"x\", label: \"Close\", className: \"persona-artifact-doc-icon-btn\" })\n    : createIconButton({ icon: \"x\", label: \"Close\" });\n\n  const getSelectedArtifactText = (): { markdown: string; jsonPayload: string; id: string | null } => {\n    const sel = records.find((r) => r.id === selectedId) ?? records[records.length - 1];\n    const id = sel?.id ?? null;\n    const markdown = sel?.artifactType === \"markdown\" ? sel.markdown ?? \"\" : \"\";\n    const jsonPayload = sel\n      ? JSON.stringify({ component: sel.component, props: sel.props }, null, 2)\n      : \"\";\n    return { markdown, jsonPayload, id };\n  };\n\n  const defaultCopy = async () => {\n    const { markdown, jsonPayload } = getSelectedArtifactText();\n    const sel = records.find((r) => r.id === selectedId) ?? records[records.length - 1];\n    const text =\n      sel?.artifactType === \"markdown\"\n        ? markdown\n        : sel\n          ? jsonPayload\n          : \"\";\n    try {\n      await navigator.clipboard.writeText(text);\n    } catch {\n      /* ignore */\n    }\n  };\n\n  copyBtn.addEventListener(\"click\", async () => {\n    const handler = layout?.onDocumentToolbarCopyMenuSelect;\n    if (handler && showCopyMenu) {\n      const { markdown, jsonPayload, id } = getSelectedArtifactText();\n      try {\n        await handler({ actionId: \"primary\", artifactId: id, markdown, jsonPayload });\n      } catch {\n        /* ignore */\n      }\n      return;\n    }\n    await defaultCopy();\n  });\n\n  if (copyMenuChevronBtn && copyMenuItems?.length) {\n    // Resolve the portal target: widget root for CSS var inheritance, escaping overflow: hidden\n    const resolvePortal = (): HTMLElement => shell.closest(\"[data-persona-root]\") as HTMLElement ?? document.body;\n\n    const initDropdown = () => {\n      copyMenuDropdown = createDropdownMenu({\n        items: copyMenuItems.map((item) => ({ id: item.id, label: item.label })),\n        onSelect: async (actionId) => {\n          const { markdown, jsonPayload, id } = getSelectedArtifactText();\n          const handler = layout?.onDocumentToolbarCopyMenuSelect;\n          try {\n            if (handler) {\n              await handler({ actionId, artifactId: id, markdown, jsonPayload });\n            } else if (actionId === \"markdown\" || actionId === \"md\") {\n              await navigator.clipboard.writeText(markdown);\n            } else if (actionId === \"json\" || actionId === \"source\") {\n              await navigator.clipboard.writeText(jsonPayload);\n            } else {\n              await navigator.clipboard.writeText(markdown || jsonPayload);\n            }\n          } catch {\n            /* ignore */\n          }\n        },\n        anchor: copyWrap ?? copyMenuChevronBtn!,\n        position: 'bottom-right',\n        portal: resolvePortal(),\n      });\n    };\n\n    // Defer init until shell is in the DOM (may not be attached yet)\n    if (shell.isConnected) {\n      initDropdown();\n    } else {\n      requestAnimationFrame(initDropdown);\n    }\n\n    copyMenuChevronBtn.addEventListener(\"click\", (e) => {\n      e.stopPropagation();\n      copyMenuDropdown?.toggle();\n    });\n  }\n\n  refreshBtn.addEventListener(\"click\", async () => {\n    try {\n      await layout?.onDocumentToolbarRefresh?.();\n    } catch {\n      /* ignore */\n    }\n    render();\n  });\n  closeIconBtn.addEventListener(\"click\", () => {\n    dismissLocalUi();\n    options.onDismiss?.();\n  });\n\n  const syncViewToggleState = () => {\n    if (!documentChrome) return;\n    viewBtn.setAttribute(\"aria-pressed\", viewMode === \"rendered\" ? \"true\" : \"false\");\n    codeBtn.setAttribute(\"aria-pressed\", viewMode === \"source\" ? \"true\" : \"false\");\n  };\n  viewBtn.addEventListener(\"click\", () => {\n    viewMode = \"rendered\";\n    syncViewToggleState();\n    render();\n  });\n  codeBtn.addEventListener(\"click\", () => {\n    viewMode = \"source\";\n    syncViewToggleState();\n    render();\n  });\n\n  const centerTitle = createElement(\n    \"span\",\n    \"persona-min-w-0 persona-flex-1 persona-text-xs persona-font-medium persona-text-persona-primary persona-truncate persona-text-center md:persona-text-left\"\n  );\n\n  if (documentChrome) {\n    toolbar.replaceChildren();\n    leftTools.append(viewBtn, codeBtn);\n    if (copyWrap) {\n      actionsRight.append(copyWrap, refreshBtn, closeIconBtn);\n    } else {\n      actionsRight.append(copyBtn, refreshBtn, closeIconBtn);\n    }\n    toolbar.append(leftTools, centerTitle, actionsRight);\n    syncViewToggleState();\n  } else {\n    toolbar.appendChild(titleEl);\n    toolbar.appendChild(closeBtn);\n  }\n\n  if (panePadding) {\n    toolbar.style.paddingLeft = panePadding;\n    toolbar.style.paddingRight = panePadding;\n  }\n\n  const list = createElement(\n    \"div\",\n    \"persona-artifact-list persona-shrink-0 persona-flex persona-gap-1 persona-overflow-x-auto persona-p-2 persona-border-b persona-border-persona-border\"\n  );\n  const content = createElement(\n    \"div\",\n    \"persona-artifact-content persona-flex-1 persona-min-h-0 persona-overflow-y-auto persona-p-3\"\n  );\n  if (panePadding) {\n    list.style.paddingLeft = panePadding;\n    list.style.paddingRight = panePadding;\n    content.style.padding = panePadding;\n  }\n\n  shell.appendChild(toolbar);\n  shell.appendChild(list);\n  shell.appendChild(content);\n\n  let records: PersonaArtifactRecord[] = [];\n  let selectedId: string | null = null;\n  let mobileOpen = false;\n\n  const render = () => {\n    const hideTabs = documentChrome && records.length <= 1;\n    list.classList.toggle(\"persona-hidden\", hideTabs);\n\n    list.replaceChildren();\n    for (const r of records) {\n      const tab = createElement(\n        \"button\",\n        \"persona-artifact-tab persona-shrink-0 persona-rounded-lg persona-px-2 persona-py-1 persona-text-xs persona-border persona-border-transparent persona-text-persona-primary\"\n      );\n      tab.type = \"button\";\n      tab.textContent = r.title || r.id.slice(0, 8);\n      if (r.id === selectedId) {\n        tab.classList.add(\"persona-bg-persona-container\", \"persona-border-persona-border\");\n      }\n      tab.addEventListener(\"click\", () => options.onSelect(r.id));\n      list.appendChild(tab);\n    }\n\n    content.replaceChildren();\n    const sel =\n      (selectedId && records.find((x) => x.id === selectedId)) ||\n      records[records.length - 1];\n    if (!sel) return;\n\n    if (documentChrome) {\n      const kind = sel.artifactType === \"markdown\" ? \"MD\" : sel.component ?? \"Component\";\n      const rawTitle = (sel.title || \"Document\").trim();\n      const baseTitle = rawTitle.replace(/\\s*·\\s*MD\\s*$/i, \"\").trim() || \"Document\";\n      centerTitle.textContent = `${baseTitle} · ${kind}`;\n    } else {\n      titleEl.textContent = \"Artifacts\";\n    }\n\n    if (sel.artifactType === \"markdown\") {\n      if (documentChrome && viewMode === \"source\") {\n        const pre = createElement(\n          \"pre\",\n          \"persona-font-mono persona-text-xs persona-whitespace-pre-wrap persona-break-words persona-text-persona-primary\"\n        );\n        pre.textContent = sel.markdown ?? \"\";\n        content.appendChild(pre);\n        return;\n      }\n      const wrap = createElement(\"div\", \"persona-text-sm persona-leading-relaxed persona-markdown-bubble\");\n      wrap.innerHTML = toHtml(sel.markdown ?? \"\");\n      content.appendChild(wrap);\n      return;\n    }\n\n    const renderer = sel.component ? componentRegistry.get(sel.component) : undefined;\n    if (renderer) {\n      const stubMessage: AgentWidgetMessage = {\n        id: sel.id,\n        role: \"assistant\",\n        content: \"\",\n        createdAt: new Date().toISOString()\n      };\n      const ctx: ComponentContext = {\n        message: stubMessage,\n        config,\n        updateProps: () => {}\n      };\n      try {\n        const el = renderer(sel.props ?? {}, ctx);\n        if (el) {\n          content.appendChild(el);\n          return;\n        }\n      } catch {\n        /* fall through */\n      }\n    }\n    content.appendChild(fallbackComponentCard(sel));\n  };\n\n  const applyLayoutVisibility = () => {\n    const has = records.length > 0;\n    shell.classList.toggle(\"persona-hidden\", !has);\n    if (backdrop) {\n      const root =\n        typeof shell.closest === \"function\" ? shell.closest(\"[data-persona-root]\") : null;\n      const narrowHost = root?.classList.contains(\"persona-artifact-narrow-host\") ?? false;\n      const isMobile =\n        narrowHost ||\n        (typeof window !== \"undefined\" && window.matchMedia(\"(max-width: 640px)\").matches);\n      if (has && isMobile && mobileOpen) {\n        backdrop.classList.remove(\"persona-hidden\");\n        shell.classList.add(\"persona-artifact-drawer-open\");\n      } else if (!isMobile) {\n        backdrop.classList.add(\"persona-hidden\");\n        shell.classList.remove(\"persona-artifact-drawer-open\");\n      } else {\n        // isMobile && !(has && mobileOpen): e.g. dismissed drawer: keep closed chrome in sync\n        backdrop.classList.add(\"persona-hidden\");\n        shell.classList.remove(\"persona-artifact-drawer-open\");\n      }\n    }\n  };\n\n  return {\n    element: shell,\n    backdrop,\n    update(state: { artifacts: PersonaArtifactRecord[]; selectedId: string | null }) {\n      records = state.artifacts;\n      selectedId =\n        state.selectedId ??\n        state.artifacts[state.artifacts.length - 1]?.id ??\n        null;\n      if (records.length > 0) {\n        mobileOpen = true;\n      }\n      render();\n      applyLayoutVisibility();\n    },\n    setMobileOpen(open: boolean) {\n      mobileOpen = open;\n      if (!open && backdrop) {\n        backdrop.classList.add(\"persona-hidden\");\n        shell.classList.remove(\"persona-artifact-drawer-open\");\n      } else {\n        applyLayoutVisibility();\n      }\n    }\n  };\n}\n","import type { AgentWidgetConfig, PersonaArtifactKind } from \"../types\";\n\nexport function artifactsSidebarEnabled(config: AgentWidgetConfig | undefined): boolean {\n  return config?.features?.artifacts?.enabled === true;\n}\n\nexport function artifactKindAllowedByFeature(\n  config: AgentWidgetConfig | undefined,\n  kind: PersonaArtifactKind\n): boolean {\n  const allowed = config?.features?.artifacts?.allowedTypes;\n  if (!allowed?.length) return true;\n  return allowed.includes(kind);\n}\n\n/** Optional custom border on artifact pane via CSS vars + root classes. */\nexport function applyArtifactPaneBorderTheme(mount: HTMLElement, config: AgentWidgetConfig): void {\n  mount.classList.remove(\"persona-artifact-border-full\", \"persona-artifact-border-left\");\n  mount.style.removeProperty(\"--persona-artifact-pane-border\");\n  mount.style.removeProperty(\"--persona-artifact-pane-border-left\");\n  if (!artifactsSidebarEnabled(config)) return;\n\n  const l = config.features?.artifacts?.layout;\n  const full = l?.paneBorder?.trim();\n  const left = l?.paneBorderLeft?.trim();\n  if (full) {\n    mount.classList.add(\"persona-artifact-border-full\");\n    mount.style.setProperty(\"--persona-artifact-pane-border\", full);\n  } else if (left) {\n    mount.classList.add(\"persona-artifact-border-left\");\n    mount.style.setProperty(\"--persona-artifact-pane-border-left\", left);\n  }\n}\n\n/** Set CSS variables on the widget root for artifact split/pane sizing. Clears when artifacts disabled. */\nfunction clearDocumentToolbarLayoutVars(mount: HTMLElement): void {\n  mount.style.removeProperty(\"--persona-artifact-doc-toolbar-icon-color\");\n  mount.style.removeProperty(\"--persona-artifact-doc-toggle-active-bg\");\n  mount.style.removeProperty(\"--persona-artifact-doc-toggle-active-border\");\n}\n\nexport function applyArtifactLayoutCssVars(mount: HTMLElement, config: AgentWidgetConfig): void {\n  if (!artifactsSidebarEnabled(config)) {\n    mount.style.removeProperty(\"--persona-artifact-split-gap\");\n    mount.style.removeProperty(\"--persona-artifact-pane-width\");\n    mount.style.removeProperty(\"--persona-artifact-pane-max-width\");\n    mount.style.removeProperty(\"--persona-artifact-pane-min-width\");\n    mount.style.removeProperty(\"--persona-artifact-pane-bg\");\n    mount.style.removeProperty(\"--persona-artifact-pane-padding\");\n    clearDocumentToolbarLayoutVars(mount);\n    applyArtifactPaneBorderTheme(mount, config);\n    return;\n  }\n  const l = config.features?.artifacts?.layout;\n  mount.style.setProperty(\"--persona-artifact-split-gap\", l?.splitGap ?? \"0.5rem\");\n  mount.style.setProperty(\"--persona-artifact-pane-width\", l?.paneWidth ?? \"40%\");\n  mount.style.setProperty(\"--persona-artifact-pane-max-width\", l?.paneMaxWidth ?? \"28rem\");\n  if (l?.paneMinWidth) {\n    mount.style.setProperty(\"--persona-artifact-pane-min-width\", l.paneMinWidth);\n  } else {\n    mount.style.removeProperty(\"--persona-artifact-pane-min-width\");\n  }\n  const paneBg = l?.paneBackground?.trim();\n  if (paneBg) {\n    mount.style.setProperty(\"--persona-artifact-pane-bg\", paneBg);\n  } else {\n    mount.style.removeProperty(\"--persona-artifact-pane-bg\");\n  }\n  const panePad = l?.panePadding?.trim();\n  if (panePad) {\n    mount.style.setProperty(\"--persona-artifact-pane-padding\", panePad);\n  } else {\n    mount.style.removeProperty(\"--persona-artifact-pane-padding\");\n  }\n\n  const iconColor = l?.documentToolbarIconColor?.trim();\n  if (iconColor) {\n    mount.style.setProperty(\"--persona-artifact-doc-toolbar-icon-color\", iconColor);\n  } else {\n    mount.style.removeProperty(\"--persona-artifact-doc-toolbar-icon-color\");\n  }\n  const toggleBg = l?.documentToolbarToggleActiveBackground?.trim();\n  if (toggleBg) {\n    mount.style.setProperty(\"--persona-artifact-doc-toggle-active-bg\", toggleBg);\n  } else {\n    mount.style.removeProperty(\"--persona-artifact-doc-toggle-active-bg\");\n  }\n  const toggleBorder = l?.documentToolbarToggleActiveBorderColor?.trim();\n  if (toggleBorder) {\n    mount.style.setProperty(\"--persona-artifact-doc-toggle-active-border\", toggleBorder);\n  } else {\n    mount.style.removeProperty(\"--persona-artifact-doc-toggle-active-border\");\n  }\n\n  applyArtifactPaneBorderTheme(mount, config);\n}\n\nconst ARTIFACT_APPEARANCE_MODES = [\"panel\", \"seamless\"] as const;\n\n/** Toggle root classes for artifact pane appearance, radius, shadow, and unified chrome. */\nexport function applyArtifactPaneAppearance(mount: HTMLElement, config: AgentWidgetConfig): void {\n  for (const m of ARTIFACT_APPEARANCE_MODES) {\n    mount.classList.remove(`persona-artifact-appearance-${m}`);\n  }\n  mount.classList.remove(\"persona-artifact-unified-split\");\n  mount.style.removeProperty(\"--persona-artifact-pane-radius\");\n  mount.style.removeProperty(\"--persona-artifact-pane-shadow\");\n  mount.style.removeProperty(\"--persona-artifact-unified-outer-radius\");\n  if (!artifactsSidebarEnabled(config)) return;\n\n  const layout = config.features?.artifacts?.layout;\n  const raw = layout?.paneAppearance ?? \"panel\";\n  const mode = (ARTIFACT_APPEARANCE_MODES as readonly string[]).includes(raw) ? raw : \"panel\";\n  mount.classList.add(`persona-artifact-appearance-${mode}`);\n\n  const radius = layout?.paneBorderRadius?.trim();\n  if (radius) {\n    mount.style.setProperty(\"--persona-artifact-pane-radius\", radius);\n  }\n\n  const shadow = layout?.paneShadow?.trim();\n  if (shadow) {\n    mount.style.setProperty(\"--persona-artifact-pane-shadow\", shadow);\n  }\n\n  if (layout?.unifiedSplitChrome === true) {\n    mount.classList.add(\"persona-artifact-unified-split\");\n    const outer = layout.unifiedSplitOuterRadius?.trim() || radius;\n    if (outer) {\n      mount.style.setProperty(\"--persona-artifact-unified-outer-radius\", outer);\n    }\n  }\n}\n\n/** Widen floating panel when artifacts show (default true); `false` opts out. */\nexport function shouldExpandLauncherForArtifacts(\n  config: AgentWidgetConfig,\n  launcherEnabled: boolean\n): boolean {\n  if (!launcherEnabled || !artifactsSidebarEnabled(config)) return false;\n  return config.features?.artifacts?.layout?.expandLauncherPanelWhenOpen !== false;\n}\n","/** Minimum width (px) reserved for the chat column while resizing the artifact pane. */\nexport const ARTIFACT_RESIZE_CHAT_MIN_PX = 200;\n\n/** Default minimum width (px) for the artifact column when `resizableMinWidth` is unset. */\nexport const ARTIFACT_RESIZE_PANE_MIN_DEFAULT_PX = 200;\n\n/** Parse a `NNpx` string; returns `fallback` if missing or invalid. */\nexport function parseArtifactResizePx(input: string | undefined, fallback: number): number {\n  if (!input?.trim()) return fallback;\n  const m = /^(\\d+(?:\\.\\d+)?)px\\s*$/i.exec(input.trim());\n  if (!m) return fallback;\n  return Math.max(0, Number(m[1]));\n}\n\n/** Optional max from config: only valid `px` strings apply; otherwise no extra cap. */\nexport function parseArtifactResizeMaxPxOptional(input: string | undefined): number | null {\n  if (!input?.trim()) return null;\n  const m = /^(\\d+(?:\\.\\d+)?)px\\s*$/i.exec(input.trim());\n  if (!m) return null;\n  return Math.max(0, Number(m[1]));\n}\n\nexport function clampArtifactPaneWidth(widthPx: number, minPx: number, maxPx: number): number {\n  if (maxPx < minPx) return minPx;\n  return Math.min(maxPx, Math.max(minPx, widthPx));\n}\n\n/**\n * Upper bound for artifact width (px) from split row geometry: leave room for chat min, two flex gaps, handle.\n */\nexport function maxArtifactWidthFromSplit(\n  splitWidthPx: number,\n  gapPx: number,\n  handleWidthPx: number,\n  chatMinPx: number\n): number {\n  const raw = splitWidthPx - chatMinPx - 2 * gapPx - handleWidthPx;\n  return Math.max(0, raw);\n}\n\n/** Read the first gap value from computed `gap` (e.g. `8px` or `8px 8px`). */\nexport function readFlexGapPx(splitRoot: HTMLElement, win: Window): number {\n  const g = win.getComputedStyle(splitRoot).gap || \"0px\";\n  const first = g.trim().split(/\\s+/)[0] ?? \"0px\";\n  const m = /^([\\d.]+)px$/i.exec(first);\n  if (m) return Number(m[1]);\n  const m2 = /^([\\d.]+)/.exec(first);\n  return m2 ? Number(m2[1]) : 8;\n}\n\nexport function resolveArtifactPaneWidthPx(\n  candidatePx: number,\n  splitWidthPx: number,\n  gapPx: number,\n  handleWidthPx: number,\n  resizableMinWidth?: string,\n  resizableMaxWidth?: string\n): number {\n  const minPx = parseArtifactResizePx(resizableMinWidth, ARTIFACT_RESIZE_PANE_MIN_DEFAULT_PX);\n  let maxPx = maxArtifactWidthFromSplit(splitWidthPx, gapPx, handleWidthPx, ARTIFACT_RESIZE_CHAT_MIN_PX);\n  maxPx = Math.max(minPx, maxPx);\n  const cap = parseArtifactResizeMaxPxOptional(resizableMaxWidth);\n  if (cap !== null) {\n    maxPx = Math.min(maxPx, cap);\n  }\n  return clampArtifactPaneWidth(candidatePx, minPx, maxPx);\n}\n","import { createElement } from \"../utils/dom\";\nimport { AgentWidgetMessage, AgentWidgetConfig } from \"../types\";\nimport { AgentWidgetSession } from \"../session\";\n\nexport const formDefinitions: Record<\n  string,\n  {\n    title: string;\n    description?: string;\n    fields: Array<{\n      name: string;\n      label: string;\n      placeholder?: string;\n      type?: \"text\" | \"email\" | \"textarea\";\n      required?: boolean;\n    }>;\n    submitLabel?: string;\n  }\n> = {\n  init: {\n    title: \"Schedule a Demo\",\n    description: \"Share the basics and we'll follow up with a confirmation.\",\n    fields: [\n      { name: \"name\", label: \"Full name\", placeholder: \"Jane Doe\", required: true },\n      { name: \"email\", label: \"Work email\", placeholder: \"jane@example.com\", type: \"email\", required: true },\n      { name: \"notes\", label: \"What would you like to cover?\", type: \"textarea\" }\n    ],\n    submitLabel: \"Submit details\"\n  },\n  followup: {\n    title: \"Additional Information\",\n    description: \"Provide any extra details to tailor the next steps.\",\n    fields: [\n      { name: \"company\", label: \"Company\", placeholder: \"Acme Inc.\" },\n      { name: \"context\", label: \"Context\", type: \"textarea\", placeholder: \"Share more about your use case\" }\n    ],\n    submitLabel: \"Send\"\n  }\n};\n\nexport const enhanceWithForms = (\n  bubble: HTMLElement,\n  message: AgentWidgetMessage,\n  config: AgentWidgetConfig,\n  session: AgentWidgetSession\n) => {\n  const placeholders = bubble.querySelectorAll<HTMLElement>(\"[data-tv-form]\");\n  if (placeholders.length) {\n    placeholders.forEach((placeholder) => {\n      if (placeholder.dataset.enhanced === \"true\") return;\n      const type = placeholder.dataset.tvForm ?? \"init\";\n      placeholder.dataset.enhanced = \"true\";\n\n      const definition = formDefinitions[type] ?? formDefinitions.init;\n      placeholder.classList.add(\"persona-form-card\", \"persona-space-y-4\");\n\n      const heading = createElement(\"div\", \"persona-space-y-1\");\n      const title = createElement(\n        \"h3\",\n        \"persona-text-base persona-font-semibold persona-text-persona-primary\"\n      );\n      title.textContent = definition.title;\n      heading.appendChild(title);\n      if (definition.description) {\n        const desc = createElement(\n          \"p\",\n          \"persona-text-sm persona-text-persona-muted\"\n        );\n        desc.textContent = definition.description;\n        heading.appendChild(desc);\n      }\n\n      const form = document.createElement(\"form\");\n      form.className = \"persona-form-grid persona-space-y-3\";\n\n      definition.fields.forEach((field) => {\n        const group = createElement(\"label\", \"persona-form-field persona-flex persona-flex-col persona-gap-1\");\n        group.htmlFor = `${message.id}-${type}-${field.name}`;\n        const label = createElement(\"span\", \"persona-text-xs persona-font-medium persona-text-persona-muted\");\n        label.textContent = field.label;\n        group.appendChild(label);\n\n        const inputType = field.type ?? \"text\";\n        let control: HTMLInputElement | HTMLTextAreaElement;\n        if (inputType === \"textarea\") {\n          control = document.createElement(\"textarea\");\n          control.rows = 3;\n        } else {\n          control = document.createElement(\"input\");\n          control.type = inputType;\n        }\n        control.className =\n          \"persona-rounded-xl persona-border persona-border-gray-200 persona-bg-white persona-px-3 persona-py-2 persona-text-sm persona-text-persona-primary focus:persona-outline-none focus:persona-border-persona-primary\";\n        control.id = `${message.id}-${type}-${field.name}`;\n        control.name = field.name;\n        control.placeholder = field.placeholder ?? \"\";\n        if (field.required) {\n          control.required = true;\n        }\n        group.appendChild(control);\n        form.appendChild(group);\n      });\n\n      const actions = createElement(\n        \"div\",\n        \"persona-flex persona-items-center persona-justify-between persona-gap-2\"\n      );\n      const status = createElement(\n        \"div\",\n        \"persona-text-xs persona-text-persona-muted persona-min-h-[1.5rem]\"\n      );\n      const submit = createElement(\n        \"button\",\n        \"persona-inline-flex persona-items-center persona-rounded-full persona-bg-persona-primary persona-px-4 persona-py-2 persona-text-sm persona-font-semibold persona-text-white disabled:persona-opacity-60 persona-cursor-pointer\"\n      ) as HTMLButtonElement;\n      submit.type = \"submit\";\n      submit.textContent = definition.submitLabel ?? \"Submit\";\n      actions.appendChild(status);\n      actions.appendChild(submit);\n      form.appendChild(actions);\n\n      placeholder.replaceChildren(heading, form);\n\n      form.addEventListener(\"submit\", async (event) => {\n        event.preventDefault();\n        const formEndpoint = config.formEndpoint ?? \"/form\";\n        const formData = new FormData(form as HTMLFormElement);\n        const payload: Record<string, unknown> = {};\n        formData.forEach((value, key) => {\n          payload[key] = value;\n        });\n        payload[\"type\"] = type;\n\n        submit.disabled = true;\n        status.textContent = \"Submitting…\";\n\n        try {\n          const response = await fetch(formEndpoint, {\n            method: \"POST\",\n            headers: {\n              \"Content-Type\": \"application/json\"\n            },\n            body: JSON.stringify(payload)\n          });\n          if (!response.ok) {\n            throw new Error(`Form submission failed (${response.status})`);\n          }\n          const data = await response.json();\n          status.textContent = data.message ?? \"Thanks! We'll be in touch soon.\";\n          if (data.success && data.nextPrompt) {\n            await session.sendMessage(String(data.nextPrompt));\n          }\n        } catch (error) {\n          status.textContent =\n            error instanceof Error ? error.message : \"Something went wrong. Please try again.\";\n        } finally {\n          submit.disabled = false;\n        }\n      });\n    });\n  }\n};\n\n\n\n\n\n\n\n\n","import { AgentWidgetPlugin } from \"./types\";\n\nclass PluginRegistry {\n  private plugins: Map<string, AgentWidgetPlugin> = new Map();\n\n  /**\n   * Register a plugin\n   */\n  register(plugin: AgentWidgetPlugin): void {\n    if (this.plugins.has(plugin.id)) {\n      console.warn(`Plugin \"${plugin.id}\" is already registered. Overwriting.`);\n    }\n\n    this.plugins.set(plugin.id, plugin);\n    plugin.onRegister?.();\n  }\n\n  /**\n   * Unregister a plugin\n   */\n  unregister(pluginId: string): void {\n    const plugin = this.plugins.get(pluginId);\n    if (plugin) {\n      plugin.onUnregister?.();\n      this.plugins.delete(pluginId);\n    }\n  }\n\n  /**\n   * Get all plugins sorted by priority\n   */\n  getAll(): AgentWidgetPlugin[] {\n    return Array.from(this.plugins.values()).sort(\n      (a, b) => (b.priority ?? 0) - (a.priority ?? 0)\n    );\n  }\n\n  /**\n   * Get plugins for a specific instance (from config)\n   * Merges instance plugins with globally registered plugins\n   */\n  getForInstance(instancePlugins?: AgentWidgetPlugin[]): AgentWidgetPlugin[] {\n    const allPlugins = this.getAll();\n\n    if (!instancePlugins || instancePlugins.length === 0) {\n      return allPlugins;\n    }\n\n    // Merge instance plugins with global plugins\n    // Instance plugins override global plugins with the same ID\n    const instanceIds = new Set(instancePlugins.map(p => p.id));\n    const merged = [\n      ...allPlugins.filter(p => !instanceIds.has(p.id)),\n      ...instancePlugins\n    ];\n\n    return merged.sort((a, b) => (b.priority ?? 0) - (a.priority ?? 0));\n  }\n\n  /**\n   * Clear all plugins\n   */\n  clear(): void {\n    this.plugins.forEach(plugin => plugin.onUnregister?.());\n    this.plugins.clear();\n  }\n}\n\nexport const pluginRegistry = new PluginRegistry();\n\n\n\n\n\n\n\n\n","type Handler<T> = (payload: T) => void;\n\nexport type EventUnsubscribe = () => void;\n\nexport const createEventBus = <EventMap extends Record<string, any>>() => {\n  const listeners = new Map<keyof EventMap, Set<Handler<any>>>();\n\n  const on = <K extends keyof EventMap>(\n    event: K,\n    handler: Handler<EventMap[K]>\n  ): EventUnsubscribe => {\n    if (!listeners.has(event)) {\n      listeners.set(event, new Set());\n    }\n    listeners.get(event)!.add(handler as Handler<any>);\n    return () => off(event, handler);\n  };\n\n  const off = <K extends keyof EventMap>(\n    event: K,\n    handler: Handler<EventMap[K]>\n  ) => {\n    listeners.get(event)?.delete(handler as Handler<any>);\n  };\n\n  const emit = <K extends keyof EventMap>(event: K, payload: EventMap[K]) => {\n    listeners.get(event)?.forEach((handler) => {\n      try {\n        handler(payload);\n      } catch (error) {\n        if (typeof console !== \"undefined\") {\n          // eslint-disable-next-line no-console\n          console.error(\"[AgentWidget] Event handler error:\", error);\n        }\n      }\n    });\n  };\n\n  return { on, off, emit };\n};\n\n","import type {\n  AgentWidgetActionContext,\n  AgentWidgetActionEventPayload,\n  AgentWidgetActionHandler,\n  AgentWidgetActionHandlerResult,\n  AgentWidgetActionParser,\n  AgentWidgetParsedAction,\n  AgentWidgetControllerEventMap,\n  AgentWidgetMessage\n} from \"../types\";\n\ntype ActionManagerProcessContext = {\n  text: string;\n  message: AgentWidgetMessage;\n  streaming: boolean;\n  raw?: string;\n};\n\ntype ActionManagerOptions = {\n  parsers: AgentWidgetActionParser[];\n  handlers: AgentWidgetActionHandler[];\n  getSessionMetadata: () => Record<string, unknown>;\n  updateSessionMetadata: (\n    updater: (prev: Record<string, unknown>) => Record<string, unknown>\n  ) => void;\n  emit: <K extends keyof AgentWidgetControllerEventMap>(\n    event: K,\n    payload: AgentWidgetControllerEventMap[K]\n  ) => void;\n  documentRef: Document | null;\n};\n\nconst stripCodeFence = (value: string) => {\n  const match = value.match(/```(?:json)?\\s*([\\s\\S]*?)```/i);\n  return match ? match[1] : value;\n};\n\nconst extractJsonObject = (value: string) => {\n  const trimmed = value.trim();\n  const start = trimmed.indexOf(\"{\");\n  if (start === -1) return null;\n\n  let depth = 0;\n  for (let i = start; i < trimmed.length; i += 1) {\n    const char = trimmed[i];\n    if (char === \"{\") depth += 1;\n    if (char === \"}\") {\n      depth -= 1;\n      if (depth === 0) {\n        return trimmed.slice(start, i + 1);\n      }\n    }\n  }\n  return null;\n};\n\nexport const defaultJsonActionParser: AgentWidgetActionParser = ({ text }) => {\n  if (!text) return null;\n  if (!text.includes(\"{\")) return null;\n\n  try {\n    const withoutFence = stripCodeFence(text);\n    const jsonBody = extractJsonObject(withoutFence);\n    if (!jsonBody) return null;\n    const parsed = JSON.parse(jsonBody);\n    if (!parsed || typeof parsed !== \"object\" || !parsed.action) {\n      return null;\n    }\n    const { action, ...payload } = parsed;\n    return {\n      type: String(action),\n      payload,\n      raw: parsed\n    };\n  } catch {\n    return null;\n  }\n};\n\nconst asString = (value: unknown) =>\n  typeof value === \"string\" ? value : value == null ? \"\" : String(value);\n\nexport const defaultActionHandlers: Record<\n  string,\n  AgentWidgetActionHandler\n> = {\n  message: (action) => {\n    if (action.type !== \"message\") return;\n    const text = asString((action.payload as Record<string, unknown>).text);\n    return {\n      handled: true,\n      displayText: text\n    };\n  },\n  messageAndClick: (action, context) => {\n    if (action.type !== \"message_and_click\") return;\n    const payload = action.payload as Record<string, unknown>;\n    const selector = asString(payload.element);\n    if (selector && context.document?.querySelector) {\n      const element = context.document.querySelector<HTMLElement>(selector);\n      if (element) {\n        setTimeout(() => {\n          element.click();\n        }, 400);\n      } else if (typeof console !== \"undefined\") {\n        // eslint-disable-next-line no-console\n        console.warn(\"[AgentWidget] Element not found for selector:\", selector);\n      }\n    }\n    return {\n      handled: true,\n      displayText: asString(payload.text)\n    };\n  }\n};\n\nconst ensureArrayOfStrings = (value: unknown): string[] => {\n  if (Array.isArray(value)) {\n    return value.map((entry) => String(entry));\n  }\n  return [];\n};\n\nexport const createActionManager = (options: ActionManagerOptions) => {\n  let processedIds = new Set(\n    ensureArrayOfStrings(options.getSessionMetadata().processedActionMessageIds)\n  );\n\n  const syncFromMetadata = () => {\n    processedIds = new Set(\n      ensureArrayOfStrings(options.getSessionMetadata().processedActionMessageIds)\n    );\n  };\n\n  const persistProcessedIds = () => {\n    const latestIds = Array.from(processedIds);\n    options.updateSessionMetadata((prev) => ({\n      ...prev,\n      processedActionMessageIds: latestIds\n    }));\n  };\n\n  const process = (context: ActionManagerProcessContext): { text: string; persist: boolean; resubmit?: boolean } | null => {\n    if (\n      context.streaming ||\n      context.message.role !== \"assistant\" ||\n      !context.text ||\n      processedIds.has(context.message.id)\n    ) {\n      return null;\n    }\n\n    const parseSource =\n      (typeof context.raw === \"string\" && context.raw) ||\n      (typeof context.message.rawContent === \"string\" &&\n        context.message.rawContent) ||\n      (typeof context.text === \"string\" && context.text) ||\n      null;\n\n    if (\n      !parseSource &&\n      typeof context.text === \"string\" &&\n      context.text.trim().startsWith(\"{\") &&\n      typeof console !== \"undefined\"\n    ) {\n      // eslint-disable-next-line no-console\n      console.warn(\n        \"[AgentWidget] Structured response detected but no raw payload was provided. Ensure your stream parser returns { text, raw }.\"\n      );\n    }\n\n    const action = parseSource\n      ? options.parsers.reduce<AgentWidgetParsedAction | null>(\n          (acc, parser) =>\n            acc || parser?.({ text: parseSource, message: context.message }) || null,\n          null\n        )\n      : null;\n\n    if (!action) {\n      return null;\n    }\n\n    processedIds.add(context.message.id);\n    persistProcessedIds();\n\n    const eventPayload: AgentWidgetActionEventPayload = {\n      action,\n      message: context.message\n    };\n    options.emit(\"action:detected\", eventPayload);\n\n    for (const handler of options.handlers) {\n      if (!handler) continue;\n      try {\n        // Create triggerResubmit function that emits the resubmit event\n        // Handlers should call this AFTER async work completes (not return resubmit: true)\n        const triggerResubmit = () => {\n          options.emit(\"action:resubmit\", eventPayload);\n        };\n\n        const handlerResult = handler(action, {\n          message: context.message,\n          metadata: options.getSessionMetadata(),\n          updateMetadata: options.updateSessionMetadata,\n          document: options.documentRef,\n          triggerResubmit\n        } as AgentWidgetActionContext) as AgentWidgetActionHandlerResult | void;\n\n        if (!handlerResult) continue;\n\n        if (handlerResult.handled) {\n          // persistMessage defaults to true if not specified\n          const persist = handlerResult.persistMessage !== false;\n          const displayText = handlerResult.displayText !== undefined ? handlerResult.displayText : \"\";\n          // Return resubmit flag - the caller (ui.ts) will handle deferred resubmit\n          // after injectAssistantMessage is called (to avoid race conditions with async handlers)\n          return { text: displayText, persist, resubmit: handlerResult.resubmit };\n        }\n      } catch (error) {\n        if (typeof console !== \"undefined\") {\n          // eslint-disable-next-line no-console\n          console.error(\"[AgentWidget] Action handler error:\", error);\n        }\n      }\n    }\n\n    return { text: \"\", persist: true };\n  };\n\n  return {\n    process,\n    syncFromMetadata\n  };\n};\n\n","import type {\n  AgentWidgetMessage,\n  AgentWidgetStorageAdapter,\n  AgentWidgetStoredState,\n  PersonaArtifactRecord\n} from \"../types\";\n\nconst safeJsonParse = (value: string | null) => {\n  if (!value) return null;\n  try {\n    return JSON.parse(value);\n  } catch (error) {\n    if (typeof console !== \"undefined\") {\n      // eslint-disable-next-line no-console\n      console.error(\"[AgentWidget] Failed to parse stored state:\", error);\n    }\n    return null;\n  }\n};\n\nconst sanitizeMessages = (messages: AgentWidgetMessage[]) =>\n  messages.map((message) => ({\n    ...message,\n    streaming: false\n  }));\n\nconst sanitizeArtifacts = (artifacts: PersonaArtifactRecord[]) =>\n  artifacts.map((artifact) => ({\n    ...artifact,\n    status: \"complete\" as const\n  }));\n\nexport const createLocalStorageAdapter = (\n  key = \"persona-state\"\n): AgentWidgetStorageAdapter => {\n  const getStorage = () => {\n    if (typeof window === \"undefined\" || !window.localStorage) {\n      return null;\n    }\n    return window.localStorage;\n  };\n\n  return {\n    load: () => {\n      const storage = getStorage();\n      if (!storage) return null;\n      return safeJsonParse(storage.getItem(key));\n    },\n    save: (state: AgentWidgetStoredState) => {\n      const storage = getStorage();\n      if (!storage) return;\n      try {\n        const payload: AgentWidgetStoredState = {\n          ...state,\n          messages: state.messages ? sanitizeMessages(state.messages) : undefined,\n          artifacts: state.artifacts ? sanitizeArtifacts(state.artifacts) : undefined\n        };\n        storage.setItem(key, JSON.stringify(payload));\n      } catch (error) {\n        if (typeof console !== \"undefined\") {\n          // eslint-disable-next-line no-console\n          console.error(\"[AgentWidget] Failed to persist state:\", error);\n        }\n      }\n    },\n    clear: () => {\n      const storage = getStorage();\n      if (!storage) return;\n      try {\n        storage.removeItem(key);\n      } catch (error) {\n        if (typeof console !== \"undefined\") {\n          // eslint-disable-next-line no-console\n          console.error(\"[AgentWidget] Failed to clear stored state:\", error);\n        }\n      }\n    }\n  };\n};\n\n","import { parse as parsePartialJson, STR, OBJ } from \"partial-json\";\n\n/**\n * Represents a component directive extracted from JSON\n */\nexport interface ComponentDirective {\n  component: string;\n  props: Record<string, unknown>;\n  raw: string;\n}\n\n/**\n * Checks if a parsed object is a component directive\n */\nfunction isComponentDirective(obj: unknown): obj is { component: string; props?: unknown } {\n  if (!obj || typeof obj !== \"object\") return false;\n  if (!(\"component\" in obj)) return false;\n  const component = (obj as { component: unknown }).component;\n  return typeof component === \"string\" && component.length > 0;\n}\n\n/**\n * Extracts component directive from parsed JSON object\n */\nfunction extractComponentDirective(\n  parsed: unknown,\n  rawJson: string\n): ComponentDirective | null {\n  if (!isComponentDirective(parsed)) {\n    return null;\n  }\n\n  const props = parsed.props && typeof parsed.props === \"object\" && parsed.props !== null\n    ? (parsed.props as Record<string, unknown>)\n    : {};\n\n  return {\n    component: parsed.component,\n    props,\n    raw: rawJson\n  };\n}\n\n/**\n * Creates a parser that extracts component directives from JSON streams\n * This parser looks for objects with a \"component\" field and extracts\n * the component name and props incrementally as they stream in.\n */\nexport function createComponentStreamParser() {\n  let extractedDirective: ComponentDirective | null = null;\n  let processedLength = 0;\n\n  return {\n    /**\n     * Get the currently extracted component directive\n     */\n    getExtractedDirective: (): ComponentDirective | null => {\n      return extractedDirective;\n    },\n\n    /**\n     * Process a chunk of JSON and extract component directive if present\n     */\n    processChunk: (accumulatedContent: string): ComponentDirective | null => {\n      // Validate that the accumulated content looks like JSON\n      const trimmed = accumulatedContent.trim();\n      if (!trimmed.startsWith(\"{\") && !trimmed.startsWith(\"[\")) {\n        return null;\n      }\n\n      // Skip if no new content\n      if (accumulatedContent.length <= processedLength) {\n        return extractedDirective;\n      }\n\n      try {\n        // Parse partial JSON - allow partial strings and objects during streaming\n        // STR | OBJ allows incomplete strings and objects during streaming\n        const parsed = parsePartialJson(accumulatedContent, STR | OBJ);\n\n        // Try to extract component directive\n        const directive = extractComponentDirective(parsed, accumulatedContent);\n        if (directive) {\n          extractedDirective = directive;\n        }\n      } catch (error) {\n        // If parsing fails completely, keep the last extracted directive\n        // This can happen with very malformed JSON during streaming\n      }\n\n      // Update processed length\n      processedLength = accumulatedContent.length;\n\n      return extractedDirective;\n    },\n\n    /**\n     * Reset the parser state\n     */\n    reset: () => {\n      extractedDirective = null;\n      processedLength = 0;\n    }\n  };\n}\n\n/**\n * Type guard to check if an object is a component directive\n */\nexport function isComponentDirectiveType(obj: unknown): obj is ComponentDirective {\n  return (\n    typeof obj === \"object\" &&\n    obj !== null &&\n    \"component\" in obj &&\n    typeof (obj as { component: unknown }).component === \"string\" &&\n    \"props\" in obj &&\n    typeof (obj as { props: unknown }).props === \"object\"\n  );\n}\n","import { AgentWidgetMessage, AgentWidgetConfig } from \"../types\";\nimport { componentRegistry, ComponentContext } from \"../components/registry\";\nimport { ComponentDirective, createComponentStreamParser } from \"./component-parser\";\nimport { createStandardBubble as _createStandardBubble, MessageTransform } from \"../components/message-bubble\";\n\n/**\n * Options for component middleware\n */\nexport interface ComponentMiddlewareOptions {\n  config: AgentWidgetConfig;\n  message: AgentWidgetMessage;\n  transform: MessageTransform;\n  onPropsUpdate?: (props: Record<string, unknown>) => void;\n}\n\n/**\n * Renders a component directive into an HTMLElement\n */\nexport function renderComponentDirective(\n  directive: ComponentDirective,\n  options: ComponentMiddlewareOptions\n): HTMLElement | null {\n  const { config, message, onPropsUpdate } = options;\n\n  // Get component renderer from registry\n  const renderer = componentRegistry.get(directive.component);\n  if (!renderer) {\n    // Component not found, fall back to default rendering\n    console.warn(\n      `[ComponentMiddleware] Component \"${directive.component}\" not found in registry. Falling back to default rendering.`\n    );\n    return null;\n  }\n\n  // Create component context\n  const context: ComponentContext = {\n    message,\n    config,\n    updateProps: (newProps: Record<string, unknown>) => {\n      if (onPropsUpdate) {\n        onPropsUpdate(newProps);\n      }\n    }\n  };\n\n  try {\n    // Render the component\n    const element = renderer(directive.props, context);\n    return element;\n  } catch (error) {\n    console.error(\n      `[ComponentMiddleware] Error rendering component \"${directive.component}\":`,\n      error\n    );\n    return null;\n  }\n}\n\n/**\n * Creates middleware that processes component directives from streamed JSON\n */\nexport function createComponentMiddleware() {\n  const parser = createComponentStreamParser();\n\n  return {\n    /**\n     * Process accumulated content and extract component directive\n     */\n    processChunk: (accumulatedContent: string): ComponentDirective | null => {\n      return parser.processChunk(accumulatedContent);\n    },\n\n    /**\n     * Get the currently extracted directive\n     */\n    getDirective: (): ComponentDirective | null => {\n      return parser.getExtractedDirective();\n    },\n\n    /**\n     * Reset the parser state\n     */\n    reset: () => {\n      parser.reset();\n    }\n  };\n}\n\n/**\n * Picks the field that may carry a JSON directive payload. Streamed messages\n * populate `rawContent`; manually injected messages may pass the JSON via\n * `content` directly. We try `rawContent` first, then fall back to `content`\n * when it looks like JSON, so both code paths render the same way.\n */\nfunction selectDirectiveSource(message: AgentWidgetMessage): string | null {\n  if (typeof message.rawContent === \"string\" && message.rawContent.length > 0) {\n    return message.rawContent;\n  }\n  if (typeof message.content === \"string\") {\n    const trimmed = message.content.trim();\n    if (trimmed.startsWith(\"{\") || trimmed.startsWith(\"[\")) {\n      return message.content;\n    }\n  }\n  return null;\n}\n\n/**\n * Checks if a message contains a component directive.\n *\n * Looks at `rawContent` first (the field set by stream parsers); falls back\n * to `content` when it looks like JSON, so injected messages that pass the\n * directive via `content` (or have no `rawContent`) are still recognized.\n */\nexport function hasComponentDirective(message: AgentWidgetMessage): boolean {\n  const source = selectDirectiveSource(message);\n  if (!source) return false;\n\n  try {\n    const parsed = JSON.parse(source);\n    return (\n      typeof parsed === \"object\" &&\n      parsed !== null &&\n      \"component\" in parsed &&\n      typeof (parsed as { component: unknown }).component === \"string\"\n    );\n  } catch {\n    return false;\n  }\n}\n\n/**\n * Extracts component directive from a complete message.\n *\n * Looks at `rawContent` first (the field set by stream parsers); falls back\n * to `content` when it looks like JSON, so injected messages that pass the\n * directive via `content` (or have no `rawContent`) render the same as\n * streamed ones.\n */\nexport function extractComponentDirectiveFromMessage(\n  message: AgentWidgetMessage\n): ComponentDirective | null {\n  const source = selectDirectiveSource(message);\n  if (!source) return null;\n\n  try {\n    const parsed = JSON.parse(source);\n    if (\n      typeof parsed === \"object\" &&\n      parsed !== null &&\n      \"component\" in parsed &&\n      typeof (parsed as { component: unknown }).component === \"string\"\n    ) {\n      const directive = parsed as { component: string; props?: unknown };\n      return {\n        component: directive.component,\n        props: (directive.props && typeof directive.props === \"object\" && directive.props !== null\n          ? directive.props\n          : {}) as Record<string, unknown>,\n        raw: source\n      };\n    }\n  } catch {\n    // Not valid JSON or not a component directive\n  }\n\n  return null;\n}\n","/**\n * Feedback UI components for CSAT and NPS collection\n */\n\nexport type CSATFeedbackOptions = {\n  /** Callback when user submits CSAT feedback */\n  onSubmit: (rating: number, comment?: string) => void | Promise<void>;\n  /** Callback when user dismisses the feedback form */\n  onDismiss?: () => void;\n  /** Title text */\n  title?: string;\n  /** Subtitle/question text */\n  subtitle?: string;\n  /** Placeholder for optional comment field */\n  commentPlaceholder?: string;\n  /** Submit button text */\n  submitText?: string;\n  /** Skip button text */\n  skipText?: string;\n  /** Show comment field */\n  showComment?: boolean;\n  /** Rating labels (5 items for ratings 1-5) */\n  ratingLabels?: [string, string, string, string, string];\n};\n\nexport type NPSFeedbackOptions = {\n  /** Callback when user submits NPS feedback */\n  onSubmit: (rating: number, comment?: string) => void | Promise<void>;\n  /** Callback when user dismisses the feedback form */\n  onDismiss?: () => void;\n  /** Title text */\n  title?: string;\n  /** Subtitle/question text */\n  subtitle?: string;\n  /** Placeholder for optional comment field */\n  commentPlaceholder?: string;\n  /** Submit button text */\n  submitText?: string;\n  /** Skip button text */\n  skipText?: string;\n  /** Show comment field */\n  showComment?: boolean;\n  /** Low label (left side) */\n  lowLabel?: string;\n  /** High label (right side) */\n  highLabel?: string;\n};\n\nconst defaultCSATLabels: [string, string, string, string, string] = [\n  'Very dissatisfied',\n  'Dissatisfied',\n  'Neutral',\n  'Satisfied',\n  'Very satisfied'\n];\n\n/**\n * Create a CSAT (Customer Satisfaction) feedback form\n * Rating scale: 1-5\n */\nexport function createCSATFeedback(options: CSATFeedbackOptions): HTMLElement {\n  const {\n    onSubmit,\n    onDismiss,\n    title = 'How satisfied are you?',\n    subtitle = 'Please rate your experience',\n    commentPlaceholder = 'Share your thoughts (optional)...',\n    submitText = 'Submit',\n    skipText = 'Skip',\n    showComment = true,\n    ratingLabels = defaultCSATLabels,\n  } = options;\n\n  const container = document.createElement('div');\n  container.className = 'persona-feedback-container persona-feedback-csat';\n  container.setAttribute('role', 'dialog');\n  container.setAttribute('aria-label', 'Customer satisfaction feedback');\n\n  let selectedRating: number | null = null;\n\n  // Create inner content\n  const content = document.createElement('div');\n  content.className = 'persona-feedback-content';\n\n  // Header\n  const header = document.createElement('div');\n  header.className = 'persona-feedback-header';\n  \n  const titleEl = document.createElement('h3');\n  titleEl.className = 'persona-feedback-title';\n  titleEl.textContent = title;\n  header.appendChild(titleEl);\n\n  const subtitleEl = document.createElement('p');\n  subtitleEl.className = 'persona-feedback-subtitle';\n  subtitleEl.textContent = subtitle;\n  header.appendChild(subtitleEl);\n\n  content.appendChild(header);\n\n  // Rating buttons (1-5 stars or numbers)\n  const ratingContainer = document.createElement('div');\n  ratingContainer.className = 'persona-feedback-rating persona-feedback-rating-csat';\n  ratingContainer.setAttribute('role', 'radiogroup');\n  ratingContainer.setAttribute('aria-label', 'Satisfaction rating from 1 to 5');\n\n  const ratingButtons: HTMLButtonElement[] = [];\n\n  for (let i = 1; i <= 5; i++) {\n    const ratingButton = document.createElement('button');\n    ratingButton.type = 'button';\n    ratingButton.className = 'persona-feedback-rating-btn persona-feedback-star-btn';\n    ratingButton.setAttribute('role', 'radio');\n    ratingButton.setAttribute('aria-checked', 'false');\n    ratingButton.setAttribute('aria-label', `${i} star${i > 1 ? 's' : ''}: ${ratingLabels[i - 1]}`);\n    ratingButton.title = ratingLabels[i - 1];\n    ratingButton.dataset.rating = String(i);\n\n    // Star icon (filled when selected)\n    ratingButton.innerHTML = `\n      <svg class=\"persona-feedback-star\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\">\n        <polygon points=\"12 2 15.09 8.26 22 9.27 17 14.14 18.18 21.02 12 17.77 5.82 21.02 7 14.14 2 9.27 8.91 8.26 12 2\"></polygon>\n      </svg>\n    `;\n\n    ratingButton.addEventListener('click', () => {\n      selectedRating = i;\n      ratingButtons.forEach((btn, index) => {\n        const isSelected = index < i;\n        btn.classList.toggle('selected', isSelected);\n        btn.setAttribute('aria-checked', index === i - 1 ? 'true' : 'false');\n      });\n    });\n\n    ratingButtons.push(ratingButton);\n    ratingContainer.appendChild(ratingButton);\n  }\n\n  content.appendChild(ratingContainer);\n\n  // Comment field\n  let commentTextarea: HTMLTextAreaElement | null = null;\n  if (showComment) {\n    const commentContainer = document.createElement('div');\n    commentContainer.className = 'persona-feedback-comment-container';\n    \n    commentTextarea = document.createElement('textarea');\n    commentTextarea.className = 'persona-feedback-comment';\n    commentTextarea.placeholder = commentPlaceholder;\n    commentTextarea.rows = 3;\n    commentTextarea.setAttribute('aria-label', 'Additional comments');\n    \n    commentContainer.appendChild(commentTextarea);\n    content.appendChild(commentContainer);\n  }\n\n  // Action buttons\n  const actions = document.createElement('div');\n  actions.className = 'persona-feedback-actions';\n\n  const skipButton = document.createElement('button');\n  skipButton.type = 'button';\n  skipButton.className = 'persona-feedback-btn persona-feedback-btn-skip';\n  skipButton.textContent = skipText;\n  skipButton.addEventListener('click', () => {\n    onDismiss?.();\n    container.remove();\n  });\n\n  const submitButton = document.createElement('button');\n  submitButton.type = 'button';\n  submitButton.className = 'persona-feedback-btn persona-feedback-btn-submit';\n  submitButton.textContent = submitText;\n  submitButton.addEventListener('click', async () => {\n    if (selectedRating === null) {\n      // Shake the rating container to indicate selection required\n      ratingContainer.classList.add('persona-feedback-shake');\n      setTimeout(() => ratingContainer.classList.remove('persona-feedback-shake'), 500);\n      return;\n    }\n    \n    submitButton.disabled = true;\n    submitButton.textContent = 'Submitting...';\n    \n    try {\n      const comment = commentTextarea?.value.trim() || undefined;\n      await onSubmit(selectedRating, comment);\n      container.remove();\n    } catch (error) {\n      submitButton.disabled = false;\n      submitButton.textContent = submitText;\n      // eslint-disable-next-line no-console\n      console.error('[CSAT Feedback] Failed to submit:', error);\n    }\n  });\n\n  actions.appendChild(skipButton);\n  actions.appendChild(submitButton);\n  content.appendChild(actions);\n\n  container.appendChild(content);\n\n  return container;\n}\n\n/**\n * Create an NPS (Net Promoter Score) feedback form\n * Rating scale: 0-10\n */\nexport function createNPSFeedback(options: NPSFeedbackOptions): HTMLElement {\n  const {\n    onSubmit,\n    onDismiss,\n    title = 'How likely are you to recommend us?',\n    subtitle = 'On a scale of 0 to 10',\n    commentPlaceholder = 'What could we do better? (optional)...',\n    submitText = 'Submit',\n    skipText = 'Skip',\n    showComment = true,\n    lowLabel = 'Not likely',\n    highLabel = 'Very likely',\n  } = options;\n\n  const container = document.createElement('div');\n  container.className = 'persona-feedback-container persona-feedback-nps';\n  container.setAttribute('role', 'dialog');\n  container.setAttribute('aria-label', 'Net Promoter Score feedback');\n\n  let selectedRating: number | null = null;\n\n  // Create inner content\n  const content = document.createElement('div');\n  content.className = 'persona-feedback-content';\n\n  // Header\n  const header = document.createElement('div');\n  header.className = 'persona-feedback-header';\n  \n  const titleEl = document.createElement('h3');\n  titleEl.className = 'persona-feedback-title';\n  titleEl.textContent = title;\n  header.appendChild(titleEl);\n\n  const subtitleEl = document.createElement('p');\n  subtitleEl.className = 'persona-feedback-subtitle';\n  subtitleEl.textContent = subtitle;\n  header.appendChild(subtitleEl);\n\n  content.appendChild(header);\n\n  // Rating buttons (0-10)\n  const ratingContainer = document.createElement('div');\n  ratingContainer.className = 'persona-feedback-rating persona-feedback-rating-nps';\n  ratingContainer.setAttribute('role', 'radiogroup');\n  ratingContainer.setAttribute('aria-label', 'Likelihood rating from 0 to 10');\n\n  // Labels row\n  const labelsRow = document.createElement('div');\n  labelsRow.className = 'persona-feedback-labels';\n  \n  const lowLabelEl = document.createElement('span');\n  lowLabelEl.className = 'persona-feedback-label-low';\n  lowLabelEl.textContent = lowLabel;\n  \n  const highLabelEl = document.createElement('span');\n  highLabelEl.className = 'persona-feedback-label-high';\n  highLabelEl.textContent = highLabel;\n  \n  labelsRow.appendChild(lowLabelEl);\n  labelsRow.appendChild(highLabelEl);\n  \n  // Numbers row\n  const numbersRow = document.createElement('div');\n  numbersRow.className = 'persona-feedback-numbers';\n\n  const ratingButtons: HTMLButtonElement[] = [];\n\n  for (let i = 0; i <= 10; i++) {\n    const ratingButton = document.createElement('button');\n    ratingButton.type = 'button';\n    ratingButton.className = 'persona-feedback-rating-btn persona-feedback-number-btn';\n    ratingButton.setAttribute('role', 'radio');\n    ratingButton.setAttribute('aria-checked', 'false');\n    ratingButton.setAttribute('aria-label', `Rating ${i} out of 10`);\n    ratingButton.textContent = String(i);\n    ratingButton.dataset.rating = String(i);\n\n    // Color coding: detractors (0-6), passives (7-8), promoters (9-10)\n    if (i <= 6) {\n      ratingButton.classList.add('persona-feedback-detractor');\n    } else if (i <= 8) {\n      ratingButton.classList.add('persona-feedback-passive');\n    } else {\n      ratingButton.classList.add('persona-feedback-promoter');\n    }\n\n    ratingButton.addEventListener('click', () => {\n      selectedRating = i;\n      ratingButtons.forEach((btn, index) => {\n        btn.classList.toggle('selected', index === i);\n        btn.setAttribute('aria-checked', index === i ? 'true' : 'false');\n      });\n    });\n\n    ratingButtons.push(ratingButton);\n    numbersRow.appendChild(ratingButton);\n  }\n\n  ratingContainer.appendChild(labelsRow);\n  ratingContainer.appendChild(numbersRow);\n  content.appendChild(ratingContainer);\n\n  // Comment field\n  let commentTextarea: HTMLTextAreaElement | null = null;\n  if (showComment) {\n    const commentContainer = document.createElement('div');\n    commentContainer.className = 'persona-feedback-comment-container';\n    \n    commentTextarea = document.createElement('textarea');\n    commentTextarea.className = 'persona-feedback-comment';\n    commentTextarea.placeholder = commentPlaceholder;\n    commentTextarea.rows = 3;\n    commentTextarea.setAttribute('aria-label', 'Additional comments');\n    \n    commentContainer.appendChild(commentTextarea);\n    content.appendChild(commentContainer);\n  }\n\n  // Action buttons\n  const actions = document.createElement('div');\n  actions.className = 'persona-feedback-actions';\n\n  const skipButton = document.createElement('button');\n  skipButton.type = 'button';\n  skipButton.className = 'persona-feedback-btn persona-feedback-btn-skip';\n  skipButton.textContent = skipText;\n  skipButton.addEventListener('click', () => {\n    onDismiss?.();\n    container.remove();\n  });\n\n  const submitButton = document.createElement('button');\n  submitButton.type = 'button';\n  submitButton.className = 'persona-feedback-btn persona-feedback-btn-submit';\n  submitButton.textContent = submitText;\n  submitButton.addEventListener('click', async () => {\n    if (selectedRating === null) {\n      // Shake the rating container to indicate selection required\n      numbersRow.classList.add('persona-feedback-shake');\n      setTimeout(() => numbersRow.classList.remove('persona-feedback-shake'), 500);\n      return;\n    }\n    \n    submitButton.disabled = true;\n    submitButton.textContent = 'Submitting...';\n    \n    try {\n      const comment = commentTextarea?.value.trim() || undefined;\n      await onSubmit(selectedRating, comment);\n      container.remove();\n    } catch (error) {\n      submitButton.disabled = false;\n      submitButton.textContent = submitText;\n      // eslint-disable-next-line no-console\n      console.error('[NPS Feedback] Failed to submit:', error);\n    }\n  });\n\n  actions.appendChild(skipButton);\n  actions.appendChild(submitButton);\n  content.appendChild(actions);\n\n  container.appendChild(content);\n\n  return container;\n}\n\n\n\n","import { escapeHtml, createMarkdownProcessorFromConfig } from \"./postprocessors\";\nimport { resolveSanitizer } from \"./utils/sanitize\";\nimport { stabilizeStreamingTables } from \"./utils/streaming-table\";\nimport { loadMarkdownParsers, getMarkdownParsersSync } from \"./markdown-parsers-loader\";\nimport { AgentWidgetSession, AgentWidgetSessionStatus } from \"./session\";\nimport {\n  AgentWidgetConfig,\n  AgentWidgetApprovalDecisionOptions,\n  AgentWidgetMessage,\n  AgentWidgetEvent,\n  AgentWidgetStorageAdapter,\n  AgentWidgetStoredState,\n  AgentWidgetControllerEventMap,\n  AgentWidgetVoiceStateEvent,\n  AgentWidgetStateEvent,\n  AgentWidgetStateSnapshot,\n  WidgetLayoutSlot,\n  SlotRenderer,\n  AgentWidgetMessageFeedback,\n  ContentPart,\n  InjectMessageOptions,\n  InjectAssistantMessageOptions,\n  InjectUserMessageOptions,\n  InjectSystemMessageOptions,\n  InjectComponentDirectiveOptions,\n  LoadingIndicatorRenderContext,\n  IdleIndicatorRenderContext,\n  VoiceStatus,\n  ReadAloudState,\n  PersonaArtifactRecord,\n  PersonaArtifactManualUpsert\n} from \"./types\";\nimport { AttachmentManager } from \"./utils/attachment-manager\";\nimport { createTextPart, ALL_SUPPORTED_MIME_TYPES } from \"./utils/content\";\nimport { applyThemeVariables, createThemeObserver, getActiveTheme } from \"./utils/theme\";\nimport { resolveTokenValue } from \"./utils/tokens\";\nimport { renderLucideIcon } from \"./utils/icons\";\nimport { createElement, createElementInDocument } from \"./utils/dom\";\nimport { morphMessages } from \"./utils/morph\";\nimport { normalizeCopiedSelectionText } from \"./utils/copy-selection\";\nimport {\n  navigateComposerHistory,\n  INITIAL_HISTORY_STATE,\n  type ComposerHistoryState\n} from \"./utils/composer-history\";\nimport { computeMessageFingerprint, createMessageCache, getCachedWrapper, setCachedWrapper, pruneCache } from \"./utils/message-fingerprint\";\nimport {\n  computeAnchorScrollState,\n  computeShrunkSpacerHeight,\n  createFollowStateController,\n  getScrollBottomOffset,\n  hasSelectionWithin,\n  isElementNearBottom,\n  resolveFollowStateFromScroll,\n  resolveFollowStateFromWheel\n} from \"./utils/auto-follow\";\nimport { statusCopy, DEFAULT_OVERLAY_Z_INDEX, PORTALED_OVERLAY_Z_INDEX } from \"./utils/constants\";\nimport {\n  applyStreamBuffer,\n  createSkeletonPlaceholder,\n  createStreamCaret,\n  detachAllPlugins,\n  ensurePluginActive,\n  resolveStreamAnimation,\n  resolveStreamAnimationPlugin,\n  wrapStreamAnimation,\n} from \"./utils/stream-animation\";\nimport { syncOverlayHostStacking } from \"./utils/overlay-host-stacking\";\nimport { acquireScrollLock } from \"./utils/scroll-lock\";\nimport { isComposerBarMountMode, isDockedMountMode, resolveDockConfig } from \"./utils/dock\";\nimport { LauncherButton } from \"./components/launcher\";\nimport { buildHeader, buildComposer, attachHeaderToContainer } from \"./components/panel\";\nimport { createWidgetView, resolveLauncher } from \"./components/widget-view\";\nimport { HEADER_THEME_CSS } from \"./components/header-builder\";\nimport { buildHeaderWithLayout } from \"./components/header-layouts\";\nimport { positionMap } from \"./utils/positioning\";\nimport type { HeaderElements as _HeaderElements, ComposerElements as _ComposerElements } from \"./components/panel\";\nimport { MessageTransform, MessageActionCallbacks, LoadingIndicatorRenderer } from \"./components/message-bubble\";\nimport { createStandardBubble, createTypingIndicator } from \"./components/message-bubble\";\nimport { createReasoningBubble, reasoningExpansionState, updateReasoningBubbleUI } from \"./components/reasoning-bubble\";\nimport { createToolBubble, toolExpansionState, updateToolBubbleUI } from \"./components/tool-bubble\";\nimport {\n  buildStructuredAnswers,\n  ensureAskUserQuestionSheet,\n  getCurrentIndex,\n  getQuestionCount,\n  getSelectedLabels,\n  isAskUserQuestionMessage,\n  isGroupedSheet,\n  navigateToPage,\n  parseAskUserQuestionPayload,\n  readAnswersFromSheet,\n  removeAskUserQuestionSheet,\n  setCurrentAnswer,\n} from \"./components/ask-user-question-bubble\";\nimport {\n  isSuggestRepliesMessage,\n  latestAgentSuggestions,\n} from \"./suggest-replies-tool\";\nimport { formatElapsedMs } from \"./utils/formatting\";\nimport { approvalDetailsExpansionState, createApprovalBubble, updateApprovalDetailsUI } from \"./components/approval-bubble\";\nimport { createBuiltInApprovalPlugin } from \"./components/approval-actions\";\nimport { createSuggestions } from \"./components/suggestions\";\nimport { EventStreamBuffer } from \"./utils/event-stream-buffer\";\nimport { EventStreamStore } from \"./utils/event-stream-store\";\nimport { ThroughputTracker } from \"./utils/throughput-tracker\";\nimport { createEventStreamView } from \"./components/event-stream-view\";\nimport { createArtifactPane, type ArtifactPaneApi } from \"./components/artifact-pane\";\nimport {\n  artifactsSidebarEnabled,\n  applyArtifactLayoutCssVars,\n  applyArtifactPaneAppearance,\n  shouldExpandLauncherForArtifacts\n} from \"./utils/artifact-gate\";\nimport { readFlexGapPx, resolveArtifactPaneWidthPx } from \"./utils/artifact-resize\";\nimport { enhanceWithForms } from \"./components/forms\";\nimport { pluginRegistry } from \"./plugins/registry\";\nimport { mergeWithDefaults, DEFAULT_FLOATING_LAUNCHER_WIDTH } from \"./defaults\";\nimport { createEventBus } from \"./utils/events\";\nimport {\n  createActionManager,\n  defaultActionHandlers,\n  defaultJsonActionParser\n} from \"./utils/actions\";\nimport { createLocalStorageAdapter } from \"./utils/storage\";\nimport { componentRegistry } from \"./components/registry\";\nimport {\n  renderComponentDirective,\n  extractComponentDirectiveFromMessage,\n  hasComponentDirective\n} from \"./utils/component-middleware\";\nimport {\n  createCSATFeedback,\n  createNPSFeedback,\n  type CSATFeedbackOptions,\n  type NPSFeedbackOptions\n} from \"./components/feedback\";\n\n// Default localStorage key for chat history (automatically cleared on clear chat)\nconst DEFAULT_CHAT_HISTORY_STORAGE_KEY = \"persona-chat-history\";\nconst VOICE_STATE_RESTORE_WINDOW = 30 * 1000;\n\nconst IMAGE_FILE_EXTENSION_BY_MIME_TYPE: Record<string, string> = {\n  \"image/png\": \"png\",\n  \"image/jpeg\": \"jpg\",\n  \"image/jpg\": \"jpg\",\n  \"image/gif\": \"gif\",\n  \"image/webp\": \"webp\",\n  \"image/svg+xml\": \"svg\",\n  \"image/bmp\": \"bmp\",\n  \"image/tiff\": \"tiff\"\n};\n\nfunction getClipboardImageFiles(clipboardData: DataTransfer | null): File[] {\n  if (!clipboardData) return [];\n\n  const imageFiles: File[] = [];\n  const clipboardItems = Array.from(clipboardData.items ?? []);\n\n  for (const item of clipboardItems) {\n    if (item.kind !== \"file\" || !item.type.startsWith(\"image/\")) continue;\n    const file = item.getAsFile();\n    if (!file) continue;\n\n    if (file.name) {\n      imageFiles.push(file);\n      continue;\n    }\n\n    const extension = IMAGE_FILE_EXTENSION_BY_MIME_TYPE[file.type] ?? \"png\";\n    imageFiles.push(\n      new File(\n        [file],\n        `clipboard-image-${Date.now()}.${extension}`,\n        {\n          type: file.type,\n          lastModified: Date.now()\n        }\n      )\n    );\n  }\n\n  if (imageFiles.length > 0) {\n    return imageFiles;\n  }\n\n  for (const file of Array.from(clipboardData.files ?? [])) {\n    if (file.type.startsWith(\"image/\")) {\n      imageFiles.push(file);\n    }\n  }\n\n  return imageFiles;\n}\n\nfunction dataTransferHasFiles(\n  dataTransfer: DataTransfer | null\n): dataTransfer is DataTransfer {\n  if (!dataTransfer) return false;\n  const types = dataTransfer.types;\n  if (!types) return false;\n  // Real browsers return DOMStringList which has .contains(); test polyfills use plain arrays.\n  if (typeof (types as unknown as { contains?: unknown }).contains === \"function\") {\n    return (types as unknown as DOMStringList).contains(\"Files\");\n  }\n  return Array.from(types).includes(\"Files\");\n}\n\n// ============================================================================\n// PERSIST STATE HELPERS\n// ============================================================================\n\ntype NormalizedPersistConfig = {\n  storage: 'local' | 'session';\n  keyPrefix: string;\n  persist: {\n    openState: boolean;\n    voiceState: boolean;\n    focusInput: boolean;\n  };\n  clearOnChatClear: boolean;\n};\n\n/**\n * Normalize persistState config - handles both boolean and object forms\n */\nfunction normalizePersistStateConfig(\n  config: boolean | { storage?: 'local' | 'session'; keyPrefix?: string; persist?: { openState?: boolean; voiceState?: boolean; focusInput?: boolean }; clearOnChatClear?: boolean } | undefined\n): NormalizedPersistConfig | null {\n  if (!config) return null;\n  \n  if (config === true) {\n    // Use defaults\n    return {\n      storage: 'session',\n      keyPrefix: 'persona-',\n      persist: {\n        openState: true,\n        voiceState: true,\n        focusInput: true\n      },\n      clearOnChatClear: true\n    };\n  }\n  \n  // Object config - merge with defaults\n  return {\n    storage: config.storage ?? 'session',\n    keyPrefix: config.keyPrefix ?? 'persona-',\n    persist: {\n      openState: config.persist?.openState ?? true,\n      voiceState: config.persist?.voiceState ?? true,\n      focusInput: config.persist?.focusInput ?? true\n    },\n    clearOnChatClear: config.clearOnChatClear ?? true\n  };\n}\n\n/**\n * Get the storage object based on config\n */\nfunction getPersistStorage(storageType: 'local' | 'session'): Storage | null {\n  try {\n    const storage = storageType === 'local' ? localStorage : sessionStorage;\n    // Test that storage is actually available\n    const testKey = '__persist_test__';\n    storage.setItem(testKey, '1');\n    storage.removeItem(testKey);\n    return storage;\n  } catch {\n    return null;\n  }\n}\n\nconst ensureRecord = (value: unknown): Record<string, unknown> => {\n  if (!value || typeof value !== \"object\") {\n    return {};\n  }\n  return { ...(value as Record<string, unknown>) };\n};\n\nconst stripStreamingFromMessages = (messages: AgentWidgetMessage[]) =>\n  messages.map((message) => ({\n    ...message,\n    streaming: false\n  }));\n\ntype Controller = {\n  update: (config: AgentWidgetConfig) => void;\n  destroy: () => void;\n  open: () => void;\n  close: () => void;\n  toggle: () => void;\n  clearChat: () => void;\n  setMessage: (message: string) => boolean;\n  submitMessage: (message?: string) => boolean;\n  startVoiceRecognition: () => boolean;\n  stopVoiceRecognition: () => boolean;\n  /**\n   * Inject a message into the conversation with dual-content support.\n   * Auto-opens the widget if closed and launcher is enabled.\n   */\n  injectMessage: (options: InjectMessageOptions) => AgentWidgetMessage;\n  /**\n   * Convenience method for injecting assistant messages.\n   */\n  injectAssistantMessage: (options: InjectAssistantMessageOptions) => AgentWidgetMessage;\n  /**\n   * Convenience method for injecting user messages.\n   */\n  injectUserMessage: (options: InjectUserMessageOptions) => AgentWidgetMessage;\n  /**\n   * Convenience method for injecting system messages.\n   */\n  injectSystemMessage: (options: InjectSystemMessageOptions) => AgentWidgetMessage;\n  /**\n   * Inject multiple messages in a single batch with one sort and one render pass.\n   */\n  injectMessageBatch: (optionsList: InjectMessageOptions[]) => AgentWidgetMessage[];\n  /**\n   * Convenience method for injecting an assistant message that renders as a\n   * registered component: same shape Persona produces from a streamed\n   * `{ \"text\": \"...\", \"component\": \"...\", \"props\": {...} }` payload.\n   */\n  injectComponentDirective: (\n    options: InjectComponentDirectiveOptions\n  ) => AgentWidgetMessage;\n  /**\n   * @deprecated Use injectMessage() instead.\n   */\n  injectTestMessage: (event: AgentWidgetEvent) => void;\n  getMessages: () => AgentWidgetMessage[];\n  getStatus: () => AgentWidgetSessionStatus;\n  getPersistentMetadata: () => Record<string, unknown>;\n  updatePersistentMetadata: (\n    updater: (prev: Record<string, unknown>) => Record<string, unknown>\n  ) => void;\n  on: <K extends keyof AgentWidgetControllerEventMap>(\n    event: K,\n    handler: (payload: AgentWidgetControllerEventMap[K]) => void\n  ) => () => void;\n  off: <K extends keyof AgentWidgetControllerEventMap>(\n    event: K,\n    handler: (payload: AgentWidgetControllerEventMap[K]) => void\n  ) => void;\n  // State query methods\n  isOpen: () => boolean;\n  isVoiceActive: () => boolean;\n  // Read-aloud (text-to-speech) methods\n  toggleReadAloud: (messageId: string) => void;\n  stopReadAloud: () => void;\n  getReadAloudState: (messageId: string) => ReadAloudState;\n  onReadAloudChange: (\n    listener: (activeId: string | null, state: ReadAloudState) => void\n  ) => () => void;\n  getState: () => AgentWidgetStateSnapshot;\n  // Feedback methods (CSAT/NPS)\n  showCSATFeedback: (options?: Partial<CSATFeedbackOptions>) => void;\n  showNPSFeedback: (options?: Partial<NPSFeedbackOptions>) => void;\n  submitCSATFeedback: (rating: number, comment?: string) => Promise<void>;\n  submitNPSFeedback: (rating: number, comment?: string) => Promise<void>;\n  /**\n   * Connect an external SSE stream and process it through the SDK's\n   * native event pipeline (tools, reasoning, streaming text, etc.).\n   */\n  connectStream: (\n    stream: ReadableStream<Uint8Array>,\n    options?: { assistantMessageId?: string }\n  ) => Promise<void>;\n  /** Push a raw event into the event stream buffer (for testing/debugging) */\n  __pushEventStreamEvent: (event: { type: string; payload: unknown }) => void;\n  /** Opens the event stream panel */\n  showEventStream: () => void;\n  /** Closes the event stream panel */\n  hideEventStream: () => void;\n  /** Returns current visibility state of the event stream panel */\n  isEventStreamVisible: () => boolean;\n  /** Show artifact sidebar (no-op if features.artifacts.enabled is false) */\n  showArtifacts: () => void;\n  /** Hide artifact sidebar */\n  hideArtifacts: () => void;\n  /** Upsert an artifact programmatically */\n  upsertArtifact: (manual: PersonaArtifactManualUpsert) => PersonaArtifactRecord | null;\n  selectArtifact: (id: string) => void;\n  clearArtifacts: () => void;\n  /** Read current artifacts (useful on init to rebuild host-side tab state after hydration). */\n  getArtifacts: () => PersonaArtifactRecord[];\n  /** Read the currently selected artifact id (paired with `getArtifacts`). */\n  getSelectedArtifactId: () => string | null;\n  /**\n   * Focus the chat input. Returns true if focus succeeded, false if panel is closed\n   * (launcher mode) or textarea is unavailable.\n   */\n  focusInput: () => boolean;\n  /**\n   * Programmatically resolve a pending approval.\n   * @param approvalId - The approval ID to resolve\n   * @param decision - \"approved\" or \"denied\"\n   * @param options - Optional decision context (e.g. `{ remember: true }`),\n   *   forwarded to `config.approval.onDecision`.\n   */\n  resolveApproval: (\n    approvalId: string,\n    decision: 'approved' | 'denied',\n    options?: AgentWidgetApprovalDecisionOptions\n  ) => Promise<void>;\n};\n\nexport const buildPostprocessor = (\n  cfg: AgentWidgetConfig | undefined,\n  actionManager?: ReturnType<typeof createActionManager>,\n  onResubmitRequested?: () => void\n): MessageTransform => {\n  // Create markdown processor from config if markdown config is provided\n  // This allows users to enable markdown rendering via config.markdown\n  const markdownProcessor = cfg?.markdown\n    ? createMarkdownProcessorFromConfig(cfg.markdown)\n    : null;\n\n  // Resolve sanitizer: enabled by default, can be disabled or replaced\n  const sanitize = resolveSanitizer(cfg?.sanitize);\n\n  // Warn developers when a custom postprocessor is used with the default sanitizer,\n  // since DOMPurify will strip any tags/attributes not in the allowlist.\n  if (cfg?.postprocessMessage && sanitize && cfg?.sanitize === undefined) {\n    console.warn(\n      \"[Persona] A custom postprocessMessage is active with the default HTML sanitizer. \" +\n      \"Tags or attributes not in the built-in allowlist will be stripped. \" +\n      \"To keep custom HTML, set `sanitize: false` or provide a custom sanitize function.\"\n    );\n  }\n\n  return (context) => {\n    let nextText = context.text ?? \"\";\n    const rawPayload = context.message.rawContent ?? null;\n\n    if (actionManager) {\n      const actionResult = actionManager.process({\n        text: nextText,\n        raw: rawPayload ?? nextText,\n        message: context.message,\n        streaming: context.streaming\n      });\n      if (actionResult !== null) {\n        nextText = actionResult.text;\n        // Mark message as non-persistable if persist is false\n        if (!actionResult.persist) {\n          (context.message as any).__skipPersist = true;\n        }\n        // Request deferred resubmit if handler requested it (and message is complete)\n        // The actual resubmit will be triggered when injectAssistantMessage is called\n        if (actionResult.resubmit && !context.streaming && onResubmitRequested) {\n          onResubmitRequested();\n        }\n      }\n    }\n\n    // Priority: postprocessMessage > markdown config > escapeHtml.\n    //\n    // Degraded path (IIFE/CDN build before `markdown-parsers.js` resolves, or if\n    // it never loads): the markdown processor and the sanitizer BOTH fall back to\n    // escapeHtml, so the old `sanitize(markdownProcessor(text))` escaped twice and\n    // displayed entities literally (I'll -> &amp;#39;). Only run the sanitizer when\n    // it can actually parse HTML; escapeHtml output is already inert. Checked per\n    // render (not once at setup): the chunk lands later and the self-heal re-renders.\n    const parsersReady = getMarkdownParsersSync() !== null;\n    let html: string;\n    if (cfg?.postprocessMessage) {\n      const out = cfg.postprocessMessage({\n        ...context,\n        text: nextText,\n        raw: rawPayload ?? context.text ?? \"\"\n      });\n      // Custom HTML is NOT pre-escaped, so this stays a single pass even via the\n      // sanitizer's degraded fallback. Honors `sanitize: false` (pass-through) as before.\n      html = sanitize ? sanitize(out) : out;\n    } else if (markdownProcessor) {\n      // While streaming, normalize tables-in-progress so they render as a real\n      // <table> from the first row with a stable column count (Telegram-style\n      // space reservation). The final, non-streaming render is left untouched.\n      const source = context.streaming ? stabilizeStreamingTables(nextText) : nextText;\n      // Already escapeHtml(text) (single, safe) while parsers are not loaded.\n      const out = markdownProcessor(source);\n      html = sanitize && parsersReady ? sanitize(out) : out;\n    } else {\n      // Plain text: escapeHtml output is inert — never re-sanitize (the second escape).\n      html = escapeHtml(nextText);\n    }\n\n    return html;\n  };\n};\n\nfunction buildDropOverlay(\n  dropCfg?: NonNullable<AgentWidgetConfig[\"attachments\"]>[\"dropOverlay\"]\n): HTMLElement {\n  const overlay = createElement(\"div\", \"persona-attachment-drop-overlay\");\n  if (dropCfg?.background) overlay.style.setProperty(\"--persona-drop-overlay-bg\", dropCfg.background);\n  if (dropCfg?.backdropBlur !== undefined) overlay.style.setProperty(\"--persona-drop-overlay-blur\", dropCfg.backdropBlur);\n  if (dropCfg?.border) overlay.style.setProperty(\"--persona-drop-overlay-border\", dropCfg.border);\n  if (dropCfg?.borderRadius) overlay.style.setProperty(\"--persona-drop-overlay-radius\", dropCfg.borderRadius);\n  if (dropCfg?.inset) overlay.style.setProperty(\"--persona-drop-overlay-inset\", dropCfg.inset);\n  if (dropCfg?.labelSize) overlay.style.setProperty(\"--persona-drop-overlay-label-size\", dropCfg.labelSize);\n  if (dropCfg?.labelColor) overlay.style.setProperty(\"--persona-drop-overlay-label-color\", dropCfg.labelColor);\n\n  const iconName = dropCfg?.iconName ?? \"upload\";\n  const iconSize = dropCfg?.iconSize ?? \"48px\";\n  const iconColor = dropCfg?.iconColor ?? \"rgba(59, 130, 246, 0.6)\";\n  const iconStrokeWidth = dropCfg?.iconStrokeWidth ?? 0.5;\n  const iconSvg = renderLucideIcon(iconName, iconSize, iconColor, iconStrokeWidth);\n  if (iconSvg) overlay.appendChild(iconSvg);\n\n  if (dropCfg?.label) {\n    const labelEl = createElement(\"span\", \"persona-drop-overlay-label\");\n    labelEl.textContent = dropCfg.label;\n    overlay.appendChild(labelEl);\n  }\n  return overlay;\n}\n\nexport const createAgentExperience = (\n  mount: HTMLElement,\n  initialConfig?: AgentWidgetConfig,\n  runtimeOptions?: { debugTools?: boolean }\n): Controller => {\n  if (mount == null) {\n    throw new Error(\n      \"createAgentExperience: mount must be a non-null HTMLElement (e.g. pass document.getElementById(\\\"my-root\\\") after the node exists).\"\n    );\n  }\n  // Preserve original mount id as data attribute for window event instance scoping\n  if (mount.id && !mount.getAttribute(\"data-persona-instance\")) {\n    mount.setAttribute(\"data-persona-instance\", mount.id);\n  }\n  // Ensure root marker is present for Tailwind scoping and DOM traversal\n  if (!mount.hasAttribute(\"data-persona-root\")) {\n    mount.setAttribute(\"data-persona-root\", \"true\");\n  }\n\n  let config = mergeWithDefaults(initialConfig) as AgentWidgetConfig;\n  // Note: applyThemeVariables is called after applyFullHeightStyles() below\n  // because applyFullHeightStyles resets mount.style.cssText\n\n  // Get plugins for this instance\n  const plugins = pluginRegistry.getForInstance(config.plugins);\n\n  // The built-in approval renderer, shaped as a plugin. Resolved as a FALLBACK\n  // (not pushed into `plugins`) so a user `renderApproval` plugin always wins\n  // and a later config-update plugin push can't reorder ahead of it.\n  const { plugin: builtInApprovalPlugin, teardown: teardownBuiltInApprovals } =\n    createBuiltInApprovalPlugin();\n\n  // Register components from config\n  if (config.components) {\n    componentRegistry.registerAll(config.components);\n  }\n  const eventBus = createEventBus<AgentWidgetControllerEventMap>();\n\n  // When persistState is explicitly false, message-history persistence is\n  // disabled: including any user-supplied storageAdapter. This is the strict\n  // kill-switch semantic; pass `persistState: true` (or omit it) to opt in.\n  const messagePersistenceDisabled = config.persistState === false;\n  const storageAdapter: AgentWidgetStorageAdapter | null =\n    messagePersistenceDisabled\n      ? null\n      : (config.storageAdapter ?? createLocalStorageAdapter());\n  let persistentMetadata: Record<string, unknown> = {};\n  let pendingStoredState: Promise<AgentWidgetStoredState | null> | null = null;\n\n  let shouldOpenAfterStateLoaded = false;\n\n  // Helper to apply onStateLoaded hook and extract state.\n  // Supports both the legacy plain-state return and the new { state, open? } return.\n  const applyStateLoadedHook = (state: AgentWidgetStoredState): AgentWidgetStoredState => {\n    if (config.onStateLoaded) {\n      try {\n        const result = config.onStateLoaded(state);\n        if (result && typeof result === 'object' && 'state' in result) {\n          const { state: processedState, open } = result as { state: AgentWidgetStoredState; open?: boolean };\n          if (open) shouldOpenAfterStateLoaded = true;\n          return processedState;\n        }\n        return result as AgentWidgetStoredState;\n      } catch (error) {\n        if (typeof console !== \"undefined\") {\n          // eslint-disable-next-line no-console\n          console.error(\"[AgentWidget] onStateLoaded hook failed:\", error);\n        }\n      }\n    }\n    return state;\n  };\n\n  if (storageAdapter?.load) {\n    try {\n      const storedState = storageAdapter.load();\n      if (storedState && typeof (storedState as Promise<any>).then === \"function\") {\n        // For async storage, apply hook when promise resolves\n        pendingStoredState = (storedState as Promise<AgentWidgetStoredState | null>).then(\n          (resolved) => {\n            const state = resolved ?? { messages: [], metadata: {} };\n            return applyStateLoadedHook(state);\n          }\n        );\n      } else {\n        // Apply hook to synchronously loaded state (or empty state if nothing stored)\n        const baseState = (storedState as AgentWidgetStoredState) ?? { messages: [], metadata: {} };\n        const processedState = applyStateLoadedHook(baseState);\n        if (processedState.metadata) {\n          persistentMetadata = ensureRecord(processedState.metadata);\n        }\n        if (processedState.messages?.length) {\n          config = { ...config, initialMessages: processedState.messages };\n        }\n        if (processedState.artifacts?.length) {\n          config = {\n            ...config,\n            initialArtifacts: processedState.artifacts,\n            initialSelectedArtifactId: processedState.selectedArtifactId ?? null\n          };\n        }\n      }\n    } catch (error) {\n      if (typeof console !== \"undefined\") {\n        // eslint-disable-next-line no-console\n        console.error(\"[AgentWidget] Failed to load stored state:\", error);\n      }\n    }\n  } else if (config.onStateLoaded) {\n    // No storage adapter but hook exists - call with empty state\n    try {\n      const processedState = applyStateLoadedHook({ messages: [], metadata: {} });\n      if (processedState.messages?.length) {\n        config = { ...config, initialMessages: processedState.messages };\n      }\n    } catch (error) {\n      if (typeof console !== \"undefined\") {\n        // eslint-disable-next-line no-console\n        console.error(\"[AgentWidget] onStateLoaded hook failed:\", error);\n      }\n    }\n  }\n\n  const getSessionMetadata = () => persistentMetadata;\n  const updateSessionMetadata = (\n    updater: (prev: Record<string, unknown>) => Record<string, unknown>\n  ) => {\n    const next = updater({ ...persistentMetadata }) ?? {};\n    persistentMetadata = next;\n    persistState();\n  };\n\n  const resolvedActionParsers =\n    config.actionParsers && config.actionParsers.length\n      ? config.actionParsers\n      : [defaultJsonActionParser];\n\n  const resolvedActionHandlers =\n    config.actionHandlers && config.actionHandlers.length\n      ? config.actionHandlers\n      : [defaultActionHandlers.message, defaultActionHandlers.messageAndClick];\n\n  let actionManager = createActionManager({\n    parsers: resolvedActionParsers,\n    handlers: resolvedActionHandlers,\n    getSessionMetadata,\n    updateSessionMetadata,\n    emit: eventBus.emit,\n    documentRef: typeof document !== \"undefined\" ? document : null\n  });\n  actionManager.syncFromMetadata();\n\n  let launcherEnabled = config.launcher?.enabled ?? true;\n  let autoExpand = config.launcher?.autoExpand ?? false;\n  const autoFocusInput = config.autoFocusInput ?? false;\n  let prevAutoExpand = autoExpand;\n  let prevLauncherEnabled = launcherEnabled;\n  let prevHeaderLayout = config.layout?.header?.layout;\n  let wasMobileFullscreen = false;\n  // Composer-bar mode behaves like a launcher-enabled panel for state/toggle\n  // purposes (open/close maps to expand/collapse) but does not render a\n  // launcher button. `isPanelToggleable()` covers both modes; checks that\n  // gate the launcher button itself stay on the raw `launcherEnabled` flag.\n  const isComposerBar = () => isComposerBarMountMode(config);\n  const isPanelToggleable = () => launcherEnabled || isComposerBar();\n  // Composer-bar starts collapsed (open=false). Inline embed (no launcher)\n  // is always open. Launcher mode honors `autoExpand`.\n  let open = isComposerBar() ? false : (launcherEnabled ? autoExpand : true);\n\n  // Track pending resubmit state for injection-triggered resubmit\n  // When a handler returns resubmit: true, we wait for injectAssistantMessage()\n  // to be called before triggering the actual resubmit (to avoid race conditions)\n  let pendingResubmit = false;\n  let pendingResubmitTimeout: ReturnType<typeof setTimeout> | null = null;\n\n  const handleResubmitRequested = () => {\n    pendingResubmit = true;\n    // Clear any existing timeout\n    if (pendingResubmitTimeout) {\n      clearTimeout(pendingResubmitTimeout);\n    }\n    // Safety timeout - clear flag after 10s if no injection occurs\n    pendingResubmitTimeout = setTimeout(() => {\n      if (pendingResubmit) {\n        if (typeof console !== \"undefined\") {\n          // eslint-disable-next-line no-console\n          console.warn(\"[AgentWidget] Resubmit requested but no injection occurred within 10s\");\n        }\n        pendingResubmit = false;\n      }\n    }, 10000);\n  };\n\n  let postprocess = buildPostprocessor(config, actionManager, handleResubmitRequested);\n  let showReasoning = config.features?.showReasoning ?? true;\n  let showToolCalls = config.features?.showToolCalls ?? true;\n  let showEventStreamToggle = config.features?.showEventStreamToggle ?? false;\n  let scrollToBottomFeature = config.features?.scrollToBottom ?? {};\n  let scrollBehaviorFeature = config.features?.scrollBehavior ?? {};\n  const persistKeyPrefix = (typeof config.persistState === 'object' ? config.persistState?.keyPrefix : undefined) ?? \"persona-\";\n  const eventStreamDbName = `${persistKeyPrefix}event-stream`;\n  let eventStreamStore = showEventStreamToggle ? new EventStreamStore(eventStreamDbName) : null;\n  const eventStreamMaxEvents = config.features?.eventStream?.maxEvents ?? 2000;\n  let eventStreamBuffer = showEventStreamToggle ? new EventStreamBuffer(eventStreamMaxEvents, eventStreamStore) : null;\n  // Passive output-throughput tracker, fed from the same SSE tap as the buffer.\n  let throughputTracker = showEventStreamToggle ? new ThroughputTracker() : null;\n  let eventStreamView: ReturnType<typeof createEventStreamView> | null = null;\n  let eventStreamVisible = false;\n  let eventStreamRAF: number | null = null;\n  let eventStreamLastUpdate = 0;\n\n  // Open IndexedDB store and restore persisted events into the buffer\n  eventStreamStore?.open().then(() => {\n    return eventStreamBuffer?.restore();\n  }).catch(err => {\n    if (config.debug) console.warn('[AgentWidget] IndexedDB not available for event stream:', err);\n  });\n\n  // Create message action callbacks that emit events and optionally send to API\n  const messageActionCallbacks: MessageActionCallbacks = {\n    onCopy: (message: AgentWidgetMessage) => {\n      eventBus.emit(\"message:copy\", message);\n      // Send copy feedback to API if in client token mode\n      if (session?.isClientTokenMode()) {\n        session.submitMessageFeedback(message.id, 'copy').catch((error) => {\n          if (config.debug) {\n            // eslint-disable-next-line no-console\n            console.error(\"[AgentWidget] Failed to submit copy feedback:\", error);\n          }\n        });\n      }\n      // Call user-provided callback\n      config.messageActions?.onCopy?.(message);\n    },\n    onFeedback: (feedback: AgentWidgetMessageFeedback) => {\n      eventBus.emit(\"message:feedback\", feedback);\n      // Send feedback to API if in client token mode\n      if (session?.isClientTokenMode()) {\n        session.submitMessageFeedback(feedback.messageId, feedback.type).catch((error) => {\n          if (config.debug) {\n            // eslint-disable-next-line no-console\n            console.error(\"[AgentWidget] Failed to submit feedback:\", error);\n          }\n        });\n      }\n      // Call user-provided callback\n      config.messageActions?.onFeedback?.(feedback);\n    }\n  };\n  \n  // Get status indicator config\n  const statusConfig = config.statusIndicator ?? {};\n  const _getStatusText = (status: AgentWidgetSessionStatus): string => {\n    if (status === \"idle\") return statusConfig.idleText ?? statusCopy.idle;\n    if (status === \"connecting\") return statusConfig.connectingText ?? statusCopy.connecting;\n    if (status === \"connected\") return statusConfig.connectedText ?? statusCopy.connected;\n    if (status === \"error\") return statusConfig.errorText ?? statusCopy.error;\n    return statusCopy[status];\n  };\n\n  /** Update statusText element, rendering a link for idle status when idleLink is configured. */\n  function applyStatusToElement(el: HTMLElement, text: string, statusCfg: typeof statusConfig, status: string): void {\n    if (status === \"idle\" && statusCfg.idleLink) {\n      el.textContent = \"\";\n      const link = document.createElement(\"a\");\n      link.href = statusCfg.idleLink;\n      link.target = \"_blank\";\n      link.rel = \"noopener noreferrer\";\n      link.textContent = text;\n      link.style.color = \"inherit\";\n      link.style.textDecoration = \"none\";\n      el.appendChild(link);\n    } else {\n      el.textContent = text;\n    }\n  }\n\n  // The view layer (`components/widget-view.ts`) owns the one-time structural\n  // assembly (shell + panel) and groups the resulting refs into named regions.\n  // Behavior stays here in ui.ts; the locals below mirror the grouped refs so\n  // the surrounding orchestration code keeps its existing variable names.\n  const view = createWidgetView({ config, showClose: isPanelToggleable() });\n  const { wrapper, panel, pillRoot } = view.shell;\n  const panelElements = view.panelElements;\n  let {\n    container,\n    body,\n    messagesWrapper,\n    suggestions,\n    textarea,\n    sendButton,\n    sendButtonWrapper,\n    composerForm,\n    statusText,\n    introTitle,\n    introSubtitle,\n    closeButton,\n    iconHolder,\n    headerTitle,\n    headerSubtitle,\n    header,\n    footer,\n    actionsRow: _actionsRow,\n    leftActions,\n    rightActions\n  } = panelElements;\n  let setSendButtonMode = panelElements.setSendButtonMode;\n\n  // Use mutable references for mic button so we can update them dynamically\n  let micButton: HTMLButtonElement | null = panelElements.micButton;\n  let micButtonWrapper: HTMLElement | null = panelElements.micButtonWrapper;\n\n  // Use mutable references for attachment elements so we can create them dynamically\n  let attachmentButton: HTMLButtonElement | null = panelElements.attachmentButton;\n  let attachmentButtonWrapper: HTMLElement | null = panelElements.attachmentButtonWrapper;\n  let attachmentInput: HTMLInputElement | null = panelElements.attachmentInput;\n  let attachmentPreviewsContainer: HTMLElement | null = panelElements.attachmentPreviewsContainer;\n  container.classList.add(\"persona-relative\");\n  body.classList.add(\"persona-relative\");\n  const SCROLL_TO_BOTTOM_EDGE_OFFSET = 12;\n\n  const getScrollToBottomLabel = () => scrollToBottomFeature.label ?? \"\";\n  const getScrollToBottomIconName = () => scrollToBottomFeature.iconName ?? \"arrow-down\";\n  const isScrollToBottomEnabled = () => scrollToBottomFeature.enabled !== false;\n  // Default is \"anchor-top\" (see DEFAULT_WIDGET_CONFIG). This `??` only applies\n  // when a partial config sets `scrollBehavior.mode` to undefined explicitly;\n  // it must agree with the declared default.\n  const getScrollMode = () => scrollBehaviorFeature.mode ?? \"anchor-top\";\n  // \"Effectively following the bottom\" for streaming auto-scroll: true in\n  // follow mode, and in anchor-top when the current turn has no anchor (the\n  // no-anchor fallback). Drives `scheduleAutoScroll`, `handleContentResize`,\n  // and `isAwayFromLatest` so a no-anchor anchor-top turn behaves like follow.\n  const isFollowEffective = () =>\n    getScrollMode() === \"follow\" ||\n    (getScrollMode() === \"anchor-top\" && followFallbackActive);\n  const getAnchorTopOffset = () => scrollBehaviorFeature.anchorTopOffset ?? 16;\n  const getScrollRestorePosition = () =>\n    scrollBehaviorFeature.restorePosition ?? \"bottom\";\n  const isPauseOnInteractionEnabled = () =>\n    scrollBehaviorFeature.pauseOnInteraction === true;\n  // Defaults on alongside the anchor-top default so the pinned-turn UX keeps the\n  // unread count + \"streaming below\" hint; opt out with `false`.\n  const isActivityWhilePinnedEnabled = () =>\n    scrollBehaviorFeature.showActivityWhilePinned !== false;\n  const isAnnounceEnabled = () => scrollBehaviorFeature.announce === true;\n  const scrollToBottomButton = createElement(\n    \"button\",\n    \"persona-scroll-to-bottom-indicator persona-absolute persona-bottom-3 persona-left-1/2 persona-z-10 persona-flex persona-items-center persona-gap-1 persona-text-xs persona-transform persona--translate-x-1/2 persona-cursor-pointer\"\n  ) as HTMLButtonElement;\n  scrollToBottomButton.type = \"button\";\n  scrollToBottomButton.style.display = \"none\";\n  scrollToBottomButton.setAttribute(\"data-persona-scroll-to-bottom\", \"true\");\n  const scrollToBottomIcon = createElement(\"span\", \"persona-flex persona-items-center\");\n  const scrollToBottomLabel = createElement(\"span\", \"\");\n  // Count of messages that arrived while auto-follow was paused (or, in\n  // non-follow scroll modes, while the user was away from the bottom).\n  // Rendered as a small badge on the scroll-to-bottom affordance, mirroring\n  // the event stream view's \"Jump to latest (N)\" indicator.\n  const scrollToBottomCount = createElement(\"span\", \"\");\n  scrollToBottomCount.setAttribute(\"data-persona-scroll-to-bottom-count\", \"\");\n  scrollToBottomCount.style.display = \"none\";\n  scrollToBottomButton.append(scrollToBottomIcon, scrollToBottomLabel, scrollToBottomCount);\n  container.appendChild(scrollToBottomButton);\n\n  // Anchor-top scroll mode: zero-height spacer kept after the messages\n  // wrapper. Sized on send so the just-sent user message can be scrolled to\n  // the top of the viewport before the streamed response is tall enough to\n  // make that position reachable; shrinks as real content fills the space.\n  const anchorSpacer = createElement(\"div\", \"persona-stream-anchor-spacer\");\n  anchorSpacer.setAttribute(\"aria-hidden\", \"true\");\n  anchorSpacer.setAttribute(\"data-persona-anchor-spacer\", \"\");\n  anchorSpacer.style.flexShrink = \"0\";\n  anchorSpacer.style.pointerEvents = \"none\";\n  anchorSpacer.style.height = \"0px\";\n  body.appendChild(anchorSpacer);\n\n  // Visually-hidden polite live region for screen-reader announcements\n  // (Principle 15: announce important events at a comfortable pace, never\n  // token-by-token). Created unconditionally but only written to when\n  // `features.scrollBehavior.announce` is opted in.\n  const liveRegion = createElement(\"div\", \"persona-sr-only\");\n  liveRegion.setAttribute(\"aria-live\", \"polite\");\n  liveRegion.setAttribute(\"aria-atomic\", \"true\");\n  liveRegion.setAttribute(\"role\", \"status\");\n  liveRegion.setAttribute(\"data-persona-live-region\", \"\");\n  Object.assign(liveRegion.style, {\n    position: \"absolute\",\n    width: \"1px\",\n    height: \"1px\",\n    margin: \"-1px\",\n    padding: \"0\",\n    overflow: \"hidden\",\n    clip: \"rect(0 0 0 0)\",\n    clipPath: \"inset(50%)\",\n    whiteSpace: \"nowrap\",\n    border: \"0\",\n  } satisfies Partial<CSSStyleDeclaration>);\n  container.appendChild(liveRegion);\n  // Debounce announcements so a fast event sequence (e.g. several messages\n  // landing while away) collapses into one calm spoken update.\n  let announceTimer: ReturnType<typeof setTimeout> | null = null;\n  let pendingAnnouncement: string | null = null;\n  const announce = (message: string) => {\n    if (!isAnnounceEnabled() || !message) return;\n    pendingAnnouncement = message;\n    if (announceTimer !== null) return;\n    announceTimer = setTimeout(() => {\n      announceTimer = null;\n      // Re-check: `update()` may have disabled `announce` within the debounce\n      // window, and a stale message must not slip through to the live region.\n      if (pendingAnnouncement && isAnnounceEnabled()) {\n        liveRegion.textContent = pendingAnnouncement;\n      }\n      pendingAnnouncement = null;\n    }, 400);\n  };\n\n  const updateScrollToBottomButtonOffset = () => {\n    const footerHidden = footer.style.display === \"none\";\n    const footerHeight = footerHidden ? 0 : footer.offsetHeight;\n    scrollToBottomButton.style.bottom = `${footerHeight + SCROLL_TO_BOTTOM_EDGE_OFFSET}px`;\n  };\n  updateScrollToBottomButtonOffset();\n\n  const renderScrollToBottomButton = () => {\n    const hasLabel = Boolean(getScrollToBottomLabel());\n    scrollToBottomButton.setAttribute(\"aria-label\", getScrollToBottomLabel() || \"Jump to latest\");\n    scrollToBottomButton.title = getScrollToBottomLabel();\n    scrollToBottomButton.setAttribute(\"data-persona-scroll-to-bottom-has-label\", hasLabel ? \"true\" : \"false\");\n    scrollToBottomIcon.innerHTML = \"\";\n    const icon = renderLucideIcon(getScrollToBottomIconName(), \"14px\", \"currentColor\", 2);\n    if (icon) {\n      scrollToBottomIcon.appendChild(icon);\n      scrollToBottomIcon.style.display = \"\";\n    } else {\n      scrollToBottomIcon.style.display = \"none\";\n    }\n    scrollToBottomLabel.textContent = getScrollToBottomLabel();\n    scrollToBottomLabel.style.display = hasLabel ? \"\" : \"none\";\n  };\n  renderScrollToBottomButton();\n\n  // Initialized after composer plugins rebind footer DOM (see `bindComposerRefsFromFooter`)\n  let attachmentManager: AttachmentManager | null = null;\n\n  /** Wired after `handleMicButtonClick` is defined; used by `renderComposer` `onVoiceToggle`. */\n  let composerVoiceBridge: (() => void) | null = null;\n\n  // Plugin hook: renderHeader - allow plugins to provide custom header\n  const headerPlugin = plugins.find(p => p.renderHeader);\n  if (headerPlugin?.renderHeader) {\n    const customHeader = headerPlugin.renderHeader({\n      config,\n      defaultRenderer: () => {\n        const headerElements = buildHeader({ config, showClose: isPanelToggleable() });\n        attachHeaderToContainer(container, headerElements, config);\n        return headerElements.header;\n      },\n      onClose: () => setOpenState(false, \"user\")\n    });\n    if (customHeader) {\n      // Replace the default header with custom header\n      const existingHeader = container.querySelector('.persona-border-b-persona-divider');\n      if (existingHeader) {\n        existingHeader.replaceWith(customHeader);\n        header = customHeader;\n        // Keep the view's tracked header element in sync so a later\n        // header-layout rebuild (view.replaceHeader) targets the mounted node.\n        view.header.element = customHeader;\n      }\n    }\n  }\n\n  // Event stream toggle functions (lifted to outer scope for controller access)\n  const toggleEventStreamOn = () => {\n    if (!eventStreamBuffer) return;\n    eventStreamVisible = true;\n    if (!eventStreamView && eventStreamBuffer) {\n      eventStreamView = createEventStreamView({\n        buffer: eventStreamBuffer,\n        getFullHistory: () => eventStreamBuffer!.getAllFromStore(),\n        onClose: () => toggleEventStreamOff(),\n        config,\n        plugins,\n        getThroughput: () =>\n          throughputTracker?.getMetric() ?? { status: \"idle\" },\n      });\n    }\n    if (eventStreamView) {\n      body.style.display = \"none\";\n      footer.parentNode?.insertBefore(eventStreamView.element, footer);\n      eventStreamView.update();\n    }\n    if (eventStreamToggleBtn) {\n      eventStreamToggleBtn.style.boxShadow = `inset 0 0 0 1.5px ${HEADER_THEME_CSS.actionIconColor}`;\n      const activeClasses = config.features?.eventStream?.classNames?.toggleButtonActive;\n      if (activeClasses) activeClasses.split(/\\s+/).forEach(c => c && eventStreamToggleBtn!.classList.add(c));\n    }\n    // Start RAF-based update loop (throttled to ~200ms)\n    const rafLoop = () => {\n      if (!eventStreamVisible) return;\n      const now = Date.now();\n      if (now - eventStreamLastUpdate >= 200) {\n        eventStreamView?.update();\n        eventStreamLastUpdate = now;\n      }\n      eventStreamRAF = requestAnimationFrame(rafLoop);\n    };\n    eventStreamLastUpdate = 0;\n    eventStreamRAF = requestAnimationFrame(rafLoop);\n    syncScrollToBottomButton();\n    eventBus.emit(\"eventStream:opened\", { timestamp: Date.now() });\n  };\n\n  const toggleEventStreamOff = () => {\n    if (!eventStreamVisible) return;\n    eventStreamVisible = false;\n    if (eventStreamView) {\n      eventStreamView.element.remove();\n    }\n    body.style.display = \"\";\n    if (eventStreamToggleBtn) {\n      eventStreamToggleBtn.style.boxShadow = \"\";\n      const activeClasses = config.features?.eventStream?.classNames?.toggleButtonActive;\n      if (activeClasses) activeClasses.split(/\\s+/).forEach(c => c && eventStreamToggleBtn!.classList.remove(c));\n    }\n    // Cancel RAF update loop\n    if (eventStreamRAF !== null) {\n      cancelAnimationFrame(eventStreamRAF);\n      eventStreamRAF = null;\n    }\n    syncScrollToBottomButton();\n    eventBus.emit(\"eventStream:closed\", { timestamp: Date.now() });\n  };\n\n  // Event stream toggle button\n  let eventStreamToggleBtn: HTMLButtonElement | null = null;\n  if (showEventStreamToggle) {\n    const esClassNames = config.features?.eventStream?.classNames;\n    const toggleBtnClasses = \"persona-inline-flex persona-items-center persona-justify-center persona-rounded-full hover:persona-opacity-80 persona-cursor-pointer persona-border-none persona-bg-transparent persona-p-1\" + (esClassNames?.toggleButton ? \" \" + esClassNames.toggleButton : \"\");\n    eventStreamToggleBtn = createElement(\"button\", toggleBtnClasses) as HTMLButtonElement;\n    eventStreamToggleBtn.style.width = \"28px\";\n    eventStreamToggleBtn.style.height = \"28px\";\n    eventStreamToggleBtn.style.color = HEADER_THEME_CSS.actionIconColor;\n    eventStreamToggleBtn.type = \"button\";\n    eventStreamToggleBtn.setAttribute(\"aria-label\", \"Event Stream\");\n    eventStreamToggleBtn.title = \"Event Stream\";\n    const activityIcon = renderLucideIcon(\"activity\", \"18px\", \"currentColor\", 1.5);\n    if (activityIcon) eventStreamToggleBtn.appendChild(activityIcon);\n\n    // Insert before clear chat button wrapper or close button wrapper\n    const clearChatWrapper = panelElements.clearChatButtonWrapper;\n    const closeWrapper = panelElements.closeButtonWrapper;\n    const insertBefore = clearChatWrapper || closeWrapper;\n    if (insertBefore && insertBefore.parentNode === header) {\n      header.insertBefore(eventStreamToggleBtn, insertBefore);\n    } else {\n      header.appendChild(eventStreamToggleBtn);\n    }\n\n    eventStreamToggleBtn.addEventListener(\"click\", () => {\n      if (eventStreamVisible) {\n        toggleEventStreamOff();\n      } else {\n        toggleEventStreamOn();\n      }\n    });\n  }\n\n  const ensureComposerAttachmentSurface = (rootFooter: HTMLElement) => {\n    const att = config.attachments;\n    if (!att?.enabled) return;\n    let previews =\n      rootFooter.querySelector<HTMLElement>(\"[data-persona-composer-attachment-previews]\") ??\n      rootFooter.querySelector<HTMLElement>(\".persona-attachment-previews\");\n    if (!previews) {\n      previews = createElement(\n        \"div\",\n        \"persona-attachment-previews persona-flex persona-flex-wrap persona-gap-2 persona-mb-2\"\n      );\n      previews.setAttribute(\"data-persona-composer-attachment-previews\", \"\");\n      previews.style.display = \"none\";\n      const form = rootFooter.querySelector(\"[data-persona-composer-form]\");\n      if (form?.parentNode) {\n        form.parentNode.insertBefore(previews, form);\n      } else {\n        rootFooter.insertBefore(previews, rootFooter.firstChild);\n      }\n    }\n    const hasFileInput =\n      rootFooter.querySelector<HTMLInputElement>(\"[data-persona-composer-attachment-input]\") ??\n      rootFooter.querySelector<HTMLInputElement>('input[type=\"file\"]');\n    if (!hasFileInput) {\n      const fileIn = createElement(\"input\") as HTMLInputElement;\n      fileIn.type = \"file\";\n      fileIn.setAttribute(\"data-persona-composer-attachment-input\", \"\");\n      fileIn.accept = (att.allowedTypes ?? ALL_SUPPORTED_MIME_TYPES).join(\",\");\n      fileIn.multiple = (att.maxFiles ?? 4) > 1;\n      fileIn.style.display = \"none\";\n      fileIn.setAttribute(\"aria-label\", att.buttonTooltipText ?? \"Attach files\");\n      rootFooter.appendChild(fileIn);\n    }\n  };\n\n  // Plugin hook: renderComposer - allow plugins to provide custom composer\n  const composerPlugin = plugins.find(p => p.renderComposer);\n  if (composerPlugin?.renderComposer) {\n    const composerCfg = config.composer;\n    const customComposer = composerPlugin.renderComposer({\n      config,\n      defaultRenderer: () => {\n        const composerElements = buildComposer({ config });\n        return composerElements.footer;\n      },\n      onSubmit: (text: string) => {\n        if (!session || session.isStreaming()) return;\n        const value = text.trim();\n        const hasAttachments = attachmentManager?.hasAttachments() ?? false;\n        if (!value && !hasAttachments) return;\n        // Mirror the default composer's auto-expand behavior so plugin\n        // composers do not silently submit while the panel stays collapsed.\n        maybeExpandComposerBar();\n        let contentParts: ContentPart[] | undefined;\n        if (hasAttachments) {\n          contentParts = [];\n          contentParts.push(...attachmentManager!.getContentParts());\n          if (value) {\n            contentParts.push(createTextPart(value));\n          }\n        }\n        session.sendMessage(value, { contentParts });\n        if (hasAttachments) {\n          attachmentManager!.clearAttachments();\n        }\n      },\n      streaming: false,\n      disabled: false,\n      openAttachmentPicker: () => {\n        attachmentInput?.click();\n      },\n      models: composerCfg?.models,\n      selectedModelId: composerCfg?.selectedModelId,\n      onModelChange: (modelId: string) => {\n        config.composer = { ...config.composer, selectedModelId: modelId };\n        // Sync to agent config so the next request uses the selected model\n        if (config.agent) {\n          config.agent = { ...config.agent, model: modelId };\n        }\n      },\n      onVoiceToggle:\n        config.voiceRecognition?.enabled === true\n          ? () => {\n              composerVoiceBridge?.();\n            }\n          : undefined\n    });\n    if (customComposer) {\n      // Replace the default footer with custom composer (keeps view.composer.footer in sync).\n      view.replaceComposer(customComposer);\n      footer = view.composer.footer;\n    }\n  }\n\n  const bindComposerRefsFromFooter = (rootFooter: HTMLElement) => {\n    // Prefer stable `data-persona-composer-*` refs (set by the default and\n    // pill builders); fall back to the legacy class selectors so custom\n    // plugin composers built before these refs existed still bind.\n    const pick = <T extends HTMLElement>(...selectors: string[]): T | null => {\n      for (const selector of selectors) {\n        const found = rootFooter.querySelector<T>(selector);\n        if (found) return found;\n      }\n      return null;\n    };\n\n    const form = rootFooter.querySelector<HTMLFormElement>(\"[data-persona-composer-form]\");\n    const ta = rootFooter.querySelector<HTMLTextAreaElement>(\"[data-persona-composer-input]\");\n    const sb = rootFooter.querySelector<HTMLButtonElement>(\"[data-persona-composer-submit]\");\n    const mic = rootFooter.querySelector<HTMLButtonElement>(\"[data-persona-composer-mic]\");\n    const st = rootFooter.querySelector<HTMLElement>(\"[data-persona-composer-status]\");\n    if (form) composerForm = form;\n    if (ta) textarea = ta;\n    if (sb) sendButton = sb;\n    if (mic) {\n      micButton = mic;\n      micButtonWrapper = mic.parentElement as HTMLElement | null;\n    }\n    if (st) statusText = st;\n    const sug = pick<HTMLElement>(\n      \"[data-persona-composer-suggestions]\",\n      \".persona-mb-3.persona-flex.persona-flex-wrap.persona-gap-2\"\n    );\n    if (sug) suggestions = sug;\n    const attBtn = pick<HTMLButtonElement>(\n      \"[data-persona-composer-attachment-button]\",\n      \".persona-attachment-button\"\n    );\n    if (attBtn) {\n      attachmentButton = attBtn;\n      attachmentButtonWrapper = attBtn.parentElement as HTMLElement | null;\n    }\n    attachmentInput = pick<HTMLInputElement>(\n      \"[data-persona-composer-attachment-input]\",\n      'input[type=\"file\"]'\n    );\n    attachmentPreviewsContainer = pick<HTMLElement>(\n      \"[data-persona-composer-attachment-previews]\",\n      \".persona-attachment-previews\"\n    );\n    const ar = pick<HTMLElement>(\n      \"[data-persona-composer-actions]\",\n      \".persona-widget-composer .persona-flex.persona-items-center.persona-justify-between\"\n    );\n    if (ar) _actionsRow = ar;\n  };\n  ensureComposerAttachmentSurface(footer);\n  bindComposerRefsFromFooter(footer);\n\n  // Apply contentMaxWidth to composer form, suggestions, and attachment\n  // previews if configured. In composer-bar mode, fall back to\n  // `composerBar.contentMaxWidth` (default `720px`) when no explicit\n  // `layout.contentMaxWidth` is set, so the expanded panel's content\n  // centers horizontally without the host having to wire it up.\n  const contentMaxWidth =\n    config.layout?.contentMaxWidth ??\n    (isComposerBar() ? config.launcher?.composerBar?.contentMaxWidth ?? \"720px\" : undefined);\n  if (contentMaxWidth) {\n    messagesWrapper.style.maxWidth = contentMaxWidth;\n    messagesWrapper.style.marginLeft = \"auto\";\n    messagesWrapper.style.marginRight = \"auto\";\n    messagesWrapper.style.width = \"100%\";\n  }\n  // The pill IS the composer in composer-bar mode and should match the\n  // wrapper's responsive width (50vw / 70vw / 90vw), not be capped by\n  // contentMaxWidth (which is a centered-column convention for the\n  // expanded panel's body, not the pill input itself).\n  if (contentMaxWidth && composerForm && !isComposerBar()) {\n    composerForm.style.maxWidth = contentMaxWidth;\n    composerForm.style.marginLeft = \"auto\";\n    composerForm.style.marginRight = \"auto\";\n  }\n  if (contentMaxWidth && suggestions && !isComposerBar()) {\n    suggestions.style.maxWidth = contentMaxWidth;\n    suggestions.style.marginLeft = \"auto\";\n    suggestions.style.marginRight = \"auto\";\n  }\n  if (contentMaxWidth && attachmentPreviewsContainer && !isComposerBar()) {\n    attachmentPreviewsContainer.style.maxWidth = contentMaxWidth;\n    attachmentPreviewsContainer.style.marginLeft = \"auto\";\n    attachmentPreviewsContainer.style.marginRight = \"auto\";\n  }\n\n  if (config.attachments?.enabled && attachmentInput && attachmentPreviewsContainer) {\n    attachmentManager = AttachmentManager.fromConfig(config.attachments);\n    attachmentManager.setPreviewsContainer(attachmentPreviewsContainer);\n    attachmentInput.addEventListener(\"change\", (e) => {\n      const target = e.target as HTMLInputElement;\n      attachmentManager?.handleFileSelect(target.files);\n      target.value = \"\";\n    });\n\n    const dropCfg = config.attachments.dropOverlay;\n    const overlay = buildDropOverlay(dropCfg);\n    container.appendChild(overlay);\n  }\n\n  // Slot system: allow custom content injection into specific regions\n  const renderSlots = () => {\n    const slots = config.layout?.slots ?? {};\n    \n    // Helper to get default slot content\n    const getDefaultSlotContent = (slot: WidgetLayoutSlot): HTMLElement | null => {\n      switch (slot) {\n        case \"body-top\":\n          // Default: the intro card\n          return container.querySelector(\".persona-rounded-2xl.persona-bg-persona-surface.persona-p-6\") as HTMLElement || null;\n        case \"messages\":\n          return messagesWrapper;\n        case \"footer-top\":\n          return suggestions;\n        case \"composer\":\n          return composerForm;\n        case \"footer-bottom\":\n          return statusText;\n        default:\n          return null;\n      }\n    };\n\n    // Helper to insert content into slot region\n    const insertSlotContent = (slot: WidgetLayoutSlot, element: HTMLElement) => {\n      switch (slot) {\n        case \"header-left\":\n        case \"header-center\":\n        case \"header-right\":\n          // Header slots - prepend/append to header\n          if (slot === \"header-left\") {\n            header.insertBefore(element, header.firstChild);\n          } else if (slot === \"header-right\") {\n            header.appendChild(element);\n          } else {\n            // header-center: insert after icon/title\n            const titleSection = header.querySelector(\".persona-flex-col\");\n            if (titleSection) {\n              titleSection.parentNode?.insertBefore(element, titleSection.nextSibling);\n            } else {\n              header.appendChild(element);\n            }\n          }\n          break;\n        case \"body-top\": {\n          // Replace or prepend to body\n          const introCard = body.querySelector(\".persona-rounded-2xl.persona-bg-persona-surface.persona-p-6\");\n          if (introCard) {\n            introCard.replaceWith(element);\n          } else {\n            body.insertBefore(element, body.firstChild);\n          }\n          break;\n        }\n        case \"body-bottom\":\n          // Append after messages wrapper\n          body.appendChild(element);\n          break;\n        case \"footer-top\":\n          // Replace suggestions area\n          suggestions.replaceWith(element);\n          break;\n        case \"footer-bottom\":\n          // Replace or append after status text\n          statusText.replaceWith(element);\n          break;\n        default:\n          // For other slots, just append to appropriate container\n          break;\n      }\n    };\n\n    // Process each configured slot\n    for (const [slotName, renderer] of Object.entries(slots) as [WidgetLayoutSlot, SlotRenderer][]) {\n      if (renderer) {\n        try {\n          const slotElement = renderer({\n            config,\n            defaultContent: () => getDefaultSlotContent(slotName)\n          });\n          if (slotElement) {\n            insertSlotContent(slotName, slotElement);\n          }\n        } catch (error) {\n          if (typeof console !== \"undefined\") {\n            // eslint-disable-next-line no-console\n            console.error(`[AgentWidget] Error rendering slot \"${slotName}\":`, error);\n          }\n        }\n      }\n    }\n  };\n\n  // Render custom slots\n  renderSlots();\n\n  // Add event delegation for reasoning and tool bubble expansion\n  // This handles clicks even after idiomorph morphs the DOM\n  const handleBubbleExpansion = (event: Event) => {\n    const target = event.target as HTMLElement;\n    \n    // Check if the click/keypress is on an expand header button\n    const headerButton = target.closest('button[data-expand-header=\"true\"]') as HTMLElement;\n    if (!headerButton) return;\n    \n    // Find the parent bubble element\n    const bubble = headerButton.closest('.persona-reasoning-bubble, .persona-tool-bubble, .persona-approval-bubble') as HTMLElement;\n    if (!bubble) return;\n    \n    // Get message ID from bubble\n    const messageId = bubble.getAttribute('data-message-id');\n    if (!messageId) return;\n    \n    const bubbleType = headerButton.getAttribute('data-bubble-type');\n    \n    // Toggle expansion state\n    if (bubbleType === 'reasoning') {\n      if (reasoningExpansionState.has(messageId)) {\n        reasoningExpansionState.delete(messageId);\n      } else {\n        reasoningExpansionState.add(messageId);\n      }\n      updateReasoningBubbleUI(messageId, bubble);\n    } else if (bubbleType === 'tool') {\n      if (toolExpansionState.has(messageId)) {\n        toolExpansionState.delete(messageId);\n      } else {\n        toolExpansionState.add(messageId);\n      }\n      updateToolBubbleUI(messageId, bubble, config);\n    } else if (bubbleType === 'approval') {\n      const approvalConfig = config.approval !== false ? config.approval : undefined;\n      const defaultExpanded = (approvalConfig?.detailsDisplay ?? 'collapsed') === 'expanded';\n      const expanded = approvalDetailsExpansionState.get(messageId) ?? defaultExpanded;\n      approvalDetailsExpansionState.set(messageId, !expanded);\n      updateApprovalDetailsUI(messageId, bubble, config);\n    }\n    // Invalidate cached wrapper so next render rebuilds with current expansion state\n    messageCache.delete(messageId);\n  };\n\n  // Attach event listeners to messagesWrapper for event delegation\n  messagesWrapper.addEventListener('pointerdown', (event) => {\n    const target = event.target as HTMLElement;\n    if (target.closest('button[data-expand-header=\"true\"]')) {\n      event.preventDefault();\n      handleBubbleExpansion(event);\n    }\n  });\n\n  messagesWrapper.addEventListener('keydown', (event) => {\n    const target = event.target as HTMLElement;\n    if ((event.key === 'Enter' || event.key === ' ') && target.closest('button[data-expand-header=\"true\"]')) {\n      event.preventDefault();\n      handleBubbleExpansion(event);\n    }\n  });\n\n  // Normalize manual (triple-click + Ctrl/Cmd-C) copies of message text. The\n  // browser serializes the DOM selection, and block-level markdown elements\n  // (<p>, <li>, <pre>, …) emit surrounding newlines — so a single-message copy\n  // arrives with stray trailing/leading blank lines. Rewrite the clipboard's\n  // plain text to the trimmed selection so the buffer matches what was visibly\n  // highlighted. (The Copy action button is unaffected; it uses message.content.)\n  messagesWrapper.addEventListener('copy', (event) => {\n    const { clipboardData } = event;\n    if (!clipboardData) return;\n    const root = messagesWrapper.getRootNode() as { getSelection?: () => Selection | null };\n    const selection =\n      typeof root.getSelection === 'function' ? root.getSelection() : window.getSelection();\n    if (!selection || selection.isCollapsed) return;\n    const raw = selection.toString();\n    const normalized = normalizeCopiedSelectionText(raw);\n    if (!normalized || normalized === raw) return;\n    clipboardData.setData('text/plain', normalized);\n    event.preventDefault();\n  });\n\n  // Add event delegation for message action buttons (upvote, downvote, copy)\n  // This handles clicks even after idiomorph morphs the DOM and strips inline listeners\n  const messageVoteState = new Map<string, \"upvote\" | \"downvote\">();\n\n  // Read-aloud (text-to-speech) button state. The ReadAloudController in the\n  // session is the source of truth; these mirror its last-known state so the\n  // button visuals can be re-applied after every render/morph (which would\n  // otherwise revert the swapped icon to the default \"volume-2\").\n  let readAloudActiveId: string | null = null;\n  let readAloudActiveState: ReadAloudState = \"idle\";\n\n  const READ_ALOUD_ICONS: Record<ReadAloudState, { icon: string; label: string }> = {\n    idle: { icon: \"volume-2\", label: \"Read aloud\" },\n    loading: { icon: \"loader-circle\", label: \"Loading…\" },\n    playing: { icon: \"pause\", label: \"Pause\" },\n    paused: { icon: \"play\", label: \"Resume\" },\n  };\n\n  const applyReadAloudButton = (btn: HTMLElement, state: ReadAloudState) => {\n    const { icon, label } = READ_ALOUD_ICONS[state];\n    btn.setAttribute(\"aria-label\", label);\n    btn.title = label;\n    btn.setAttribute(\"aria-pressed\", state === \"idle\" ? \"false\" : \"true\");\n    btn.classList.toggle(\"persona-message-action-active\", state !== \"idle\");\n    btn.classList.toggle(\"persona-message-action-loading\", state === \"loading\");\n    const svg = renderLucideIcon(icon, 14, \"currentColor\", 2);\n    if (svg) {\n      btn.innerHTML = \"\";\n      btn.appendChild(svg);\n    }\n  };\n\n  // Re-apply the current read-aloud state to every read-aloud button in the\n  // thread. Called on state change and after each render so a button that is\n  // playing/paused keeps its icon across DOM morphs.\n  const refreshReadAloudButtons = () => {\n    const buttons = messagesWrapper.querySelectorAll<HTMLElement>('[data-action=\"read-aloud\"]');\n    buttons.forEach((btn) => {\n      const container = btn.closest(\"[data-actions-for]\");\n      const id = container?.getAttribute(\"data-actions-for\") ?? null;\n      const state: ReadAloudState = id && id === readAloudActiveId ? readAloudActiveState : \"idle\";\n      applyReadAloudButton(btn, state);\n    });\n  };\n\n  messagesWrapper.addEventListener('click', (event) => {\n    const target = event.target as HTMLElement;\n    const actionBtn = target.closest('.persona-message-action-btn[data-action]') as HTMLElement;\n    if (!actionBtn) return;\n\n    event.preventDefault();\n    event.stopPropagation();\n\n    const actionsContainer = actionBtn.closest('[data-actions-for]') as HTMLElement;\n    if (!actionsContainer) return;\n\n    const messageId = actionsContainer.getAttribute('data-actions-for');\n    if (!messageId) return;\n\n    const action = actionBtn.getAttribute('data-action');\n\n    if (action === 'copy') {\n      const messages = session.getMessages();\n      const message = messages.find(m => m.id === messageId);\n      if (message && messageActionCallbacks.onCopy) {\n        // Copy to clipboard\n        const textToCopy = message.content || \"\";\n        navigator.clipboard.writeText(textToCopy).then(() => {\n          // Show success feedback - swap icon temporarily\n          actionBtn.classList.add(\"persona-message-action-success\");\n          const checkIcon = renderLucideIcon(\"check\", 14, \"currentColor\", 2);\n          if (checkIcon) {\n            actionBtn.innerHTML = \"\";\n            actionBtn.appendChild(checkIcon);\n          }\n          setTimeout(() => {\n            actionBtn.classList.remove(\"persona-message-action-success\");\n            const originalIcon = renderLucideIcon(\"copy\", 14, \"currentColor\", 2);\n            if (originalIcon) {\n              actionBtn.innerHTML = \"\";\n              actionBtn.appendChild(originalIcon);\n            }\n          }, 2000);\n        }).catch((err) => {\n          if (typeof console !== \"undefined\") {\n            // eslint-disable-next-line no-console\n            console.error(\"[AgentWidget] Failed to copy message:\", err);\n          }\n        });\n        messageActionCallbacks.onCopy(message);\n      }\n    } else if (action === 'read-aloud') {\n      // Toggle play/pause/resume; ReadAloudController drives the engine and\n      // notifies onReadAloudChange, which refreshes the button icon.\n      session.toggleReadAloud(messageId);\n    } else if (action === 'upvote' || action === 'downvote') {\n      const currentVote = messageVoteState.get(messageId) ?? null;\n      const wasActive = currentVote === action;\n      const iconName = action === 'upvote' ? 'thumbs-up' : 'thumbs-down';\n\n      if (wasActive) {\n        // Toggle off: revert to outline icon\n        messageVoteState.delete(messageId);\n        actionBtn.classList.remove(\"persona-message-action-active\");\n        const outlineIcon = renderLucideIcon(iconName, 14, \"currentColor\", 2);\n        if (outlineIcon) {\n          actionBtn.innerHTML = \"\";\n          actionBtn.appendChild(outlineIcon);\n        }\n      } else {\n        // Clear opposite vote button and revert its icon\n        const oppositeAction = action === 'upvote' ? 'downvote' : 'upvote';\n        const oppositeBtn = actionsContainer.querySelector(`[data-action=\"${oppositeAction}\"]`);\n        if (oppositeBtn) {\n          oppositeBtn.classList.remove(\"persona-message-action-active\");\n          const oppositeIconName = oppositeAction === 'upvote' ? 'thumbs-up' : 'thumbs-down';\n          const outlineIcon = renderLucideIcon(oppositeIconName, 14, \"currentColor\", 2);\n          if (outlineIcon) {\n            oppositeBtn.innerHTML = \"\";\n            oppositeBtn.appendChild(outlineIcon);\n          }\n        }\n\n        messageVoteState.set(messageId, action);\n        actionBtn.classList.add(\"persona-message-action-active\");\n\n        // Swap to filled icon\n        const filledIcon = renderLucideIcon(iconName, 14, \"currentColor\", 2);\n        if (filledIcon) {\n          filledIcon.setAttribute(\"fill\", \"currentColor\");\n          actionBtn.innerHTML = \"\";\n          actionBtn.appendChild(filledIcon);\n        }\n\n        // Pop animation\n        actionBtn.classList.remove(\"persona-message-action-pop\");\n        void actionBtn.offsetWidth; // force reflow to restart animation\n        actionBtn.classList.add(\"persona-message-action-pop\");\n\n        // Trigger feedback\n        const messages = session.getMessages();\n        const message = messages.find(m => m.id === messageId);\n        if (message && messageActionCallbacks.onFeedback) {\n          messageActionCallbacks.onFeedback({\n            type: action,\n            messageId: message.id,\n            message\n          });\n        }\n      }\n    }\n  });\n\n  // Add event delegation for approval action buttons (approve/deny)\n  messagesWrapper.addEventListener('click', (event) => {\n    const target = event.target as HTMLElement;\n    const approvalButton = target.closest('button[data-approval-action]') as HTMLElement;\n    if (!approvalButton) return;\n\n    event.preventDefault();\n    event.stopPropagation();\n\n    const approvalBubble = approvalButton.closest('.persona-approval-bubble') as HTMLElement;\n    if (!approvalBubble) return;\n\n    const messageId = approvalBubble.getAttribute('data-message-id');\n    if (!messageId) return;\n\n    const action = approvalButton.getAttribute('data-approval-action') as 'approve' | 'deny';\n    if (!action) return;\n\n    const decision = action === 'approve' ? 'approved' as const : 'denied' as const;\n\n    // Find the approval message\n    const messages = session.getMessages();\n    const approvalMessage = messages.find(m => m.id === messageId);\n    if (!approvalMessage?.approval) return;\n\n    // Disable buttons immediately for responsive UI\n    const buttonsContainer = approvalBubble.querySelector('[data-approval-buttons]') as HTMLElement;\n    if (buttonsContainer) {\n      const buttons = buttonsContainer.querySelectorAll('button');\n      buttons.forEach(btn => {\n        (btn as HTMLButtonElement).disabled = true;\n        btn.style.opacity = '0.5';\n        btn.style.cursor = 'not-allowed';\n      });\n    }\n\n    // WebMCP gate approvals resolve a local Promise the bridge is parked on\n    // (no server round-trip); server-driven approvals call the API. The\n    // `toolType` marker set in `requestWebMcpApproval` discriminates the two.\n    if (approvalMessage.approval.toolType === \"webmcp\") {\n      session.resolveWebMcpApproval(messageId, decision);\n    } else {\n      session.resolveApproval(approvalMessage.approval, decision);\n    }\n  });\n\n  let artifactPaneApi: ArtifactPaneApi | null = null;\n  let artifactPanelResizeObs: ResizeObserver | null = null;\n  let lastArtifactsState: {\n    artifacts: PersonaArtifactRecord[];\n    selectedId: string | null;\n  } = { artifacts: [], selectedId: null };\n  let artifactsPaneUserHidden = false;\n  const sessionRef: { current: AgentWidgetSession | null } = { current: null };\n\n  // Click delegation for artifact download buttons\n  messagesWrapper.addEventListener('click', (event) => {\n    const target = event.target as HTMLElement;\n    const dlBtn = target.closest('[data-download-artifact]') as HTMLElement;\n    if (!dlBtn) return;\n    event.preventDefault();\n    event.stopPropagation();\n    const artifactId = dlBtn.getAttribute('data-download-artifact');\n    if (!artifactId) return;\n    // Let integrator intercept\n    const dlPrevented = config.features?.artifacts?.onArtifactAction?.({ type: 'download', artifactId });\n    if (dlPrevented === true) return;\n    // Try session state first, fall back to content stored in the card's rawContent props\n    const artifact = session.getArtifactById(artifactId);\n    let markdown = artifact?.markdown;\n    let title = artifact?.title || 'artifact';\n    if (!markdown) {\n      // After page refresh, session state is gone: read from the persisted card message\n      const cardEl = dlBtn.closest('[data-open-artifact]');\n      const msgEl = cardEl?.closest('[data-message-id]');\n      const msgId = msgEl?.getAttribute('data-message-id');\n      if (msgId) {\n        const msgs = session.getMessages();\n        const msg = msgs.find(m => m.id === msgId);\n        if (msg?.rawContent) {\n          try {\n            const parsed = JSON.parse(msg.rawContent);\n            markdown = parsed?.props?.markdown;\n            title = parsed?.props?.title || title;\n          } catch { /* ignore */ }\n        }\n      }\n    }\n    if (!markdown) return;\n    const blob = new Blob([markdown], { type: 'text/markdown' });\n    const url = URL.createObjectURL(blob);\n    const a = document.createElement('a');\n    a.href = url;\n    a.download = `${title}.md`;\n    a.click();\n    URL.revokeObjectURL(url);\n  });\n\n  // Click delegation for artifact reference cards\n  messagesWrapper.addEventListener('click', (event) => {\n    const target = event.target as HTMLElement;\n    const card = target.closest('[data-open-artifact]') as HTMLElement;\n    if (!card) return;\n    const artifactId = card.getAttribute('data-open-artifact');\n    if (!artifactId) return;\n    // Let integrator intercept\n    const openPrevented = config.features?.artifacts?.onArtifactAction?.({ type: 'open', artifactId });\n    if (openPrevented === true) return;\n    event.preventDefault();\n    event.stopPropagation();\n    artifactsPaneUserHidden = false;\n    session.selectArtifact(artifactId);\n    syncArtifactPane();\n  });\n\n  // Keyboard support for artifact cards\n  messagesWrapper.addEventListener('keydown', (event) => {\n    if (event.key !== 'Enter' && event.key !== ' ') return;\n    const target = event.target as HTMLElement;\n    if (!target.hasAttribute('data-open-artifact')) return;\n    event.preventDefault();\n    target.click();\n  });\n\n  // --- ask_user_question sheet interaction ---\n  // Event delegation for the answer-pill sheet that mounts in the composer\n  // overlay. Handles pill pick (single), multi-select toggle + submit, free-\n  // text pill expansion + submit, and dismissal. Selection becomes a regular\n  // user message via session.sendMessage so the agent resumes on the next turn.\n  const askUserOverlay = panelElements.composerOverlay;\n\n  const submitAskUserAnswer = (\n    sheet: HTMLElement,\n    text: string,\n    meta: {\n      source: \"pick\" | \"multi\" | \"free-text\" | \"submit-all\";\n      values?: string[];\n      structured?: Record<string, string | string[]>;\n    }\n  ): void => {\n    const trimmed = text.trim();\n    if (!trimmed || !sessionRef.current) return;\n    const toolCallId = sheet.getAttribute(\"data-tool-call-id\") ?? \"\";\n    const isFreeText = meta.source === \"free-text\";\n\n    // Dispatch before removing the sheet so listeners can still query DOM state.\n    mount.dispatchEvent(\n      new CustomEvent(\"persona:askUserQuestion:answered\", {\n        detail: {\n          toolUseId: toolCallId,\n          answer: trimmed,\n          answers: meta.structured,\n          values: meta.values ?? (meta.source === \"multi\" ? trimmed.split(\", \") : [trimmed]),\n          isFreeText,\n          source: meta.source,\n        },\n        bubbles: true,\n        composed: true,\n      })\n    );\n\n    removeAskUserQuestionSheet(askUserOverlay, toolCallId);\n\n    // Branch: LOCAL-tool pause (step_await) resumes via /resume with structured\n    // toolOutputs; legacy path sends as a plain user message.\n    const sourceMessage = sessionRef.current\n      .getMessages()\n      .find((m) => m.toolCall?.id === toolCallId);\n    if (sourceMessage?.agentMetadata?.awaitingLocalTool) {\n      sessionRef.current.resolveAskUserQuestion(sourceMessage, meta.structured ?? trimmed);\n    } else {\n      sessionRef.current.sendMessage(trimmed);\n    }\n  };\n\n  /**\n   * Persist in-progress grouped-question answers + page index back to the\n   * source message so a refresh restores the user's spot.\n   */\n  const persistGroupedProgress = (sheet: HTMLElement): void => {\n    const session = sessionRef.current;\n    if (!session) return;\n    const toolCallId = sheet.getAttribute(\"data-tool-call-id\") ?? \"\";\n    const sourceMessage = session.getMessages().find((m) => m.toolCall?.id === toolCallId);\n    if (!sourceMessage) return;\n    session.persistAskUserQuestionProgress(sourceMessage, {\n      answers: buildStructuredAnswers(sheet, sourceMessage),\n      currentIndex: getCurrentIndex(sheet),\n    });\n  };\n\n  /**\n   * Build a one-line summary string for the legacy `answer` field on the\n   * answered event when submit-all fires from a grouped sheet.\n   */\n  const stringifyStructured = (answers: Record<string, string | string[]>): string => {\n    return Object.entries(answers)\n      .map(([q, v]) => `${q}: ${Array.isArray(v) ? v.join(\", \") : v}`)\n      .join(\" | \");\n  };\n\n  /**\n   * If `groupedAutoAdvance` is enabled (default) and we're not on the final\n   * page, advance one step. The final page never auto-submits: users always\n   * confirm with an explicit Submit-all click so they can review.\n   */\n  const maybeAutoAdvance = (sheet: HTMLElement): void => {\n    if (config.features?.askUserQuestion?.groupedAutoAdvance === false) return;\n    const idx = getCurrentIndex(sheet);\n    const count = getQuestionCount(sheet);\n    if (idx >= count - 1) return;\n    const sourceMessage = sessionRef.current\n      ?.getMessages()\n      .find((m) => m.toolCall?.id === sheet.getAttribute(\"data-tool-call-id\"));\n    if (!sourceMessage) return;\n    navigateToPage(sheet, sourceMessage, config, idx + 1);\n    persistGroupedProgress(sheet);\n  };\n\n  askUserOverlay.addEventListener(\"click\", (event) => {\n    const target = event.target as HTMLElement;\n    const trigger = target.closest<HTMLElement>(\"[data-ask-user-action]\");\n    if (!trigger) return;\n    const sheet = trigger.closest<HTMLElement>(\"[data-persona-ask-sheet-for]\");\n    if (!sheet) return;\n\n    const action = trigger.getAttribute(\"data-ask-user-action\");\n    event.preventDefault();\n    event.stopPropagation();\n\n    if (action === \"dismiss\") {\n      const toolCallId = sheet.getAttribute(\"data-tool-call-id\") ?? \"\";\n      mount.dispatchEvent(\n        new CustomEvent(\"persona:askUserQuestion:dismissed\", {\n          detail: { toolUseId: toolCallId },\n          bubbles: true,\n          composed: true,\n        })\n      );\n      removeAskUserQuestionSheet(askUserOverlay, toolCallId);\n\n      // Best-effort: if this sheet corresponds to a LOCAL-awaiting tool,\n      // unblock the paused execution with a sentinel answer so the server\n      // doesn't sit in waiting_for_local forever. Fire-and-forget: errors\n      // are surfaced to the onError callback. Flip the answered flag first\n      // so a racing render pass doesn't re-mount the sheet mid-dismissal.\n      const sourceMessage = sessionRef.current\n        ?.getMessages()\n        .find((m) => m.toolCall?.id === toolCallId);\n      if (sourceMessage?.agentMetadata?.awaitingLocalTool) {\n        sessionRef.current?.markAskUserQuestionResolved(sourceMessage);\n        sessionRef.current?.resolveAskUserQuestion(sourceMessage, \"(dismissed)\");\n      }\n      return;\n    }\n\n    if (action === \"pick\") {\n      const label = trigger.getAttribute(\"data-option-label\");\n      if (!label) return;\n      const multiSelect = sheet.getAttribute(\"data-multi-select\") === \"true\";\n      const grouped = isGroupedSheet(sheet);\n\n      if (grouped && multiSelect) {\n        const stored = readAnswersFromSheet(sheet)[getCurrentIndex(sheet)];\n        const set = new Set<string>(Array.isArray(stored) ? stored : []);\n        if (set.has(label)) set.delete(label);\n        else set.add(label);\n        setCurrentAnswer(sheet, Array.from(set));\n        persistGroupedProgress(sheet);\n        return;\n      }\n\n      if (grouped) {\n        setCurrentAnswer(sheet, label);\n        persistGroupedProgress(sheet);\n        maybeAutoAdvance(sheet);\n        return;\n      }\n\n      // 1-question modes: preserve original UX.\n      if (multiSelect) {\n        const pressed = trigger.getAttribute(\"aria-pressed\") === \"true\";\n        trigger.setAttribute(\"aria-pressed\", pressed ? \"false\" : \"true\");\n        trigger.classList.toggle(\"persona-ask-pill-selected\", !pressed);\n        const submitBtn = sheet.querySelector<HTMLButtonElement>(\n          '[data-ask-user-action=\"submit-multi\"]'\n        );\n        if (submitBtn) {\n          submitBtn.disabled = getSelectedLabels(sheet).length === 0;\n        }\n        return;\n      }\n      submitAskUserAnswer(sheet, label, { source: \"pick\", values: [label] });\n      return;\n    }\n\n    if (action === \"submit-multi\") {\n      const labels = getSelectedLabels(sheet);\n      if (labels.length === 0) return;\n      submitAskUserAnswer(sheet, labels.join(\", \"), {\n        source: \"multi\",\n        values: labels,\n      });\n      return;\n    }\n\n    if (action === \"open-free-text\") {\n      const row = sheet.querySelector<HTMLElement>('[data-ask-free-text-row=\"true\"]');\n      if (row) {\n        row.classList.remove(\"persona-hidden\");\n        const input = row.querySelector<HTMLInputElement>('[data-ask-free-text-input=\"true\"]');\n        input?.focus();\n      }\n      return;\n    }\n\n    if (action === \"focus-free-text\") {\n      // Rows-layout Other row: input lives inside the row container itself.\n      // Native click on the input already focuses it; this branch handles\n      // clicks on the badge or row chrome AND digit-shortcut activations.\n      const input = sheet.querySelector<HTMLInputElement>('[data-ask-free-text-input=\"true\"]');\n      input?.focus();\n      return;\n    }\n\n    if (action === \"submit-free-text\") {\n      const input = sheet.querySelector<HTMLInputElement>('[data-ask-free-text-input=\"true\"]');\n      const text = input?.value ?? \"\";\n      if (!text.trim()) return;\n      if (isGroupedSheet(sheet)) {\n        setCurrentAnswer(sheet, text.trim());\n        persistGroupedProgress(sheet);\n        maybeAutoAdvance(sheet);\n        return;\n      }\n      submitAskUserAnswer(sheet, text, { source: \"free-text\" });\n      return;\n    }\n\n    if (action === \"next\" || action === \"back\") {\n      if (!sessionRef.current) return;\n      const toolCallId = sheet.getAttribute(\"data-tool-call-id\") ?? \"\";\n      const sourceMessage = sessionRef.current\n        .getMessages()\n        .find((m) => m.toolCall?.id === toolCallId);\n      if (!sourceMessage) return;\n      // Flush any unsubmitted free-text input as the current answer.\n      const freeInput = sheet.querySelector<HTMLInputElement>('[data-ask-free-text-input=\"true\"]');\n      const pending = freeInput?.value?.trim() ?? \"\";\n      if (pending) {\n        const stored = readAnswersFromSheet(sheet)[getCurrentIndex(sheet)];\n        if (typeof stored !== \"string\" || stored !== pending) {\n          setCurrentAnswer(sheet, pending);\n        }\n      }\n      const direction = action === \"next\" ? 1 : -1;\n      const nextIdx = getCurrentIndex(sheet) + direction;\n      navigateToPage(sheet, sourceMessage, config, nextIdx);\n      persistGroupedProgress(sheet);\n      return;\n    }\n\n    if (action === \"submit-all\") {\n      if (!sessionRef.current) return;\n      const toolCallId = sheet.getAttribute(\"data-tool-call-id\") ?? \"\";\n      const sourceMessage = sessionRef.current\n        .getMessages()\n        .find((m) => m.toolCall?.id === toolCallId);\n      if (!sourceMessage) return;\n      // Flush any pending free-text on the final page first.\n      const freeInput = sheet.querySelector<HTMLInputElement>('[data-ask-free-text-input=\"true\"]');\n      const pending = freeInput?.value?.trim() ?? \"\";\n      if (pending) setCurrentAnswer(sheet, pending);\n\n      const structured = buildStructuredAnswers(sheet, sourceMessage);\n      // Persist final answers to message metadata BEFORE resolving so the\n      // answered-state review card (which reads `agentMetadata\n      // .askUserQuestionAnswers`) shows the user's actual picks instead of\n      // \"(skipped)\" placeholders. Without this, any answer set only via the\n      // pending-flush above (or via paths that bypassed the per-pick persist\n      // hook) would be missing from the transcript review even though it\n      // landed in the structured payload sent to the agent.\n      sessionRef.current.persistAskUserQuestionProgress(sourceMessage, {\n        answers: structured,\n        currentIndex: getCurrentIndex(sheet),\n      });\n      const summary = stringifyStructured(structured);\n      submitAskUserAnswer(sheet, summary || \"(submitted)\", {\n        source: \"submit-all\",\n        structured,\n      });\n      return;\n    }\n\n    if (action === \"skip\") {\n      if (!sessionRef.current) return;\n      const toolCallId = sheet.getAttribute(\"data-tool-call-id\") ?? \"\";\n      const sourceMessage = sessionRef.current\n        .getMessages()\n        .find((m) => m.toolCall?.id === toolCallId);\n      if (!sourceMessage) return;\n\n      const grouped = isGroupedSheet(sheet);\n      const idx = getCurrentIndex(sheet);\n      const count = getQuestionCount(sheet);\n      const isFinal = idx >= count - 1;\n\n      // Single-question payloads behave like dismiss.\n      if (!grouped) {\n        mount.dispatchEvent(\n          new CustomEvent(\"persona:askUserQuestion:dismissed\", {\n            detail: { toolUseId: toolCallId },\n            bubbles: true,\n            composed: true,\n          })\n        );\n        removeAskUserQuestionSheet(askUserOverlay, toolCallId);\n        if (sourceMessage.agentMetadata?.awaitingLocalTool) {\n          sessionRef.current.markAskUserQuestionResolved(sourceMessage);\n          sessionRef.current.resolveAskUserQuestion(sourceMessage, \"(dismissed)\");\n        }\n        return;\n      }\n\n      // Drop the current question's answer (if any) so it's absent from the\n      // resolved Record. setCurrentAnswer with an empty string deletes the\n      // index from the in-memory map.\n      setCurrentAnswer(sheet, \"\");\n      // Also clear any unsubmitted free-text on this page.\n      const freeInput = sheet.querySelector<HTMLInputElement>('[data-ask-free-text-input=\"true\"]');\n      if (freeInput) freeInput.value = \"\";\n\n      if (isFinal) {\n        // Submit with whatever has been recorded so far.\n        const structured = buildStructuredAnswers(sheet, sourceMessage);\n        const summary = stringifyStructured(structured);\n        submitAskUserAnswer(sheet, summary || \"(skipped)\", {\n          source: \"submit-all\",\n          structured,\n        });\n        return;\n      }\n\n      // Intermediate page: advance one step without recording.\n      navigateToPage(sheet, sourceMessage, config, idx + 1);\n      persistGroupedProgress(sheet);\n      return;\n    }\n  });\n\n  // Enter on the free-text input → submit. Stays on the overlay because the\n  // event target IS the input, which lives inside the overlay subtree.\n  askUserOverlay.addEventListener(\"keydown\", (event) => {\n    if (event.key !== \"Enter\") return;\n    const target = event.target as HTMLElement;\n    const input = target as HTMLInputElement;\n    if (!input.matches?.('[data-ask-free-text-input=\"true\"]')) return;\n    const sheet = input.closest<HTMLElement>(\"[data-persona-ask-sheet-for]\");\n    if (!sheet) return;\n    event.preventDefault();\n    const text = input.value;\n    if (!text.trim()) return;\n    if (isGroupedSheet(sheet)) {\n      setCurrentAnswer(sheet, text.trim());\n      persistGroupedProgress(sheet);\n      maybeAutoAdvance(sheet);\n      return;\n    }\n    submitAskUserAnswer(sheet, text, { source: \"free-text\" });\n  });\n\n  // Digit 1–9 → pick option N on the current rows-layout single-select page.\n  // Listens on `document` so the shortcut fires regardless of where focus\n  // currently sits (host page body, panel chrome, anywhere). The handler\n  // gates strictly: only fires when an active sheet is mounted in our\n  // overlay, and bails when focus is on any input/textarea/contenteditable\n  // (covers the free-text input, the chat composer, and any host-page input).\n  const handleAskUserDigitKey = (event: KeyboardEvent): void => {\n    if (!/^[1-9]$/.test(event.key)) return;\n    if (event.metaKey || event.ctrlKey || event.altKey) return;\n    const target = event.target as HTMLElement | null;\n    if (\n      target?.tagName === \"INPUT\" ||\n      target?.tagName === \"TEXTAREA\" ||\n      target?.isContentEditable\n    ) {\n      return;\n    }\n    const sheet = askUserOverlay.querySelector<HTMLElement>(\"[data-persona-ask-sheet-for]\");\n    if (!sheet) return;\n    if (sheet.getAttribute(\"data-ask-layout\") !== \"rows\") return;\n    if (sheet.getAttribute(\"data-multi-select\") === \"true\") return;\n    const n = Number(event.key);\n    const pills = sheet.querySelectorAll<HTMLElement>(\n      '[data-ask-pill-list=\"true\"] [data-ask-user-action=\"pick\"], [data-ask-pill-list=\"true\"] [data-ask-user-action=\"focus-free-text\"]'\n    );\n    const target_pill = pills[n - 1];\n    if (!target_pill) return;\n    event.preventDefault();\n    target_pill.click();\n  };\n  document.addEventListener(\"keydown\", handleAskUserDigitKey);\n\n  let artifactSplitRoot: HTMLElement | null = null;\n  let artifactResizeHandle: HTMLElement | null = null;\n  let artifactResizeUnbind: (() => void) | null = null;\n  let artifactResizeDocEnd: (() => void) | null = null;\n  let reconcileArtifactResize: () => void = () => {};\n\n  function stopArtifactResizePointer() {\n    artifactResizeDocEnd?.();\n    artifactResizeDocEnd = null;\n  }\n\n  /** Flush split: overlay handle on the seam so it does not consume flex gap (extension + resizable). */\n  const positionExtensionArtifactResizeHandle = () => {\n    if (!artifactSplitRoot || !artifactResizeHandle) return;\n    const ext = mount.classList.contains(\"persona-artifact-appearance-seamless\");\n    const ownerWin = mount.ownerDocument.defaultView ?? window;\n    const mobile = ownerWin.innerWidth <= 640;\n    if (!ext || mount.classList.contains(\"persona-artifact-narrow-host\") || mobile) {\n      artifactResizeHandle.style.removeProperty(\"position\");\n      artifactResizeHandle.style.removeProperty(\"left\");\n      artifactResizeHandle.style.removeProperty(\"top\");\n      artifactResizeHandle.style.removeProperty(\"bottom\");\n      artifactResizeHandle.style.removeProperty(\"width\");\n      artifactResizeHandle.style.removeProperty(\"z-index\");\n      return;\n    }\n    const chat = artifactSplitRoot.firstElementChild as HTMLElement | null;\n    if (!chat || chat === artifactResizeHandle) return;\n    const hitW = 10;\n    artifactResizeHandle.style.position = \"absolute\";\n    artifactResizeHandle.style.top = \"0\";\n    artifactResizeHandle.style.bottom = \"0\";\n    artifactResizeHandle.style.width = `${hitW}px`;\n    artifactResizeHandle.style.zIndex = \"5\";\n    const left = chat.offsetWidth - hitW / 2;\n    artifactResizeHandle.style.left = `${Math.max(0, left)}px`;\n  };\n\n  /** No-op until artifact pane is created; replaced below when artifacts are enabled. */\n  let applyLauncherArtifactPanelWidth: () => void = () => {};\n\n  const syncArtifactPane = () => {\n    if (!artifactPaneApi || !artifactsSidebarEnabled(config)) return;\n    applyArtifactLayoutCssVars(mount, config);\n    applyArtifactPaneAppearance(mount, config);\n    applyLauncherArtifactPanelWidth();\n    const threshold = config.features?.artifacts?.layout?.narrowHostMaxWidth ?? 520;\n    const w = panel.getBoundingClientRect().width || 0;\n    mount.classList.toggle(\"persona-artifact-narrow-host\", w > 0 && w <= threshold);\n    artifactPaneApi.update(lastArtifactsState);\n    if (artifactsPaneUserHidden) {\n      artifactPaneApi.setMobileOpen(false);\n      artifactPaneApi.element.classList.add(\"persona-hidden\");\n      artifactPaneApi.backdrop?.classList.add(\"persona-hidden\");\n    } else if (lastArtifactsState.artifacts.length > 0) {\n      // User chose “show” again (e.g. programmatic showArtifacts): clear dismiss chrome\n      // and force drawer open so narrow-host / mobile slide-out is not stuck off-screen.\n      artifactPaneApi.element.classList.remove(\"persona-hidden\");\n      artifactPaneApi.setMobileOpen(true);\n    }\n    reconcileArtifactResize();\n  };\n\n  if (artifactsSidebarEnabled(config)) {\n    panel.style.position = \"relative\";\n    const chatColumn = createElement(\n      \"div\",\n      \"persona-flex persona-flex-1 persona-flex-col persona-min-w-0 persona-min-h-0\"\n    );\n    const splitRoot = createElement(\n      \"div\",\n      \"persona-flex persona-h-full persona-w-full persona-min-h-0 persona-artifact-split-root\"\n    );\n    chatColumn.appendChild(container);\n    artifactPaneApi = createArtifactPane(config, {\n      onSelect: (id) => sessionRef.current?.selectArtifact(id),\n      onDismiss: () => {\n        artifactsPaneUserHidden = true;\n        syncArtifactPane();\n      }\n    });\n    artifactPaneApi.element.classList.add(\"persona-hidden\");\n    artifactSplitRoot = splitRoot;\n    splitRoot.appendChild(chatColumn);\n    splitRoot.appendChild(artifactPaneApi.element);\n    if (artifactPaneApi.backdrop) {\n      panel.appendChild(artifactPaneApi.backdrop);\n    }\n    panel.appendChild(splitRoot);\n\n    reconcileArtifactResize = () => {\n      if (!artifactSplitRoot || !artifactPaneApi) return;\n      const want = config.features?.artifacts?.layout?.resizable === true;\n      if (!want) {\n        artifactResizeUnbind?.();\n        artifactResizeUnbind = null;\n        stopArtifactResizePointer();\n        if (artifactResizeHandle) {\n          artifactResizeHandle.remove();\n          artifactResizeHandle = null;\n        }\n        artifactPaneApi.element.style.removeProperty(\"width\");\n        artifactPaneApi.element.style.removeProperty(\"maxWidth\");\n        return;\n      }\n      if (!artifactResizeHandle) {\n        const handle = createElement(\n          \"div\",\n          \"persona-artifact-split-handle persona-shrink-0 persona-h-full\"\n        );\n        handle.setAttribute(\"role\", \"separator\");\n        handle.setAttribute(\"aria-orientation\", \"vertical\");\n        handle.setAttribute(\"aria-label\", \"Resize artifacts panel\");\n        handle.tabIndex = 0;\n\n        const doc = mount.ownerDocument;\n        const win = doc.defaultView ?? window;\n\n        const onPointerDown = (e: PointerEvent) => {\n          if (!artifactPaneApi || e.button !== 0) return;\n          if (mount.classList.contains(\"persona-artifact-narrow-host\")) return;\n          if (win.innerWidth <= 640) return;\n          e.preventDefault();\n          stopArtifactResizePointer();\n          const startX = e.clientX;\n          const startW = artifactPaneApi.element.getBoundingClientRect().width;\n          const layout = config.features?.artifacts?.layout;\n          const onMove = (ev: PointerEvent) => {\n            const splitW = artifactSplitRoot!.getBoundingClientRect().width;\n            const extensionChrome = mount.classList.contains(\"persona-artifact-appearance-seamless\");\n            const gapPx = extensionChrome ? 0 : readFlexGapPx(artifactSplitRoot!, win);\n            const handleW = extensionChrome ? 0 : handle.getBoundingClientRect().width || 6;\n            // Handle is left of the artifact: drag left widens artifact, drag right narrows it.\n            const next = startW - (ev.clientX - startX);\n            const clamped = resolveArtifactPaneWidthPx(\n              next,\n              splitW,\n              gapPx,\n              handleW,\n              layout?.resizableMinWidth,\n              layout?.resizableMaxWidth\n            );\n            artifactPaneApi!.element.style.width = `${clamped}px`;\n            artifactPaneApi!.element.style.maxWidth = \"none\";\n            positionExtensionArtifactResizeHandle();\n          };\n          const onUp = () => {\n            doc.removeEventListener(\"pointermove\", onMove);\n            doc.removeEventListener(\"pointerup\", onUp);\n            doc.removeEventListener(\"pointercancel\", onUp);\n            artifactResizeDocEnd = null;\n            try {\n              handle.releasePointerCapture(e.pointerId);\n            } catch {\n              /* ignore */\n            }\n          };\n          artifactResizeDocEnd = onUp;\n          doc.addEventListener(\"pointermove\", onMove);\n          doc.addEventListener(\"pointerup\", onUp);\n          doc.addEventListener(\"pointercancel\", onUp);\n          try {\n            handle.setPointerCapture(e.pointerId);\n          } catch {\n            /* ignore */\n          }\n        };\n\n        handle.addEventListener(\"pointerdown\", onPointerDown);\n        artifactResizeHandle = handle;\n        artifactSplitRoot.insertBefore(handle, artifactPaneApi.element);\n        artifactResizeUnbind = () => {\n          handle.removeEventListener(\"pointerdown\", onPointerDown);\n        };\n      }\n      if (artifactResizeHandle) {\n        const has =\n          lastArtifactsState.artifacts.length > 0 && !artifactsPaneUserHidden;\n        artifactResizeHandle.classList.toggle(\"persona-hidden\", !has);\n        positionExtensionArtifactResizeHandle();\n      }\n    };\n\n    applyLauncherArtifactPanelWidth = () => {\n      if (!launcherEnabled || !artifactPaneApi) return;\n      const sidebarMode = config.launcher?.sidebarMode ?? false;\n      if (sidebarMode) return;\n      if (isDockedMountMode(config) && resolveDockConfig(config).reveal === \"emerge\") return;\n      const ownerWindow = mount.ownerDocument.defaultView ?? window;\n      const mobileFullscreen = config.launcher?.mobileFullscreen ?? true;\n      const mobileBreakpoint = config.launcher?.mobileBreakpoint ?? 640;\n      if (mobileFullscreen && ownerWindow.innerWidth <= mobileBreakpoint) return;\n      if (!shouldExpandLauncherForArtifacts(config, launcherEnabled)) return;\n\n      const base = config.launcher?.width ?? config.launcherWidth ?? DEFAULT_FLOATING_LAUNCHER_WIDTH;\n      const expanded =\n        config.features?.artifacts?.layout?.expandedPanelWidth ??\n        \"min(720px, calc(100vw - 24px))\";\n      const hasVisible =\n        lastArtifactsState.artifacts.length > 0 && !artifactsPaneUserHidden;\n      if (hasVisible) {\n        panel.style.width = expanded;\n        panel.style.maxWidth = expanded;\n      } else {\n        panel.style.width = base;\n        panel.style.maxWidth = base;\n      }\n    };\n\n    if (typeof ResizeObserver !== \"undefined\") {\n      artifactPanelResizeObs = new ResizeObserver(() => {\n        syncArtifactPane();\n      });\n      artifactPanelResizeObs.observe(panel);\n    }\n  } else {\n    panel.appendChild(container);\n    // Composer-bar mode: the pill (footer) and peek banner live in a\n    // viewport-fixed sibling of the wrapper (`pillRoot`) so they're\n    // independent of the wrapper's geometry transitions. Critical for\n    // modal mode: the wrapper there has `transform: translate(-50%, -50%)`\n    // which would establish a containing block trapping any `position: fixed`\n    // descendant. Order inside pillRoot: peekBanner (slim row above pill)\n    // → footer (pill). pillRoot's `gap` spaces them; the peek is hidden by\n    // default until ui.ts toggles `.persona-pill-peek--visible` based on\n    // streaming/hover/open state via syncComposerBarPeek().\n    if (isComposerBar() && pillRoot) {\n      if (panelElements.peekBanner) {\n        pillRoot.appendChild(panelElements.peekBanner);\n      }\n      pillRoot.appendChild(footer);\n    }\n  }\n  mount.appendChild(wrapper);\n  // pillRoot is mounted *after* wrapper so it naturally stacks on top\n  // when both share the same z-index (e.g. fullscreen mode where the\n  // pill should float above the chat panel chrome).\n  if (pillRoot) {\n    mount.appendChild(pillRoot);\n  }\n\n  // Apply full-height and sidebar styles if enabled\n  // This ensures the widget fills its container height with proper flex layout\n  const applyFullHeightStyles = () => {\n    // Composer-bar mode owns its own sizing/chrome. Geometry comes from\n    // `applyComposerBarGeometry()` (per-state inline on the wrapper), the\n    // pill carries its own chrome via `.persona-pill-composer`, and the\n    // expanded chat panel chrome (border + radius + shadow + bg) is painted\n    // inline on the `container` (NOT the panel: the panel is a transparent\n    // flex column with a gap so the pill renders as a sibling below the\n    // chrome). Same theme contract as floating mode\n    // (`theme.components.panel.{shadow,border,borderRadius}`); collapsed\n    // clears it (container is hidden via display:none anyway), expanded\n    // re-applies it, with the `fullscreen` variant intentionally chrome-less.\n    if (isComposerBar()) {\n      panel.style.width = \"100%\";\n      panel.style.maxWidth = \"100%\";\n      const cb = config.launcher?.composerBar ?? {};\n      const isExpanded = wrapper.dataset.state === \"expanded\";\n      const expandedSize = cb.expandedSize ?? \"anchored\";\n      const wantsChrome = isExpanded && expandedSize !== \"fullscreen\";\n      if (!wantsChrome) {\n        container.style.background = \"\";\n        container.style.border = \"\";\n        container.style.borderRadius = \"\";\n        container.style.overflow = \"\";\n        container.style.boxShadow = \"\";\n        return;\n      }\n      const panelPartial = config.theme?.components?.panel;\n      const activeTheme = getActiveTheme(config);\n      const resolveCb = (raw: string | undefined, fallback: string): string => {\n        if (raw == null || raw === \"\") return fallback;\n        return resolveTokenValue(activeTheme, raw) ?? raw;\n      };\n      const defaultBorder = \"1px solid var(--persona-border)\";\n      const defaultShadow = \"var(--persona-palette-shadows-xl, 0 25px 50px -12px rgba(0, 0, 0, 0.25))\";\n      const defaultRadius = \"var(--persona-panel-radius, var(--persona-radius-xl, 0.75rem))\";\n      container.style.background = \"var(--persona-surface, #ffffff)\";\n      container.style.border = resolveCb(panelPartial?.border, defaultBorder);\n      container.style.borderRadius = resolveCb(panelPartial?.borderRadius, defaultRadius);\n      container.style.boxShadow = resolveCb(panelPartial?.shadow, defaultShadow);\n      container.style.overflow = \"hidden\";\n      return;\n    }\n    const dockedMode = isDockedMountMode(config);\n    const sidebarMode = config.launcher?.sidebarMode ?? false;\n    const fullHeight = dockedMode || sidebarMode || (config.launcher?.fullHeight ?? false);\n    /** Script-tag / div embed: launcher off, host supplies a sized mount. */\n    const isInlineEmbed = config.launcher?.enabled === false;\n    const panelPartial = config.theme?.components?.panel;\n    const activeTheme = getActiveTheme(config);\n    const resolvePanelChrome = (raw: string | undefined, fallback: string): string => {\n      if (raw == null || raw === \"\") return fallback;\n      return resolveTokenValue(activeTheme, raw) ?? raw;\n    };\n\n    // Mobile fullscreen detection\n    // Use mount's ownerDocument window to get correct viewport width when widget is inside an iframe\n    const ownerWindow = mount.ownerDocument.defaultView ?? window;\n    const mobileFullscreen = config.launcher?.mobileFullscreen ?? true;\n    const mobileBreakpoint = config.launcher?.mobileBreakpoint ?? 640;\n    const isMobileViewport = ownerWindow.innerWidth <= mobileBreakpoint;\n    const shouldGoFullscreen = mobileFullscreen && isMobileViewport && launcherEnabled;\n\n    // Determine panel styling based on mode, with theme overrides\n    const position = config.launcher?.position ?? 'bottom-left';\n    const isLeftSidebar = position === 'bottom-left' || position === 'top-left';\n    const overlayZIndex = config.launcher?.zIndex ?? DEFAULT_OVERLAY_Z_INDEX;\n\n    // Default values based on mode\n    let defaultPanelBorder = (sidebarMode || shouldGoFullscreen) ? 'none' : '1px solid var(--persona-border)';\n    let defaultPanelShadow = shouldGoFullscreen\n      ? 'none'\n      : sidebarMode\n        ? (isLeftSidebar ? 'var(--persona-palette-shadows-sidebar-left, 2px 0 12px rgba(0, 0, 0, 0.08))' : 'var(--persona-palette-shadows-sidebar-right, -2px 0 12px rgba(0, 0, 0, 0.08))')\n        : 'var(--persona-palette-shadows-xl, 0 25px 50px -12px rgba(0, 0, 0, 0.25))';\n\n    if (dockedMode && !shouldGoFullscreen) {\n      defaultPanelShadow = 'none';\n      defaultPanelBorder = 'none';\n    }\n    const defaultPanelBorderRadius = (sidebarMode || shouldGoFullscreen)\n      ? '0'\n      : 'var(--persona-panel-radius, var(--persona-radius-xl, 0.75rem))';\n\n    // Apply theme overrides or defaults (components.panel.*)\n    const panelBorder = resolvePanelChrome(panelPartial?.border, defaultPanelBorder);\n    const panelShadow = resolvePanelChrome(panelPartial?.shadow, defaultPanelShadow);\n    const panelBorderRadius = resolvePanelChrome(panelPartial?.borderRadius, defaultPanelBorderRadius);\n\n    // Clearing body.style.cssText below wipes the inline `flex: 1 1 0%` /\n    // `min-height: 0` / `overflow-y: auto` that make the messages area a\n    // scroll container. Between the reset and the mode-specific reapply,\n    // the body's clientHeight == scrollHeight momentarily, so the browser\n    // clamps scrollTop to 0, and a synchronous restore at the end of this\n    // function runs before layout has reflowed, so the write is also\n    // clamped. Defer the restore to the next frame, once the reapplied\n    // styles have produced a scrollable container again.\n    const prevBodyScrollTop = body.scrollTop;\n\n    // Reset all inline styles first to handle mode toggling\n    // This ensures styles don't persist when switching between modes\n    mount.style.cssText = '';\n    wrapper.style.cssText = '';\n    panel.style.cssText = '';\n    container.style.cssText = '';\n    body.style.cssText = '';\n    footer.style.cssText = '';\n\n    // Preserve the event-stream takeover across a layout-mode change. The\n    // cssText reset above wiped the `display: none` that toggleEventStreamOn\n    // set on the messages body, and none of the per-mode reapply branches below\n    // touch `display` — so without this the messages would reappear and stack\n    // above the event panel when the window crosses the fullscreen breakpoint.\n    if (eventStreamVisible) {\n      body.style.display = \"none\";\n    }\n\n    const restoreBodyScrollTop = (): void => {\n      if (prevBodyScrollTop <= 0) return;\n      const ownerWindow = body.ownerDocument.defaultView ?? window;\n      ownerWindow.requestAnimationFrame(() => {\n        if (body.scrollTop === prevBodyScrollTop) return;\n        // If scrollHeight collapsed (content actually shrank), don't fight it\n        const maxScrollTop = body.scrollHeight - body.clientHeight;\n        if (maxScrollTop <= 0) return;\n        body.scrollTop = Math.min(prevBodyScrollTop, maxScrollTop);\n      });\n    };\n    \n    // Mobile fullscreen: fill entire viewport with no radius/shadow/margins\n    if (shouldGoFullscreen) {\n      // Remove position offset classes\n      wrapper.classList.remove(\n        'persona-bottom-6', 'persona-right-6', 'persona-left-6', 'persona-top-6',\n        'persona-bottom-4', 'persona-right-4', 'persona-left-4', 'persona-top-4'\n      );\n\n      // Wrapper: fill entire viewport\n      wrapper.style.cssText = `\n        position: fixed !important;\n        inset: 0 !important;\n        width: 100% !important;\n        height: 100% !important;\n        max-height: 100% !important;\n        margin: 0 !important;\n        padding: 0 !important;\n        display: flex !important;\n        flex-direction: column !important;\n        z-index: ${overlayZIndex} !important;\n        background-color: var(--persona-surface, #ffffff) !important;\n      `;\n\n      // Panel: fill wrapper, no radius/shadow\n      panel.style.cssText = `\n        position: relative !important;\n        display: flex !important;\n        flex-direction: column !important;\n        flex: 1 1 0% !important;\n        width: 100% !important;\n        max-width: 100% !important;\n        height: 100% !important;\n        min-height: 0 !important;\n        margin: 0 !important;\n        padding: 0 !important;\n        box-shadow: none !important;\n        border-radius: 0 !important;\n      `;\n\n      // Container: fill panel, no radius/border\n      container.style.cssText = `\n        display: flex !important;\n        flex-direction: column !important;\n        flex: 1 1 0% !important;\n        width: 100% !important;\n        height: 100% !important;\n        min-height: 0 !important;\n        max-height: 100% !important;\n        overflow: hidden !important;\n        border-radius: 0 !important;\n        border: none !important;\n      `;\n\n      // Body: scrollable messages\n      body.style.flex = '1 1 0%';\n      body.style.minHeight = '0';\n      body.style.overflowY = 'auto';\n\n      // Footer: pinned at bottom\n      footer.style.flexShrink = '0';\n\n      wasMobileFullscreen = true;\n      restoreBodyScrollTop();\n      return; // Skip remaining mode logic\n    }\n\n    // Re-apply panel width/maxWidth from initial setup\n    const launcherWidth = config?.launcher?.width ?? config?.launcherWidth;\n    const width = launcherWidth ?? DEFAULT_FLOATING_LAUNCHER_WIDTH;\n    if (!sidebarMode && !dockedMode) {\n      if (isInlineEmbed && fullHeight) {\n        panel.style.width = \"100%\";\n        panel.style.maxWidth = \"100%\";\n      } else {\n        panel.style.width = width;\n        panel.style.maxWidth = width;\n      }\n    } else if (dockedMode) {\n      const dockReveal = resolveDockConfig(config).reveal;\n      if (dockReveal === \"emerge\") {\n        const dw = resolveDockConfig(config).width;\n        panel.style.width = dw;\n        panel.style.maxWidth = dw;\n      } else {\n        panel.style.width = \"100%\";\n        panel.style.maxWidth = \"100%\";\n      }\n    }\n    applyLauncherArtifactPanelWidth();\n\n    // Apply panel styling\n    // Box-shadow is applied to panel (parent) instead of container to avoid\n    // rendering artifacts when container has overflow:hidden + border-radius\n    // Panel also gets border-radius to make the shadow follow the rounded corners\n    panel.style.boxShadow = panelShadow;\n    panel.style.borderRadius = panelBorderRadius;\n    container.style.border = panelBorder;\n    container.style.borderRadius = panelBorderRadius;\n\n    if (dockedMode && !shouldGoFullscreen && panelPartial?.border === undefined) {\n      container.style.border = 'none';\n      const dockSide = resolveDockConfig(config).side;\n      if (dockSide === 'right') {\n        container.style.borderLeft = '1px solid var(--persona-border)';\n      } else {\n        container.style.borderRight = '1px solid var(--persona-border)';\n      }\n    }\n\n    if (fullHeight) {\n      // Mount container\n      mount.style.display = 'flex';\n      mount.style.flexDirection = 'column';\n      mount.style.height = '100%';\n      mount.style.minHeight = '0';\n      if (isInlineEmbed) {\n        mount.style.width = '100%';\n      }\n      \n      // Wrapper\n      // - Inline embed: needs overflow:hidden to contain the flex layout\n      // - Launcher mode: no overflow:hidden to allow panel's box-shadow to render fully\n      wrapper.style.display = 'flex';\n      wrapper.style.flexDirection = 'column';\n      wrapper.style.flex = '1 1 0%';\n      wrapper.style.minHeight = '0';\n      wrapper.style.maxHeight = '100%';\n      wrapper.style.height = '100%';\n      if (isInlineEmbed) {\n        wrapper.style.overflow = 'hidden';\n      }\n      \n      // Panel\n      panel.style.display = 'flex';\n      panel.style.flexDirection = 'column';\n      panel.style.flex = '1 1 0%';\n      panel.style.minHeight = '0';\n      panel.style.maxHeight = '100%';\n      panel.style.height = '100%';\n      panel.style.overflow = 'hidden';\n      \n      // Main container\n      container.style.display = 'flex';\n      container.style.flexDirection = 'column';\n      container.style.flex = '1 1 0%';\n      container.style.minHeight = '0';\n      container.style.maxHeight = '100%';\n      container.style.overflow = 'hidden';\n      \n      // Body (scrollable messages area)\n      body.style.flex = '1 1 0%';\n      body.style.minHeight = '0';\n      body.style.overflowY = 'auto';\n      \n      // Footer (composer) - should not shrink\n      footer.style.flexShrink = '0';\n    }\n    \n    // Handle positioning classes based on mode\n    // First remove all position classes to reset state\n    wrapper.classList.remove(\n      'persona-bottom-6', 'persona-right-6', 'persona-left-6', 'persona-top-6',\n      'persona-bottom-4', 'persona-right-4', 'persona-left-4', 'persona-top-4'\n    );\n    \n    if (!sidebarMode && !isInlineEmbed && !dockedMode) {\n      // Restore positioning classes when not in sidebar mode (launcher mode only)\n      const positionClasses = positionMap[position as keyof typeof positionMap] ?? positionMap['bottom-right'];\n      positionClasses.split(' ').forEach(cls => wrapper.classList.add(cls));\n    }\n    \n    // Apply sidebar-specific styles\n    if (sidebarMode) {\n      const sidebarWidth = config.launcher?.sidebarWidth ?? '420px';\n      \n      // Wrapper - fixed position, flush with edges\n      wrapper.style.cssText = `\n        position: fixed !important;\n        top: 0 !important;\n        bottom: 0 !important;\n        width: ${sidebarWidth} !important;\n        height: 100vh !important;\n        max-height: 100vh !important;\n        margin: 0 !important;\n        padding: 0 !important;\n        display: flex !important;\n        flex-direction: column !important;\n        z-index: ${overlayZIndex} !important;\n        ${isLeftSidebar ? 'left: 0 !important; right: auto !important;' : 'left: auto !important; right: 0 !important;'}\n      `;\n      \n      // Panel - fill wrapper (override inline width/max-width from panel.ts)\n      // Box-shadow is on panel to avoid rendering artifacts with container's overflow:hidden\n      // Border-radius on panel ensures shadow follows rounded corners\n      panel.style.cssText = `\n        position: relative !important;\n        display: flex !important;\n        flex-direction: column !important;\n        flex: 1 1 0% !important;\n        width: 100% !important;\n        max-width: 100% !important;\n        height: 100% !important;\n        min-height: 0 !important;\n        margin: 0 !important;\n        padding: 0 !important;\n        box-shadow: ${panelShadow} !important;\n        border-radius: ${panelBorderRadius} !important;\n      `;\n      // Force override any inline width/maxWidth that may be set elsewhere\n      panel.style.setProperty('width', '100%', 'important');\n      panel.style.setProperty('max-width', '100%', 'important');\n      \n      // Container - apply configurable styles with sidebar layout\n      // Note: box-shadow is on panel, not container\n      container.style.cssText = `\n        display: flex !important;\n        flex-direction: column !important;\n        flex: 1 1 0% !important;\n        width: 100% !important;\n        height: 100% !important;\n        min-height: 0 !important;\n        max-height: 100% !important;\n        overflow: hidden !important;\n        border-radius: ${panelBorderRadius} !important;\n        border: ${panelBorder} !important;\n      `;\n      \n      // Remove footer border in sidebar mode\n      footer.style.cssText = `\n        flex-shrink: 0 !important;\n        border-top: none !important;\n        padding: 8px 16px 12px 16px !important;\n      `;\n    }\n    \n    // Apply max-height constraints to wrapper to prevent expanding past viewport top\n    // Use both -moz-available (Firefox) and stretch (standard) for cross-browser support\n    // Append to cssText to allow multiple fallback values for the same property\n    // Only apply to launcher mode (not sidebar or inline embed)\n    if (!isInlineEmbed && !dockedMode) {\n      const maxHeightStyles = 'max-height: -moz-available !important; max-height: stretch !important;';\n      const paddingStyles = sidebarMode ? '' : 'padding-top: 1.25em !important;';\n      const zIndexStyles = !sidebarMode\n        ? `z-index: ${config.launcher?.zIndex ?? DEFAULT_OVERLAY_Z_INDEX} !important;`\n        : '';\n      wrapper.style.cssText += maxHeightStyles + paddingStyles + zIndexStyles;\n    }\n\n    restoreBodyScrollTop();\n  };\n  applyFullHeightStyles();\n  // Apply theme variables after applyFullHeightStyles since it resets mount.style.cssText\n  applyThemeVariables(mount, config);\n  applyArtifactLayoutCssVars(mount, config);\n  applyArtifactPaneAppearance(mount, config);\n\n  const destroyCallbacks: Array<() => void> = [];\n  // Clean up the document-level digit-key shortcut listener registered earlier.\n  destroyCallbacks.push(() => {\n    document.removeEventListener(\"keydown\", handleAskUserDigitKey);\n  });\n  // Clear any pending live-region announcement timer on teardown.\n  destroyCallbacks.push(() => {\n    if (announceTimer !== null) clearTimeout(announceTimer);\n  });\n\n  let teardownHostStacking: (() => void) | null = null;\n  let releaseScrollLock: (() => void) | null = null;\n\n  destroyCallbacks.push(() => {\n    teardownHostStacking?.();\n    teardownHostStacking = null;\n    releaseScrollLock?.();\n    releaseScrollLock = null;\n  });\n\n  if (artifactPanelResizeObs) {\n    destroyCallbacks.push(() => {\n      artifactPanelResizeObs?.disconnect();\n      artifactPanelResizeObs = null;\n    });\n  }\n\n  destroyCallbacks.push(() => {\n    artifactResizeUnbind?.();\n    artifactResizeUnbind = null;\n    stopArtifactResizePointer();\n    if (artifactResizeHandle) {\n      artifactResizeHandle.remove();\n      artifactResizeHandle = null;\n    }\n    artifactPaneApi?.element.style.removeProperty(\"width\");\n    artifactPaneApi?.element.style.removeProperty(\"maxWidth\");\n  });\n\n  // Event stream cleanup\n  if (showEventStreamToggle) {\n    destroyCallbacks.push(() => {\n      if (eventStreamRAF !== null) {\n        cancelAnimationFrame(eventStreamRAF);\n        eventStreamRAF = null;\n      }\n      eventStreamView?.destroy();\n      eventStreamView = null;\n      eventStreamBuffer?.destroy();\n      eventStreamBuffer = null;\n      eventStreamStore = null;\n    });\n  }\n\n  // Set up theme observer for auto color scheme detection\n  let cleanupThemeObserver: (() => void) | null = null;\n  const setupThemeObserver = () => {\n    // Clean up existing observer if any\n    if (cleanupThemeObserver) {\n      cleanupThemeObserver();\n      cleanupThemeObserver = null;\n    }\n    // Set up new observer if colorScheme is 'auto'\n    if (config.colorScheme === 'auto') {\n      cleanupThemeObserver = createThemeObserver(() => {\n        // Re-apply theme when color scheme changes\n        applyThemeVariables(mount, config);\n      });\n    }\n  };\n  setupThemeObserver();\n  destroyCallbacks.push(() => {\n    if (cleanupThemeObserver) {\n      cleanupThemeObserver();\n      cleanupThemeObserver = null;\n    }\n  });\n\n  // Release this widget's pending built-in approval listeners + \"Allow once\"\n  // popovers if it's destroyed while an approval is still open. Scoped to this\n  // instance's state, so other widgets on the page are unaffected.\n  destroyCallbacks.push(teardownBuiltInApprovals);\n\n  // Activate the stream-animation plugin for this widget instance. Plugins\n  // with `styles` inject their CSS into the widget root once; plugins with\n  // `onAttach` (e.g., glyph-cycle's MutationObserver for real glyph tick\n  // loops) can register long-lived DOM listeners here. Detach callbacks are\n  // deferred to widget destroy.\n  const streamAnimationConfig = config.features?.streamAnimation;\n  if (streamAnimationConfig?.type && streamAnimationConfig.type !== \"none\") {\n    const plugin = resolveStreamAnimationPlugin(\n      streamAnimationConfig.type,\n      streamAnimationConfig.plugins\n    );\n    if (plugin) {\n      ensurePluginActive(plugin, mount);\n      destroyCallbacks.push(() => detachAllPlugins(mount));\n    }\n  }\n\n  const suggestionsManager = createSuggestions(suggestions);\n  let closeHandler: (() => void) | null = null;\n  let session: AgentWidgetSession;\n\n  // Single render rule for the suggestions row, shared by the message-change,\n  // initial-paint, and config-update paths: agent-pushed `suggest_replies`\n  // chips win when the latest-turn rule yields any (last suggest_replies tool\n  // message with no user message after it); otherwise static config chips\n  // keep their before-first-user-message behavior. Config updates MUST route\n  // through here too: re-rendering with only `config.suggestionChips` would\n  // drop a live agent chip row until the next message change.\n  const renderSuggestions = (messages?: AgentWidgetMessage[]) => {\n    if (!session) return;\n    const current = messages ?? session.getMessages();\n    const agentChips =\n      config.features?.suggestReplies?.enabled !== false\n        ? latestAgentSuggestions(current)\n        : null;\n    if (agentChips) {\n      suggestionsManager.render(\n        agentChips,\n        session,\n        textarea,\n        current,\n        config.suggestionChipsConfig,\n        { agentPushed: true }\n      );\n    } else if (current.some((msg) => msg.role === \"user\")) {\n      // Hide suggestions once a user message exists.\n      suggestionsManager.render([], session, textarea, current);\n    } else {\n      suggestionsManager.render(\n        config.suggestionChips,\n        session,\n        textarea,\n        current,\n        config.suggestionChipsConfig\n      );\n    }\n  };\n  let isStreaming = false;\n  const messageCache = createMessageCache();\n  // Tracks the last fingerprint we rendered a plugin-rendered ask_user_question\n  // bubble for, per message id. Lets us skip unnecessary rebuilds across\n  // re-renders so user state inside the plugin (typed text, focus) survives.\n  const lastAskBubbleFingerprint = new Map<string, string>();\n  // Same idea for component-directive bubbles (registered custom components\n  // rendered from JSON directives). The renderer's element is injected into the\n  // live DOM post-morph so its event listeners survive; this map gates the\n  // expensive rebuild on fingerprint change so user state inside the rendered\n  // component (e.g. partially-filled form inputs) is not wiped on every pass.\n  const lastComponentDirectiveFingerprint = new Map<string, string>();\n  // Same idea for plugin-rendered approval bubbles (`renderApproval`). The\n  // custom element is injected into the live DOM post-morph so its event\n  // listeners (Approve/Deny, an expandable parameters accordion, etc.) survive;\n  // this map gates the rebuild on fingerprint change so interactive state (e.g.\n  // a collapsed accordion) is not reset on every pass while the approval is\n  // pending.\n  const lastApprovalBubbleFingerprint = new Map<string, string>();\n  let configVersion = 0;\n  // Whether the markdown parsers (marked + dompurify) were already loaded when\n  // this widget mounted. False only on the IIFE/CDN lazy path before the\n  // `markdown-parsers.js` chunk resolves; in that window messages render as\n  // escaped plain text and are re-rendered once the chunk lands (see below).\n  const markdownReadyAtInit = getMarkdownParsersSync() !== null;\n  const autoFollow = createFollowStateController();\n  let lastScrollTop = 0;\n  let scrollRAF: number | null = null;\n  let isAutoScrolling = false;\n  let hasPendingAutoScroll = false;\n  // Messages that arrived while the user was away from the latest content;\n  // shown as a badge on the scroll-to-bottom affordance.\n  let newMessagesSincePause = 0;\n  // Live anchor-top state for the current turn (null when not anchored).\n  let anchorState: {\n    initialSpacerHeight: number;\n    contentHeightAtAnchor: number;\n    spacerHeight: number;\n  } | null = null;\n  let anchorRAF: number | null = null;\n  // Seeded send-detection so restored history doesn't read as a fresh send.\n  let scrollSendSeeded = false;\n  let suppressScrollSend = false;\n  let lastSentUserMessageId: string | null = null;\n  // anchor-top no-anchor fallback: anchor-top pins on a USER send. An assistant\n  // message that streams when NO user send has anchored the conversation yet\n  // (first-load / proactive-first streaming) has nothing to anchor to, so it\n  // falls back to follow-to-bottom — otherwise its content streams in\n  // off-screen. `true` by default (nothing anchored yet); a user send clears it\n  // and the anchor takes over. Inert in follow/none mode (see\n  // `isFollowEffective`).\n  let followFallbackActive = true;\n  // True once a user send has anchored the current conversation (until the chat\n  // is cleared). While anchored, follow-on assistant content — the response, a\n  // multi-part reply, an injected embed (tweet/image), a tool result — stays\n  // pinned and never re-arms the fallback, so a late-loading embed can't yank\n  // the viewport down to the bottom.\n  let currentTurnAnchored = false;\n  // Dedupes assistant-turn detection across token-by-token re-renders.\n  let lastHandledAssistantId: string | null = null;\n\n  // Scroll events caused by layout, scroll anchoring, and smooth-scroll\n  // easing can easily move by a couple pixels. Keep manual wheel intent\n  // responsive, but require a slightly larger raw scroll delta before we\n  // treat a plain scroll event as the user breaking away.\n  const USER_SCROLL_THRESHOLD = 4;\n  const BOTTOM_THRESHOLD = 24;\n  const AUTO_SCROLL_SNAP_THRESHOLD = 80;\n  const messageState = new Map<\n    string,\n    { streaming?: boolean; role: AgentWidgetMessage[\"role\"] }\n  >();\n  const voiceState = {\n    active: false,\n    manuallyDeactivated: false,\n    lastUserMessageWasVoice: false,\n    lastUserMessageId: null as string | null\n  };\n  const voiceAutoResumeMode = config.voiceRecognition?.autoResume ?? false;\n  const emitVoiceState = (source: AgentWidgetVoiceStateEvent[\"source\"]) => {\n    eventBus.emit(\"voice:state\", {\n      active: voiceState.active,\n      source,\n      timestamp: Date.now()\n    });\n  };\n  const persistVoiceMetadata = () => {\n    updateSessionMetadata((prev) => ({\n      ...prev,\n      voiceState: {\n        active: voiceState.active,\n        timestamp: Date.now(),\n        manuallyDeactivated: voiceState.manuallyDeactivated\n      }\n    }));\n  };\n  const maybeRestoreVoiceFromMetadata = () => {\n    if (config.voiceRecognition?.enabled === false) return;\n    const rawVoiceState = ensureRecord((persistentMetadata as any).voiceState);\n    const wasActive = Boolean(rawVoiceState.active);\n    const timestamp = Number(rawVoiceState.timestamp ?? 0);\n    voiceState.manuallyDeactivated = Boolean(rawVoiceState.manuallyDeactivated);\n    if (wasActive && Date.now() - timestamp < VOICE_STATE_RESTORE_WINDOW) {\n      setTimeout(() => {\n        if (!voiceState.active) {\n          voiceState.manuallyDeactivated = false;\n          if (config.voiceRecognition?.provider?.type === 'runtype') {\n            session.toggleVoice().then(() => {\n              voiceState.active = session.isVoiceActive();\n              emitVoiceState(\"restore\");\n              if (session.isVoiceActive()) applyRuntypeMicRecordingStyles();\n            });\n          } else {\n            startVoiceRecognition(\"restore\");\n          }\n        }\n      }, 1000);\n    }\n  };\n\n  const getMessagesForPersistence = () =>\n    session \n      ? stripStreamingFromMessages(session.getMessages()).filter(msg => !(msg as any).__skipPersist)\n      : [];\n\n  function persistState(messagesOverride?: AgentWidgetMessage[]) {\n    if (!storageAdapter?.save) return;\n\n    // Allow saving even if session doesn't exist yet (for metadata during init)\n    const messages = messagesOverride\n      ? stripStreamingFromMessages(messagesOverride)\n      : session\n        ? getMessagesForPersistence()\n        : [];\n\n    const payload = {\n      messages,\n      metadata: persistentMetadata,\n      artifacts: lastArtifactsState.artifacts,\n      selectedArtifactId: lastArtifactsState.selectedId\n    };\n    try {\n      const result = storageAdapter.save(payload);\n      if (result instanceof Promise) {\n        result.catch((error) => {\n          if (typeof console !== \"undefined\") {\n            // eslint-disable-next-line no-console\n            console.error(\"[AgentWidget] Failed to persist state:\", error);\n          }\n        });\n      }\n    } catch (error) {\n      if (typeof console !== \"undefined\") {\n        // eslint-disable-next-line no-console\n        console.error(\"[AgentWidget] Failed to persist state:\", error);\n      }\n    }\n  }\n\n  // Track ongoing smooth scroll animation\n  let smoothScrollRAF: number | null = null;\n\n  // Get the scrollable container using its unique ID\n  const getScrollableContainer = (): HTMLElement => {\n    // Use the unique ID for reliable selection\n    const scrollable = wrapper.querySelector('#persona-scroll-container') as HTMLElement;\n    // Fallback to body if ID not found (shouldn't happen, but safe fallback)\n    return scrollable || body;\n  };\n\n  const cancelSmoothScroll = () => {\n    if (smoothScrollRAF !== null) {\n      cancelAnimationFrame(smoothScrollRAF);\n      smoothScrollRAF = null;\n    }\n    isAutoScrolling = false;\n  };\n\n  const cancelAutoScroll = () => {\n    if (scrollRAF !== null) {\n      cancelAnimationFrame(scrollRAF);\n      scrollRAF = null;\n    }\n    hasPendingAutoScroll = false;\n    cancelSmoothScroll();\n  };\n\n  // True when a response is streaming in below the reader's current position,\n  // i.e. content is arriving out of view. Drives the \"still streaming\" hint on\n  // the scroll-to-bottom affordance (Principle 8: show what's happening out of\n  // view). In anchor-top mode this is gated behind `showActivityWhilePinned`\n  // so the historical \"silent while pinned\" behavior is preserved by default.\n  const isStreamingOutOfView = () =>\n    isStreaming &&\n    isAwayFromLatest() &&\n    (getScrollMode() !== \"anchor-top\" || isActivityWhilePinnedEnabled());\n\n  const updateScrollToBottomCountBadge = () => {\n    const base = getScrollToBottomLabel() || \"Jump to latest\";\n    const streamingBelow = isStreamingOutOfView();\n    scrollToBottomButton.toggleAttribute(\n      \"data-persona-scroll-to-bottom-streaming\",\n      streamingBelow\n    );\n    if (newMessagesSincePause > 0) {\n      scrollToBottomCount.textContent = String(newMessagesSincePause);\n      scrollToBottomCount.style.display = \"\";\n      scrollToBottomButton.setAttribute(\n        \"aria-label\",\n        `${base} (${newMessagesSincePause} new)`\n      );\n    } else {\n      scrollToBottomCount.textContent = \"\";\n      scrollToBottomCount.style.display = \"none\";\n      scrollToBottomButton.setAttribute(\n        \"aria-label\",\n        streamingBelow ? `${base} (response streaming below)` : base\n      );\n    }\n  };\n\n  const resetNewMessagesCount = () => {\n    if (newMessagesSincePause === 0) return;\n    newMessagesSincePause = 0;\n    updateScrollToBottomCountBadge();\n  };\n\n  // Whether the user is currently away from the latest content: drives both\n  // the scroll-to-bottom affordance and the new-messages badge. When following\n  // the bottom (follow mode, or a no-anchor anchor-top fallback turn) that's\n  // \"auto-follow paused\"; otherwise it's simply \"not near the bottom\".\n  const isAwayFromLatest = () =>\n    isFollowEffective()\n      ? !autoFollow.isFollowing()\n      : !isElementNearBottom(body, BOTTOM_THRESHOLD);\n\n  const syncScrollToBottomButton = () => {\n    if (!isScrollToBottomEnabled() || eventStreamVisible) {\n      if (scrollToBottomButton.parentNode) {\n        scrollToBottomButton.remove();\n      }\n      scrollToBottomButton.style.display = \"none\";\n      return;\n    }\n    if (scrollToBottomButton.parentNode !== container) {\n      container.appendChild(scrollToBottomButton);\n    }\n    updateScrollToBottomButtonOffset();\n    const hasOverflow = getScrollBottomOffset(body) > 0;\n    const show = hasOverflow && isAwayFromLatest();\n    if (!show) {\n      resetNewMessagesCount();\n    } else {\n      // Refresh the streaming-below hint while the affordance is visible.\n      updateScrollToBottomCountBadge();\n    }\n    scrollToBottomButton.style.display = show ? \"\" : \"none\";\n  };\n\n  const pauseAutoScroll = () => {\n    if (!autoFollow.pause()) return;\n    cancelAutoScroll();\n    syncScrollToBottomButton();\n  };\n\n  const resumeAutoScroll = () => {\n    autoFollow.resume();\n    resetNewMessagesCount();\n    syncScrollToBottomButton();\n  };\n\n  const scheduleAutoScroll = (force = false) => {\n    // Auto-follow applies in \"follow\" mode, and in anchor-top only for a\n    // no-anchor fallback turn (see `isFollowEffective`). Anchored anchor-top\n    // turns and \"none\" never chase the bottom during streaming.\n    if (!isFollowEffective()) return;\n\n    if (!autoFollow.isFollowing()) return;\n\n    if (!force && !isStreaming) return;\n\n    // Only cancel the pending schedule rAF: keep the ongoing smooth scroll\n    // animation alive so isAutoScrolling stays true.  This prevents scroll\n    // events fired by DOM morphing (between cancel and the next rAF) from\n    // being misinterpreted as user-initiated upward scrolls that would\n    // permanently pause auto-follow during streaming.\n    // smoothScrollToBottom() already calls cancelSmoothScroll() internally\n    // before starting its new animation.\n    if (scrollRAF !== null) {\n      cancelAnimationFrame(scrollRAF);\n      scrollRAF = null;\n    }\n\n    // Treat the render -> next-rAF window as programmatic scrolling too.\n    // This prevents layout/scroll-anchoring scroll events fired before the\n    // actual smooth scroll starts from being misread as user intent.\n    hasPendingAutoScroll = true;\n    scrollRAF = requestAnimationFrame(() => {\n      scrollRAF = null;\n      hasPendingAutoScroll = false;\n      if (!autoFollow.isFollowing()) return;\n      smoothScrollToBottom(getScrollableContainer(), force ? 220 : 140);\n    });\n  };\n\n  // Generic eased scroll animation. `resolveTarget` is re-read every frame so\n  // a moving target (the bottom of a streaming transcript) stays accurate;\n  // `shouldContinue` lets the caller cancel mid-flight (e.g. when auto-follow\n  // pauses). Scroll events emitted by the animation are masked from the\n  // user-intent detector via `isAutoScrolling`.\n  const animateScrollTo = (\n    element: HTMLElement,\n    resolveTarget: () => number,\n    duration: number,\n    shouldContinue: () => boolean = () => true\n  ) => {\n    const start = element.scrollTop;\n    let target = resolveTarget();\n    let distance = target - start;\n\n    // Cancel any ongoing smooth scroll animation\n    cancelSmoothScroll();\n\n    // Nothing to scroll: land exactly on target and skip the rAF loop. Avoids a\n    // no-op animation when already in place (e.g. anchoring with zero overflow),\n    // which also keeps environments with a synchronous rAF from spinning.\n    if (Math.abs(distance) < 1) {\n      isAutoScrolling = true;\n      element.scrollTop = target;\n      lastScrollTop = element.scrollTop;\n      isAutoScrolling = false;\n      return;\n    }\n\n    const startTime = performance.now();\n    isAutoScrolling = true;\n\n    // Easing function: ease-out cubic for smooth deceleration\n    const easeOutCubic = (t: number): number => {\n      return 1 - Math.pow(1 - t, 3);\n    };\n\n    const animate = (currentTime: number) => {\n      if (!shouldContinue()) {\n        cancelSmoothScroll();\n        return;\n      }\n\n      // Recalculate target each frame in case scrollHeight changed\n      const currentTarget = resolveTarget();\n      if (currentTarget !== target) {\n        target = currentTarget;\n        distance = target - start;\n      }\n\n      const elapsed = currentTime - startTime;\n      const progress = Math.min(elapsed / duration, 1);\n      const eased = easeOutCubic(progress);\n\n      const currentScroll = start + distance * eased;\n      element.scrollTop = currentScroll;\n      lastScrollTop = element.scrollTop;\n\n      if (progress < 1) {\n        smoothScrollRAF = requestAnimationFrame(animate);\n      } else {\n        // Ensure we end exactly at the target\n        element.scrollTop = target;\n        lastScrollTop = element.scrollTop;\n        smoothScrollRAF = null;\n        isAutoScrolling = false;\n      }\n    };\n\n    smoothScrollRAF = requestAnimationFrame(animate);\n  };\n\n  // Custom smooth scroll animation with easing\n  const smoothScrollToBottom = (element: HTMLElement, duration = 500) => {\n    const distance = getScrollBottomOffset(element) - element.scrollTop;\n\n    // If already at bottom or very close, skip animation to prevent glitch\n    if (Math.abs(distance) < 1) {\n      lastScrollTop = element.scrollTop;\n      return;\n    }\n\n    // If the transcript has fallen noticeably behind, catch up immediately\n    // instead of easing over multiple frames. This keeps fast streaming /\n    // bursty tool and reasoning updates pinned to the bottom.\n    if (Math.abs(distance) >= AUTO_SCROLL_SNAP_THRESHOLD) {\n      cancelSmoothScroll();\n      isAutoScrolling = true;\n      element.scrollTop = getScrollBottomOffset(element);\n      lastScrollTop = element.scrollTop;\n      isAutoScrolling = false;\n      return;\n    }\n\n    animateScrollTo(\n      element,\n      () => getScrollBottomOffset(element),\n      duration,\n      () => autoFollow.isFollowing()\n    );\n  };\n\n  // Instant jump used for initial mount / panel open in non-follow scroll\n  // modes (where scheduleAutoScroll is inert).\n  const jumpToBottomInstant = () => {\n    const element = getScrollableContainer();\n    isAutoScrolling = true;\n    element.scrollTop = getScrollBottomOffset(element);\n    lastScrollTop = element.scrollTop;\n    isAutoScrolling = false;\n    syncScrollToBottomButton();\n  };\n\n  // Walk offsetParents up to `body` (the positioned scroll ancestor) to get a\n  // node's top relative to the scroll content. offsetTop avoids skew from any\n  // in-flight entrance transforms. Mirrors the anchor-top geometry.\n  const offsetTopWithinBody = (el: HTMLElement): number => {\n    let top = 0;\n    let node: HTMLElement | null = el;\n    while (node && node !== body) {\n      top += node.offsetTop;\n      node = node.offsetParent as HTMLElement | null;\n    }\n    return top;\n  };\n\n  // Principle 11: reopen where the reader left off. When `restorePosition` is\n  // \"last-user-turn\" and there is pre-existing history, land with the last user\n  // message pinned near the top of the viewport instead of jumping to the\n  // absolute bottom. Returns true when it handled positioning. Opt-in; the\n  // default (\"bottom\") returns false so callers fall back to jump-to-bottom.\n  const restoreScrollPosition = (): boolean => {\n    if (getScrollRestorePosition() !== \"last-user-turn\") return false;\n    const messages = session?.getMessages() ?? [];\n    // A *restore* only makes sense when reopening existing history; a fresh\n    // (empty or single-turn) conversation should still start at the latest.\n    if (messages.length < 2) return false;\n    const lastUser = [...messages].reverse().find((m) => m.role === \"user\");\n    if (!lastUser) return false;\n    const escapedId =\n      typeof CSS !== \"undefined\" && typeof CSS.escape === \"function\"\n        ? CSS.escape(lastUser.id)\n        : lastUser.id.replace(/\\\\/g, \"\\\\\\\\\").replace(/\"/g, '\\\\\"');\n    const bubble = body.querySelector<HTMLElement>(\n      `[data-message-id=\"${escapedId}\"]`\n    );\n    if (!bubble) return false;\n    const target = Math.min(\n      Math.max(0, offsetTopWithinBody(bubble) - getAnchorTopOffset()),\n      getScrollBottomOffset(body)\n    );\n    isAutoScrolling = true;\n    body.scrollTop = target;\n    lastScrollTop = body.scrollTop;\n    isAutoScrolling = false;\n    // In follow mode, deliberately landing above the bottom means we are not\n    // following; pause so the first streamed token doesn't yank the reader\n    // down. (In anchor-top/none there is no follow state to manage.)\n    if (\n      getScrollMode() === \"follow\" &&\n      !isElementNearBottom(body, BOTTOM_THRESHOLD)\n    ) {\n      autoFollow.pause();\n    }\n    syncScrollToBottomButton();\n    return true;\n  };\n\n  const setAnchorSpacerHeight = (height: number) => {\n    anchorSpacer.style.height = `${Math.max(0, Math.round(height))}px`;\n    if (anchorState) {\n      anchorState.spacerHeight = Math.max(0, height);\n    }\n  };\n\n  const resetAnchorState = () => {\n    if (anchorRAF !== null) {\n      cancelAnimationFrame(anchorRAF);\n      anchorRAF = null;\n    }\n    // Also stop an in-flight anchor scroll animation: otherwise its\n    // remaining frames keep easing scrollTop toward the stale anchor target\n    // after a jump-to-latest, chat clear, or scroll-mode change.\n    cancelSmoothScroll();\n    anchorState = null;\n    anchorSpacer.style.height = \"0px\";\n  };\n\n  // Anchor-top mode: scroll the just-sent user message to rest\n  // `anchorTopOffset` px below the viewport top and hold it there while the\n  // response streams in beneath it. Deferred one frame so the message bubble\n  // has been rendered and laid out.\n  const scheduleAnchorToUserMessage = (messageId: string) => {\n    if (anchorRAF !== null) {\n      cancelAnimationFrame(anchorRAF);\n    }\n    anchorRAF = requestAnimationFrame(() => {\n      anchorRAF = null;\n      const escapedId =\n        typeof CSS !== \"undefined\" && typeof CSS.escape === \"function\"\n          ? CSS.escape(messageId)\n          : messageId.replace(/\\\\/g, \"\\\\\\\\\").replace(/\"/g, '\\\\\"');\n      const bubble = body.querySelector<HTMLElement>(\n        `[data-message-id=\"${escapedId}\"]`\n      );\n      if (!bubble) return;\n\n      // Bubble top relative to the scroll content. offsetTop is used instead\n      // of getBoundingClientRect so in-flight entrance animations (transforms)\n      // can't skew the target.\n      const anchorOffsetTop = offsetTopWithinBody(bubble);\n\n      const previousSpacerHeight = anchorState?.spacerHeight ?? 0;\n      const contentHeight = body.scrollHeight - previousSpacerHeight;\n      const { targetScrollTop, spacerHeight } = computeAnchorScrollState({\n        anchorOffsetTop,\n        topOffset: getAnchorTopOffset(),\n        viewportHeight: body.clientHeight,\n        contentHeight\n      });\n\n      anchorState = {\n        initialSpacerHeight: spacerHeight,\n        contentHeightAtAnchor: contentHeight,\n        spacerHeight\n      };\n      setAnchorSpacerHeight(spacerHeight);\n      animateScrollTo(body, () => targetScrollTop, 220);\n    });\n  };\n\n  // Content growth handler (ResizeObserver-driven). In follow mode this is\n  // what keeps the transcript pinned when content grows *without* a render\n  // event: images/embeds finishing loading mid-stream, fonts swapping,\n  // the panel or composer resizing. In anchor-top mode it gives spacer room\n  // back as the streamed response grows (shrink-only, so total scroll height\n  // stays constant and nothing jumps).\n  const handleContentResize = () => {\n    if (isFollowEffective()) {\n      if (!autoFollow.isFollowing()) return;\n      if (isElementNearBottom(body, 1)) return;\n      scheduleAutoScroll(!isStreaming);\n      return;\n    }\n    if (anchorState && anchorState.initialSpacerHeight > 0) {\n      const currentContentHeight = body.scrollHeight - anchorState.spacerHeight;\n      const next = computeShrunkSpacerHeight({\n        initialSpacerHeight: anchorState.initialSpacerHeight,\n        contentHeightAtAnchor: anchorState.contentHeightAtAnchor,\n        currentContentHeight\n      });\n      if (next !== anchorState.spacerHeight) {\n        setAnchorSpacerHeight(next);\n      }\n    }\n    syncScrollToBottomButton();\n  };\n\n  // Reacts to a user message the user just sent (seeded so restored history\n  // never triggers it). Follow mode re-sticks to the bottom even if the user\n  // had scrolled up: sending is an unambiguous \"take me to the latest\"\n  // signal. Anchor-top mode pins the sent message near the viewport top.\n  const handleUserMessageSent = (messageId: string) => {\n    const mode = getScrollMode();\n    if (mode === \"follow\") {\n      resumeAutoScroll();\n      scheduleAutoScroll(true);\n    } else if (mode === \"anchor-top\") {\n      // A real anchor now drives the conversation: disarm the no-anchor\n      // fallback. Every follow-on assistant message stays anchored until the\n      // next user send.\n      followFallbackActive = false;\n      currentTurnAnchored = true;\n      scheduleAnchorToUserMessage(messageId);\n    }\n  };\n\n  // Reacts to a new assistant message that arrived without a fresh user send.\n  // Only meaningful in anchor-top. While the conversation is anchored (a user\n  // has sent at least once), follow-on assistant content — the response, a\n  // multi-part reply, an injected embed, a tool result — keeps the anchor so a\n  // late-loading embed never yanks the viewport. Only when nothing has anchored\n  // yet (first-load / proactive-first streaming) does it fall back to\n  // follow-to-bottom so the content isn't stranded off-screen.\n  const handleAssistantTurnStarted = () => {\n    if (getScrollMode() !== \"anchor-top\") return;\n    if (currentTurnAnchored) {\n      followFallbackActive = false;\n      return;\n    }\n    followFallbackActive = true;\n    resetAnchorState();\n    resumeAutoScroll();\n    scheduleAutoScroll(true);\n  };\n\n  const trackMessages = (messages: AgentWidgetMessage[]) => {\n    const nextState = new Map<\n      string,\n      { streaming?: boolean; role: AgentWidgetMessage[\"role\"] }\n    >();\n\n    messages.forEach((message) => {\n      const previous = messageState.get(message.id);\n      nextState.set(message.id, {\n        streaming: message.streaming,\n        role: message.role\n      });\n\n      if (!previous && message.role === \"assistant\") {\n        eventBus.emit(\"assistant:message\", message);\n        // Count messages the user hasn't seen for the scroll-to-bottom badge.\n        // Skipped in anchor-top (the user is already reading the latest turn\n        // from its top, so a \"new\" count would normally mislead) and during\n        // history hydration (restored messages aren't \"missed\"). When\n        // `showActivityWhilePinned` is opted in, anchor-top *does* count so the\n        // reader is told content is arriving offscreen below (Principle 8).\n        if (\n          !suppressScrollSend &&\n          (getScrollMode() !== \"anchor-top\" || isActivityWhilePinnedEnabled()) &&\n          isAwayFromLatest()\n        ) {\n          newMessagesSincePause += 1;\n          updateScrollToBottomCountBadge();\n          syncScrollToBottomButton();\n          announce(\n            newMessagesSincePause === 1\n              ? \"1 new message below.\"\n              : `${newMessagesSincePause} new messages below.`\n          );\n        }\n      }\n\n      if (\n        message.role === \"assistant\" &&\n        previous?.streaming &&\n        message.streaming === false\n      ) {\n        eventBus.emit(\"assistant:complete\", message);\n      }\n\n      // Emit approval events\n      if (message.variant === \"approval\" && message.approval) {\n        if (!previous) {\n          eventBus.emit(\"approval:requested\", { approval: message.approval, message });\n        } else if (message.approval.status !== \"pending\") {\n          eventBus.emit(\"approval:resolved\", { approval: message.approval, decision: message.approval.status });\n        }\n      }\n    });\n\n    messageState.clear();\n    nextState.forEach((value, key) => {\n      messageState.set(key, value);\n    });\n  };\n\n\n  // Message rendering with plugin support (implementation)\n  const renderMessagesWithPluginsImpl = (\n    container: HTMLElement,\n    messages: AgentWidgetMessage[],\n    transform: MessageTransform\n  ) => {\n    // Build new content in a temporary container for morphing\n    const tempContainer = document.createElement(\"div\");\n\n    // Create inline loading indicator renderer using priority chain: plugin -> config -> default\n    const getInlineLoadingIndicatorRenderer = (): LoadingIndicatorRenderer | undefined => {\n      // Check if any plugin has renderLoadingIndicator\n      const loadingPlugin = plugins.find(p => p.renderLoadingIndicator);\n      if (loadingPlugin?.renderLoadingIndicator) {\n        return loadingPlugin.renderLoadingIndicator;\n      }\n\n      // Check if config has loadingIndicator.render\n      if (config.loadingIndicator?.render) {\n        return config.loadingIndicator.render;\n      }\n\n      // Return undefined to use default in createStandardBubble\n      return undefined;\n    };\n\n    const inlineLoadingRenderer = getInlineLoadingIndicatorRenderer();\n    const appendRenderedValue = (\n      containerEl: HTMLElement,\n      value: HTMLElement | string | null | undefined\n    ): boolean => {\n      if (value == null) return false;\n      if (typeof value === \"string\") {\n        containerEl.textContent = value;\n        return true;\n      }\n      containerEl.appendChild(value);\n      return true;\n    };\n\n    // Track active message IDs for cache pruning\n    const activeMessageIds = new Set<string>();\n    // Track ask_user_question tool-call ids whose bubbles were rendered this\n    // pass: used to prune stale sheets from the composer overlay afterward.\n    const liveAskToolIds = new Set<string>();\n\n    // Plugins that render `ask_user_question` typically attach DOM listeners\n    // directly to their buttons. The wrapper cache uses `cloneNode(true)` and\n    // idiomorph inserts new nodes via `document.importNode`: both strip\n    // listeners. For plugin-handled ask messages we therefore append an empty\n    // stub during the morph pass and hydrate the live plugin bubble into the\n    // morphed wrapper afterward (see post-morph loop below). The stub carries\n    // `data-preserve-runtime` so subsequent passes leave the live wrapper\n    // (with its listener-bearing bubble) untouched.\n    const hasAskPlugin = plugins.some((p) => p.renderAskUserQuestion);\n    type AskPluginHydrate = {\n      messageId: string;\n      fingerprint: string;\n      bubble: HTMLElement | null;\n    };\n    const askPluginHydrate: AskPluginHydrate[] = [];\n\n    // Component-directive bubbles use the same stub-and-hydrate pattern as\n    // ask_user_question plugins: the renderer's HTMLElement is built live and\n    // injected into the morphed wrapper afterward, so listeners attached via\n    // `addEventListener` (e.g. form `submit` handlers) survive transcript\n    // morphs. `bubble: null` means the fingerprint matched a previous pass and\n    // the live wrapper is reused as-is.\n    type ComponentDirectiveHydrate = {\n      messageId: string;\n      fingerprint: string;\n      bubble: HTMLElement | null;\n    };\n    const componentDirectiveHydrate: ComponentDirectiveHydrate[] = [];\n    const componentStreamingEnabled = config.enableComponentStreaming !== false;\n\n    // Plugin-rendered approval bubbles use the same stub-and-hydrate pattern:\n    // `renderApproval` may attach listeners (the built-in bubble resolves via\n    // delegation on `messagesWrapper`, but a custom element owns its own\n    // interactivity), and idiomorph imports nodes via `document.importNode`,\n    // which strips them. So we build the live element, append a stub during\n    // morph, and inject the live element afterward.\n    // The built-in approval renderer is always available (as a fallback plugin),\n    // so every approval flows through the stub-and-hydrate path whenever\n    // approvals are enabled — a user `renderApproval` plugin just overrides it.\n    const hasApprovalPlugin = config.approval !== false;\n    type ApprovalPluginHydrate = {\n      messageId: string;\n      fingerprint: string;\n      bubble: HTMLElement | null;\n    };\n    const approvalPluginHydrate: ApprovalPluginHydrate[] = [];\n\n    messages.forEach((message) => {\n      activeMessageIds.add(message.id);\n\n      const askWithPlugin = hasAskPlugin && isAskUserQuestionMessage(message);\n      const approvalWithPlugin =\n        hasApprovalPlugin && message.variant === \"approval\" && !!message.approval;\n      const hasDirectiveBubble =\n        !askWithPlugin &&\n        message.role === \"assistant\" &&\n        !message.variant &&\n        componentStreamingEnabled &&\n        hasComponentDirective(message);\n\n      // If a message stops being an approval-plugin bubble, strip\n      // `data-preserve-runtime` so the next morph can replace the live wrapper.\n      if (!approvalWithPlugin && lastApprovalBubbleFingerprint.has(message.id)) {\n        const existing = container.querySelector<HTMLElement>(`#wrapper-${message.id}`);\n        existing?.removeAttribute(\"data-preserve-runtime\");\n        lastApprovalBubbleFingerprint.delete(message.id);\n      }\n\n      // If a message previously rendered as a directive bubble but no longer\n      // does (e.g. content was rewritten), strip `data-preserve-runtime` from\n      // the live wrapper so the next morph can replace it.\n      if (!hasDirectiveBubble && lastComponentDirectiveFingerprint.has(message.id)) {\n        const existing = container.querySelector<HTMLElement>(`#wrapper-${message.id}`);\n        existing?.removeAttribute(\"data-preserve-runtime\");\n        lastComponentDirectiveFingerprint.delete(message.id);\n      }\n\n      // Fingerprint cache: skip re-rendering unchanged messages. Append the\n      // ask-user-question answered/answers state so flipping `askUserQuestionAnswered`\n      // (or accumulating answers) busts both the wrapper cache and the plugin's\n      // `lastAskBubbleFingerprint` check, forcing a re-render of the review UX.\n      const askMeta = isAskUserQuestionMessage(message)\n        ? `:${message.agentMetadata?.askUserQuestionAnswered ? \"a\" : \"u\"}:${\n            message.agentMetadata?.askUserQuestionAnswers\n              ? Object.keys(message.agentMetadata.askUserQuestionAnswers).length\n              : 0\n          }`\n        : \"\";\n      const fingerprint = computeMessageFingerprint(message, configVersion) + askMeta;\n      const cachedWrapper = (askWithPlugin || approvalWithPlugin || hasDirectiveBubble)\n        ? null\n        : getCachedWrapper(messageCache, message.id, fingerprint);\n      if (cachedWrapper) {\n        tempContainer.appendChild(cachedWrapper.cloneNode(true));\n        // Keep the overlay sheet alive only while the server is actively\n        // waiting on the user (awaitingLocalTool === true). Before step_await\n        // fires, or after the answer resumes the flow, omit from\n        // liveAskToolIds so the prune loop below removes any stale DOM sheet.\n        // Guards against lingering skeleton sheets from tool_start events\n        // that never get a matching step_await (e.g. LLM-hallucinated trailing\n        // ask_user_question calls at end-of-turn).\n        if (\n          isAskUserQuestionMessage(message) &&\n          message.toolCall?.id &&\n          message.agentMetadata?.awaitingLocalTool === true &&\n          !message.agentMetadata?.askUserQuestionAnswered\n        ) {\n          liveAskToolIds.add(message.toolCall.id);\n          ensureAskUserQuestionSheet(message, config, panelElements.composerOverlay);\n        }\n        return;\n      }\n\n      let bubble: HTMLElement | null = null;\n\n      // Try plugins first\n      const matchingPlugin = plugins.find((p) => {\n        if (message.variant === \"reasoning\" && p.renderReasoning) {\n          return true;\n        }\n        if (message.variant === \"tool\" && p.renderToolCall) {\n          return true;\n        }\n        // Approval plugins are handled via the stub-and-hydrate path below\n        // (see `approvalWithPlugin`), not this inline morph path, so their\n        // listeners survive, so they are intentionally excluded here.\n        if (!message.variant && p.renderMessage) {\n          return true;\n        }\n        return false;\n      });\n\n      // Get message layout config\n      const messageLayoutConfig = config.layout?.messages;\n\n      // ask_user_question has two rendering modes while waiting for an answer:\n      //   1. Plugin `renderAskUserQuestion`: returns an inline transcript\n      //      element with its own UI; the composer-overlay sheet is suppressed.\n      //   2. Built-in composer-overlay answer-pill sheet: no transcript stub.\n      // Plugins win when they return a non-null element; otherwise fall\n      // through to the built-in overlay.\n      //\n      // Once answered, the original tool message is suppressed entirely from\n      // the transcript. `session.resolveAskUserQuestion` injects one assistant\n      // bubble per question and one user bubble per answer (skipped questions\n      // become an italic `*Skipped*` user bubble), so the transcript reads\n      // like a normal Q→A conversation. Plugins do not render the answered\n      // state.\n      if (\n        isAskUserQuestionMessage(message) &&\n        message.agentMetadata?.askUserQuestionAnswered === true\n      ) {\n        // Drop any previously-mounted plugin bubble so the morph pass\n        // removes the now-stale interactive sheet.\n        lastAskBubbleFingerprint.delete(message.id);\n        const existing = container.querySelector<HTMLElement>(`#wrapper-${message.id}`);\n        existing?.removeAttribute(\"data-preserve-runtime\");\n        return;\n      }\n\n      // suggest_replies renders no transcript bubble: the chips above the\n      // composer are the only UI, and the session auto-resumes the call.\n      // When the feature is disabled the message falls through to the generic\n      // tool bubble (and is never auto-resumed), keeping the parked execution\n      // visible instead of silently swallowed.\n      if (\n        isSuggestRepliesMessage(message) &&\n        config.features?.suggestReplies?.enabled !== false\n      ) {\n        return;\n      }\n\n      if (\n        isAskUserQuestionMessage(message) &&\n        config.features?.askUserQuestion?.enabled !== false\n      ) {\n        const askPlugin = plugins.find((p) => typeof p.renderAskUserQuestion === \"function\");\n        if (askPlugin && sessionRef.current) {\n          const lastFp = lastAskBubbleFingerprint.get(message.id);\n          // Whether to actually call the plugin renderer this pass. We do it\n          // on first sight of this message, or when its fingerprint changed\n          // (e.g. payload streamed in more options). Otherwise we rely on the\n          // already-mounted bubble in `container`.\n          const needsRebuild = lastFp !== fingerprint;\n\n          let pluginBubble: HTMLElement | null = null;\n          if (needsRebuild) {\n            const { payload, complete } = parseAskUserQuestionPayload(message);\n            const messageId = message.id;\n            const liveMessage = (): AgentWidgetMessage | undefined =>\n              sessionRef.current?.getMessages().find((m) => m.id === messageId);\n            pluginBubble = askPlugin.renderAskUserQuestion!({\n              message,\n              payload,\n              complete,\n              resolve: (answer) => {\n                const live = liveMessage();\n                if (live) sessionRef.current?.resolveAskUserQuestion(live, answer);\n              },\n              dismiss: () => {\n                const live = liveMessage();\n                if (live?.agentMetadata?.awaitingLocalTool) {\n                  sessionRef.current?.markAskUserQuestionResolved(live);\n                  sessionRef.current?.resolveAskUserQuestion(live, \"(dismissed)\");\n                }\n              },\n              config,\n            });\n          }\n\n          // If the plugin opted out (returned null on a fresh build) AND we\n          // have no previously-mounted bubble for this message, fall back to\n          // the built-in overlay sheet. If we already have a mounted bubble\n          // and the plugin didn't run this pass (cached), keep using it.\n          const previouslyMounted = lastFp != null;\n          if (needsRebuild && pluginBubble === null && !previouslyMounted) {\n            if (\n              message.agentMetadata?.awaitingLocalTool === true &&\n              !message.agentMetadata?.askUserQuestionAnswered\n            ) {\n              liveAskToolIds.add(message.toolCall!.id);\n              ensureAskUserQuestionSheet(message, config, panelElements.composerOverlay);\n            }\n            return;\n          }\n\n          // Append a stub wrapper for the morph pass; hydrate the real bubble\n          // into it post-morph so its event listeners survive.\n          const stub = document.createElement(\"div\");\n          stub.className = \"persona-flex\";\n          stub.id = `wrapper-${message.id}`;\n          stub.setAttribute(\"data-wrapper-id\", message.id);\n          stub.setAttribute(\"data-ask-plugin-stub\", \"true\");\n          stub.setAttribute(\"data-preserve-runtime\", \"true\");\n          tempContainer.appendChild(stub);\n          askPluginHydrate.push({\n            messageId: message.id,\n            fingerprint,\n            bubble: pluginBubble,\n          });\n          return;\n        } else {\n          if (\n            message.agentMetadata?.awaitingLocalTool === true &&\n            !message.agentMetadata?.askUserQuestionAnswered\n          ) {\n            liveAskToolIds.add(message.toolCall!.id);\n            ensureAskUserQuestionSheet(message, config, panelElements.composerOverlay);\n          }\n          return;\n        }\n      } else if (approvalWithPlugin) {\n        // Plugin-rendered approval bubble. Build the live element with its\n        // listeners, append a stub for the morph pass, and hydrate the live\n        // element into the morphed wrapper afterward (same trick as\n        // `renderAskUserQuestion` / component directives) so Approve/Deny and\n        // any accordion listeners survive idiomorph's `importNode`. Gate the\n        // rebuild on fingerprint so interactive state (e.g. a collapsed\n        // accordion) is preserved while the approval stays pending.\n        const approvalPlugin =\n          plugins.find((p) => typeof p.renderApproval === \"function\") ?? builtInApprovalPlugin;\n        const lastFp = lastApprovalBubbleFingerprint.get(message.id);\n        const needsRebuild = lastFp !== fingerprint;\n        let liveBubble: HTMLElement | null = null;\n\n        if (needsRebuild && approvalPlugin?.renderApproval) {\n          // Re-find the live message at decision time so we resolve against\n          // current state, and route WebMCP gate approvals to the local\n          // resolver: mirroring the built-in delegated handler.\n          const approvalMessageId = message.id;\n          const resolveDecision = (\n            decision: \"approved\" | \"denied\",\n            options?: AgentWidgetApprovalDecisionOptions\n          ): void => {\n            const live = sessionRef.current\n              ?.getMessages()\n              .find((m) => m.id === approvalMessageId);\n            if (!live?.approval) return;\n            if (live.approval.toolType === \"webmcp\") {\n              sessionRef.current?.resolveWebMcpApproval(live.id, decision);\n            } else {\n              sessionRef.current?.resolveApproval(live.approval, decision, options);\n            }\n          };\n          liveBubble = approvalPlugin.renderApproval({\n            message,\n            defaultRenderer: () => createApprovalBubble(message, config),\n            config,\n            approve: (options) => resolveDecision(\"approved\", options),\n            deny: (options) => resolveDecision(\"denied\", options)\n          });\n        }\n\n        if (needsRebuild && liveBubble === null) {\n          // Plugin opted out for this state (e.g. a resolved approval, where the\n          // demo plugin defers to the built-in approved/denied bubble). Render\n          // the built-in bubble: it resolves via the delegated `messagesWrapper`\n          // handler and morphs normally, and drop any preserved live wrapper so\n          // morph can replace the now-stale pending bubble.\n          const existing = container.querySelector<HTMLElement>(`#wrapper-${message.id}`);\n          existing?.removeAttribute(\"data-preserve-runtime\");\n          lastApprovalBubbleFingerprint.delete(message.id);\n          bubble = createApprovalBubble(message, config);\n        } else {\n          // A fresh live bubble to hydrate (needsRebuild), or fingerprint\n          // unchanged so we reuse the preserved live wrapper (`bubble: null`).\n          const stub = document.createElement(\"div\");\n          stub.className = \"persona-flex\";\n          stub.id = `wrapper-${message.id}`;\n          stub.setAttribute(\"data-wrapper-id\", message.id);\n          stub.setAttribute(\"data-approval-plugin-stub\", \"true\");\n          stub.setAttribute(\"data-preserve-runtime\", \"true\");\n          tempContainer.appendChild(stub);\n          approvalPluginHydrate.push({\n            messageId: message.id,\n            fingerprint,\n            bubble: liveBubble\n          });\n          return;\n        }\n      } else if (matchingPlugin) {\n        if (message.variant === \"reasoning\" && message.reasoning && matchingPlugin.renderReasoning) {\n          if (!showReasoning) return;\n          bubble = matchingPlugin.renderReasoning({\n            message,\n            defaultRenderer: () => createReasoningBubble(message, config),\n            config\n          });\n        } else if (message.variant === \"tool\" && message.toolCall && matchingPlugin.renderToolCall) {\n          if (!showToolCalls) return;\n          bubble = matchingPlugin.renderToolCall({\n            message,\n            defaultRenderer: () => createToolBubble(message, config),\n            config\n          });\n        } else if (matchingPlugin.renderMessage) {\n          bubble = matchingPlugin.renderMessage({\n            message,\n            defaultRenderer: () => {\n              const b = createStandardBubble(\n                message,\n                transform,\n                messageLayoutConfig,\n                config.messageActions,\n                messageActionCallbacks,\n                {\n                  loadingIndicatorRenderer: inlineLoadingRenderer,\n                  widgetConfig: config\n                }\n              );\n              if (message.role !== \"user\") {\n                enhanceWithForms(b, message, config, session);\n              }\n              return b;\n            },\n            config\n          });\n        }\n      }\n\n      // Check for component directive if no plugin handled it. We use the\n      // same stub-and-hydrate trick as ask_user_question plugins (see comment\n      // above `componentDirectiveHydrate`): build the live element with its\n      // listeners, append a stub for the morph pass, then inject the live\n      // element into the morphed wrapper afterward.\n      if (!bubble && hasDirectiveBubble) {\n        const directive = extractComponentDirectiveFromMessage(message);\n        if (directive) {\n          const lastFp = lastComponentDirectiveFingerprint.get(message.id);\n          const needsRebuild = lastFp !== fingerprint;\n          const wrapChrome = config.wrapComponentDirectiveInBubble !== false;\n          let liveBubble: HTMLElement | null = null;\n\n          if (needsRebuild) {\n            const componentBubble = renderComponentDirective(directive, {\n              config,\n              message,\n              transform\n            });\n            if (componentBubble) {\n              if (wrapChrome) {\n                const componentWrapper = document.createElement(\"div\");\n                componentWrapper.className = [\n                  \"persona-message-bubble\",\n                  \"persona-max-w-[85%]\",\n                  \"persona-rounded-2xl\",\n                  \"persona-bg-persona-surface\",\n                  \"persona-border\",\n                  \"persona-border-persona-message-border\",\n                  \"persona-p-4\"\n                ].join(\" \");\n                componentWrapper.id = `bubble-${message.id}`;\n                componentWrapper.setAttribute(\"data-message-id\", message.id);\n\n                if (message.content && message.content.trim()) {\n                  const textDiv = document.createElement(\"div\");\n                  textDiv.className = \"persona-mb-3 persona-text-sm persona-leading-relaxed\";\n                  textDiv.innerHTML = transform({\n                    text: message.content,\n                    message,\n                    streaming: Boolean(message.streaming),\n                    raw: message.rawContent\n                  });\n                  componentWrapper.appendChild(textDiv);\n                }\n\n                componentWrapper.appendChild(componentBubble);\n                liveBubble = componentWrapper;\n              } else {\n                const stack = document.createElement(\"div\");\n                stack.className =\n                  \"persona-flex persona-flex-col persona-w-full persona-max-w-full persona-gap-3 persona-items-stretch\";\n                stack.id = `bubble-${message.id}`;\n                stack.setAttribute(\"data-message-id\", message.id);\n                stack.setAttribute(\"data-persona-component-directive\", \"true\");\n\n                if (message.content && message.content.trim()) {\n                  const textDiv = document.createElement(\"div\");\n                  textDiv.className =\n                    \"persona-text-sm persona-leading-relaxed persona-text-persona-primary persona-w-full\";\n                  textDiv.innerHTML = transform({\n                    text: message.content,\n                    message,\n                    streaming: Boolean(message.streaming),\n                    raw: message.rawContent\n                  });\n                  stack.appendChild(textDiv);\n                }\n\n                stack.appendChild(componentBubble);\n                liveBubble = stack;\n              }\n            }\n          }\n\n          // If the directive is registered (live bubble built or already\n          // mounted from a previous pass), use the stub-and-hydrate path.\n          // Otherwise fall through to the standard render path so the message\n          // text is at least visible.\n          if (liveBubble || lastFp != null) {\n            const stub = document.createElement(\"div\");\n            stub.className = \"persona-flex\";\n            stub.id = `wrapper-${message.id}`;\n            stub.setAttribute(\"data-wrapper-id\", message.id);\n            stub.setAttribute(\"data-component-directive-stub\", \"true\");\n            stub.setAttribute(\"data-preserve-runtime\", \"true\");\n            if (!wrapChrome) {\n              stub.classList.add(\"persona-w-full\");\n            }\n            tempContainer.appendChild(stub);\n            componentDirectiveHydrate.push({\n              messageId: message.id,\n              fingerprint,\n              bubble: liveBubble\n            });\n            return;\n          }\n        }\n      }\n\n      // Fallback to default rendering if plugin returned null or no plugin matched\n      if (!bubble) {\n        if (message.variant === \"reasoning\" && message.reasoning) {\n          if (!showReasoning) return;\n          bubble = createReasoningBubble(message, config);\n        } else if (message.variant === \"tool\" && message.toolCall) {\n          if (!showToolCalls) return;\n          bubble = createToolBubble(message, config);\n        } else if (message.variant === \"approval\" && message.approval) {\n          if (config.approval === false) return;\n          bubble = createApprovalBubble(message, config);\n        } else {\n          // Check for custom message renderers in layout config\n          const messageLayoutConfig = config.layout?.messages;\n          if (messageLayoutConfig?.renderUserMessage && message.role === \"user\") {\n            bubble = messageLayoutConfig.renderUserMessage({\n              message,\n              config,\n              streaming: Boolean(message.streaming)\n            });\n          } else if (messageLayoutConfig?.renderAssistantMessage && message.role === \"assistant\") {\n            bubble = messageLayoutConfig.renderAssistantMessage({\n              message,\n              config,\n              streaming: Boolean(message.streaming)\n            });\n          } else {\n            bubble = createStandardBubble(\n              message,\n              transform,\n              messageLayoutConfig,\n              config.messageActions,\n              messageActionCallbacks,\n              {\n                loadingIndicatorRenderer: inlineLoadingRenderer,\n                widgetConfig: config\n              }\n            );\n          }\n          if (message.role !== \"user\" && bubble) {\n            enhanceWithForms(bubble, message, config, session);\n          }\n        }\n      }\n\n      const wrapper = document.createElement(\"div\");\n      wrapper.className = \"persona-flex\";\n      // Set id for idiomorph matching\n      wrapper.id = `wrapper-${message.id}`;\n      wrapper.setAttribute(\"data-wrapper-id\", message.id);\n      if (message.role === \"user\") {\n        wrapper.classList.add(\"persona-justify-end\");\n      }\n      if (bubble?.getAttribute(\"data-persona-component-directive\") === \"true\") {\n        wrapper.classList.add(\"persona-w-full\");\n      }\n      wrapper.appendChild(bubble);\n      setCachedWrapper(messageCache, message.id, fingerprint, wrapper);\n      tempContainer.appendChild(wrapper);\n    });\n\n    // Prune any ask_user_question sheets whose source message is no longer in\n    // the message list (e.g. after clearChat or a splice).\n    if (panelElements.composerOverlay) {\n      const sheets = panelElements.composerOverlay.querySelectorAll<HTMLElement>(\n        \"[data-persona-ask-sheet-for]\"\n      );\n      sheets.forEach((sheet) => {\n        const id = sheet.getAttribute(\"data-persona-ask-sheet-for\");\n        if (id && !liveAskToolIds.has(id)) {\n          removeAskUserQuestionSheet(panelElements.composerOverlay, id);\n        }\n      });\n    }\n\n    if (config.features?.toolCallDisplay?.grouped) {\n      const toolGroups: AgentWidgetMessage[][] = [];\n      let currentGroup: AgentWidgetMessage[] = [];\n\n      messages.forEach((message) => {\n        if (message.variant === \"tool\" && message.toolCall && showToolCalls) {\n          currentGroup.push(message);\n          return;\n        }\n        if (currentGroup.length > 1) {\n          toolGroups.push(currentGroup);\n        }\n        currentGroup = [];\n      });\n      if (currentGroup.length > 1) {\n        toolGroups.push(currentGroup);\n      }\n\n      toolGroups.forEach((group, groupIndex) => {\n        const wrappers = group\n          .map((groupMessage) =>\n            Array.from(tempContainer.children).find(\n              (child) =>\n                child instanceof HTMLElement &&\n                child.getAttribute(\"data-wrapper-id\") === groupMessage.id\n            ) as HTMLElement | undefined\n          )\n          .filter((wrapper): wrapper is HTMLElement => Boolean(wrapper));\n\n        if (wrappers.length < 2) {\n          return;\n        }\n\n        const groupWrapper = document.createElement(\"div\");\n        groupWrapper.className = \"persona-flex\";\n        groupWrapper.id = `wrapper-tool-group-${groupIndex}-${group[0].id}`;\n        groupWrapper.setAttribute(\"data-wrapper-id\", `tool-group-${groupIndex}-${group[0].id}`);\n\n        const groupContainer = document.createElement(\"div\");\n        groupContainer.className =\n          \"persona-tool-group persona-flex persona-w-full persona-flex-col persona-gap-2\";\n        groupContainer.setAttribute(\"data-persona-tool-group\", \"true\");\n\n        const summary = document.createElement(\"div\");\n        summary.className =\n          \"persona-tool-group-summary persona-text-xs persona-text-persona-muted\";\n\n        const defaultSummary = `Called ${group.length} tools`;\n        const renderedSummary = config.toolCall?.renderGroupedSummary?.({\n          messages: group,\n          toolCalls: group\n            .map((groupMessage) => groupMessage.toolCall)\n            .filter((toolCall): toolCall is NonNullable<typeof group[number][\"toolCall\"]> => Boolean(toolCall)),\n          defaultSummary,\n          config,\n        });\n        if (!appendRenderedValue(summary, renderedSummary)) {\n          summary.textContent = defaultSummary;\n        }\n\n        const stack = document.createElement(\"div\");\n        stack.className = \"persona-tool-group-stack persona-flex persona-flex-col\";\n\n        groupContainer.append(summary, stack);\n        groupWrapper.appendChild(groupContainer);\n        wrappers[0].before(groupWrapper);\n\n        wrappers.forEach((wrapper, wrapperIndex) => {\n          const item = document.createElement(\"div\");\n          item.className = \"persona-tool-group-item persona-relative\";\n          item.setAttribute(\"data-persona-tool-group-item\", \"true\");\n          if (wrapperIndex < wrappers.length - 1) {\n            item.setAttribute(\"data-persona-tool-group-connector\", \"true\");\n          }\n          item.appendChild(wrapper);\n          stack.appendChild(item);\n        });\n      });\n    }\n\n    // Remove cache entries for messages that no longer exist\n    pruneCache(messageCache, activeMessageIds);\n\n    // Add standalone typing indicator only if streaming but no assistant message is streaming yet\n    // (This shows while waiting for the stream to start)\n    // Check for ANY streaming assistant message, even if empty (to avoid duplicate bubbles)\n    const hasStreamingAssistantMessage = messages.some(\n      (msg) => msg.role === \"assistant\" && msg.streaming\n    );\n    \n    // Also check if there's a recently completed assistant message (streaming just ended)\n    // This prevents flicker when the message completes but isStreaming hasn't updated yet\n    // Approval-variant messages are UI controls, not content: exclude them so the typing\n    // indicator still shows while the agent resumes after approval\n    const lastMessage = messages[messages.length - 1];\n    const hasRecentAssistantResponse = lastMessage?.role === \"assistant\" && !lastMessage.streaming && lastMessage.variant !== \"approval\";\n\n    if (isStreaming && messages.some((msg) => msg.role === \"user\") && !hasStreamingAssistantMessage && !hasRecentAssistantResponse) {\n      // Get loading indicator using priority chain: plugin -> config -> default\n      const loadingIndicatorContext: LoadingIndicatorRenderContext = {\n        config,\n        streaming: true,\n        location: 'standalone',\n        defaultRenderer: createTypingIndicator\n      };\n\n      // Try plugin renderLoadingIndicator first\n      const loadingPlugin = plugins.find(p => p.renderLoadingIndicator);\n      let typingIndicator: HTMLElement | null = null;\n\n      if (loadingPlugin?.renderLoadingIndicator) {\n        typingIndicator = loadingPlugin.renderLoadingIndicator(loadingIndicatorContext);\n      }\n\n      // Try config loadingIndicator.render if no plugin handled it\n      if (typingIndicator === null && config.loadingIndicator?.render) {\n        typingIndicator = config.loadingIndicator.render(loadingIndicatorContext);\n      }\n\n      // Fall back to default\n      if (typingIndicator === null) {\n        typingIndicator = createTypingIndicator();\n      }\n\n      // Only render if we have an indicator (allows hiding via returning null)\n      if (typingIndicator) {\n        // Create a bubble wrapper for the typing indicator (similar to assistant messages)\n        const typingBubble = document.createElement(\"div\");\n        const showBubble = config.loadingIndicator?.showBubble !== false; // default true\n        typingBubble.className = showBubble\n          ? [\n              \"persona-max-w-[85%]\",\n              \"persona-rounded-2xl\",\n              \"persona-text-sm\",\n              \"persona-leading-relaxed\",\n              \"persona-shadow-sm\",\n              \"persona-bg-persona-surface\",\n              \"persona-border\",\n              \"persona-text-persona-primary\",\n              \"persona-px-5\",\n              \"persona-py-3\"\n            ].join(\" \")\n          : [\n              \"persona-max-w-[85%]\",\n              \"persona-text-sm\",\n              \"persona-leading-relaxed\",\n              \"persona-text-persona-primary\"\n            ].join(\" \");\n        typingBubble.setAttribute(\"data-typing-indicator\", \"true\");\n        typingBubble.style.borderColor = \"var(--persona-message-assistant-border, var(--persona-border, #e5e7eb))\";\n\n        typingBubble.appendChild(typingIndicator);\n\n        const typingWrapper = document.createElement(\"div\");\n        typingWrapper.className = \"persona-flex\";\n        // Set id for idiomorph matching\n        typingWrapper.id = \"wrapper-typing-indicator\";\n        typingWrapper.setAttribute(\"data-wrapper-id\", \"typing-indicator\");\n        typingWrapper.appendChild(typingBubble);\n        tempContainer.appendChild(typingWrapper);\n      }\n    }\n\n    // Render idle state indicator when not streaming and has messages\n    if (!isStreaming && messages.length > 0) {\n      const lastMessage = messages[messages.length - 1];\n\n      // Create context for idle indicator render functions\n      const idleIndicatorContext: IdleIndicatorRenderContext = {\n        config,\n        lastMessage,\n        messageCount: messages.length\n      };\n\n      // Get idle indicator using priority chain: plugin -> config -> null (default)\n      // Try plugin renderIdleIndicator first\n      const idlePlugin = plugins.find(p => p.renderIdleIndicator);\n      let idleIndicator: HTMLElement | null = null;\n\n      if (idlePlugin?.renderIdleIndicator) {\n        idleIndicator = idlePlugin.renderIdleIndicator(idleIndicatorContext);\n      }\n\n      // Try config loadingIndicator.renderIdle if no plugin handled it\n      if (idleIndicator === null && config.loadingIndicator?.renderIdle) {\n        idleIndicator = config.loadingIndicator.renderIdle(idleIndicatorContext);\n      }\n\n      // Only render if we have an indicator (default is null - no idle indicator)\n      if (idleIndicator) {\n        // Create a wrapper for the idle indicator (similar to typing indicator)\n        const idleBubble = document.createElement(\"div\");\n        const showBubble = config.loadingIndicator?.showBubble !== false; // default true\n        idleBubble.className = showBubble\n          ? [\n              \"persona-max-w-[85%]\",\n              \"persona-rounded-2xl\",\n              \"persona-text-sm\",\n              \"persona-leading-relaxed\",\n              \"persona-shadow-sm\",\n              \"persona-bg-persona-surface\",\n              \"persona-border\",\n              \"persona-border-persona-message-border\",\n              \"persona-text-persona-primary\",\n              \"persona-px-5\",\n              \"persona-py-3\"\n            ].join(\" \")\n          : [\n              \"persona-max-w-[85%]\",\n              \"persona-text-sm\",\n              \"persona-leading-relaxed\",\n              \"persona-text-persona-primary\"\n            ].join(\" \");\n        idleBubble.setAttribute(\"data-idle-indicator\", \"true\");\n\n        idleBubble.appendChild(idleIndicator);\n\n        const idleWrapper = document.createElement(\"div\");\n        idleWrapper.className = \"persona-flex\";\n        // Set id for idiomorph matching\n        idleWrapper.id = \"wrapper-idle-indicator\";\n        idleWrapper.setAttribute(\"data-wrapper-id\", \"idle-indicator\");\n        idleWrapper.appendChild(idleBubble);\n        tempContainer.appendChild(idleWrapper);\n      }\n    }\n\n    // Use idiomorph to morph the container contents\n    morphMessages(container, tempContainer);\n\n    // Hydrate plugin-rendered ask-question bubbles into their stub wrappers.\n    // Idiomorph imports new nodes via `document.importNode`, which strips\n    // listeners, so we built only an empty stub during morph and now inject\n    // the real, listener-bearing bubble directly into the live DOM.\n    if (askPluginHydrate.length > 0) {\n      for (const { messageId, fingerprint, bubble } of askPluginHydrate) {\n        const wrapper = container.querySelector(`#wrapper-${messageId}`);\n        if (!wrapper) continue;\n        if (bubble === null) {\n          // No fresh bubble built this pass: either the plugin opted out\n          // and a previously-mounted bubble already lives here (preserved by\n          // `data-preserve-runtime`), or we skipped the rebuild because the\n          // fingerprint matched. Either way, leave the live wrapper alone.\n          continue;\n        }\n        wrapper.replaceChildren(bubble);\n        wrapper.setAttribute(\"data-bubble-fp\", fingerprint);\n        lastAskBubbleFingerprint.set(messageId, fingerprint);\n      }\n    }\n\n    // Drop fingerprints for messages that are no longer present so a future\n    // re-appearance triggers a fresh plugin render.\n    if (lastAskBubbleFingerprint.size > 0) {\n      for (const id of lastAskBubbleFingerprint.keys()) {\n        if (!activeMessageIds.has(id)) lastAskBubbleFingerprint.delete(id);\n      }\n    }\n\n    // Hydrate component-directive bubbles into their stub wrappers, mirroring\n    // the ask-question hydration above.\n    if (componentDirectiveHydrate.length > 0) {\n      for (const { messageId, fingerprint, bubble } of componentDirectiveHydrate) {\n        const wrapper = container.querySelector(`#wrapper-${messageId}`);\n        if (!wrapper) continue;\n        if (bubble === null) {\n          // Fingerprint matched the previous pass: the live wrapper (kept\n          // alive by `data-preserve-runtime`) still holds the listener-bearing\n          // bubble from a prior render. Leave it untouched.\n          continue;\n        }\n        wrapper.replaceChildren(bubble);\n        wrapper.setAttribute(\"data-bubble-fp\", fingerprint);\n        lastComponentDirectiveFingerprint.set(messageId, fingerprint);\n      }\n    }\n\n    if (lastComponentDirectiveFingerprint.size > 0) {\n      for (const id of lastComponentDirectiveFingerprint.keys()) {\n        if (!activeMessageIds.has(id)) lastComponentDirectiveFingerprint.delete(id);\n      }\n    }\n\n    // Hydrate plugin-rendered approval bubbles into their stub wrappers,\n    // mirroring the ask-question / component-directive hydration above.\n    if (approvalPluginHydrate.length > 0) {\n      for (const { messageId, fingerprint, bubble } of approvalPluginHydrate) {\n        const wrapper = container.querySelector(`#wrapper-${messageId}`);\n        if (!wrapper) continue;\n        if (bubble === null) {\n          // Fingerprint matched the previous pass (or the plugin opted out\n          // after a prior render): the live wrapper, kept alive by\n          // `data-preserve-runtime`, still holds the listener-bearing bubble.\n          continue;\n        }\n        wrapper.replaceChildren(bubble);\n        wrapper.setAttribute(\"data-bubble-fp\", fingerprint);\n        lastApprovalBubbleFingerprint.set(messageId, fingerprint);\n      }\n    }\n\n    if (lastApprovalBubbleFingerprint.size > 0) {\n      for (const id of lastApprovalBubbleFingerprint.keys()) {\n        if (!activeMessageIds.has(id)) lastApprovalBubbleFingerprint.delete(id);\n      }\n    }\n  };\n\n  // Alias for clarity - the implementation handles flicker prevention via typing indicator logic.\n  // Re-apply read-aloud button state after each render so a playing/paused\n  // message keeps its icon across idiomorph DOM morphs.\n  const renderMessagesWithPlugins = (\n    container: HTMLElement,\n    messages: AgentWidgetMessage[],\n    transform: MessageTransform\n  ) => {\n    renderMessagesWithPluginsImpl(container, messages, transform);\n    refreshReadAloudButtons();\n  };\n\n  /**\n   * Composer-bar outside-click dismiss. While the chat is expanded, clicking\n   * anywhere outside the wrapper (i.e. NOT inside the chat panel chrome and\n   * NOT inside the pill) collapses back to just the pill. Uses `pointerdown`\n   * + capture so we run before host-page click handlers (and before any\n   * stop-propagation upstream); composedPath() includes the shadow DOM\n   * subtree, so clicks inside the wrapper (which lives in the shadow root)\n   * are correctly identified as inside.\n   */\n  let composerBarOutsideClickListener: ((e: PointerEvent) => void) | null = null;\n\n  const attachComposerBarOutsideClickDismiss = () => {\n    if (composerBarOutsideClickListener) return;\n    const listener: (e: PointerEvent) => void = (event) => {\n      const path = event.composedPath();\n      // pillRoot is a viewport-fixed sibling of the wrapper, so a click on\n      // the pill or peek wouldn't be in `wrapper`'s composedPath even\n      // though it's logically \"inside\" the widget.\n      if (path.includes(wrapper)) return;\n      if (pillRoot && path.includes(pillRoot)) return;\n      setOpenState(false, \"user\");\n    };\n    composerBarOutsideClickListener = listener;\n    const targetDoc = mount.ownerDocument ?? document;\n    targetDoc.addEventListener(\"pointerdown\", listener, true);\n  };\n\n  const detachComposerBarOutsideClickDismiss = () => {\n    if (!composerBarOutsideClickListener) return;\n    const targetDoc = mount.ownerDocument ?? document;\n    targetDoc.removeEventListener(\n      \"pointerdown\",\n      composerBarOutsideClickListener,\n      true\n    );\n    composerBarOutsideClickListener = null;\n  };\n\n  destroyCallbacks.push(() => detachComposerBarOutsideClickDismiss());\n\n  /**\n   * Composer-bar ESC dismiss. While the chat is expanded, pressing Escape\n   * collapses back to just the pill: same end state as outside-click.\n   * Matches the WAI-ARIA dialog pattern (modal mode is literally a dialog)\n   * and the dominant chat-widget convention (Intercom, Drift, Crisp).\n   * Guards on `event.isComposing` so dismissing an IME suggestion doesn't\n   * also collapse the panel.\n   */\n  let composerBarEscapeListener: ((e: KeyboardEvent) => void) | null = null;\n\n  const attachComposerBarEscapeDismiss = () => {\n    if (composerBarEscapeListener) return;\n    const listener: (e: KeyboardEvent) => void = (event) => {\n      if (event.key !== \"Escape\") return;\n      if (event.isComposing) return;\n      setOpenState(false, \"user\");\n    };\n    composerBarEscapeListener = listener;\n    const targetDoc = mount.ownerDocument ?? document;\n    targetDoc.addEventListener(\"keydown\", listener, true);\n  };\n\n  const detachComposerBarEscapeDismiss = () => {\n    if (!composerBarEscapeListener) return;\n    const targetDoc = mount.ownerDocument ?? document;\n    targetDoc.removeEventListener(\n      \"keydown\",\n      composerBarEscapeListener,\n      true\n    );\n    composerBarEscapeListener = null;\n  };\n\n  destroyCallbacks.push(() => detachComposerBarEscapeDismiss());\n\n  /**\n   * Composer-bar \"peek\" affordance: a chrome-less row above the pill that\n   * shows a chat-bubble icon, the trailing 100 chars of the most recent\n   * assistant message, and a chevron-up. It is the user's path back into the\n   * expanded chat from the collapsed pill.\n   *\n   * Visible when (collapsed) AND (there is an assistant message with content)\n   * AND (`isStreaming` OR `composerHovered`). Otherwise hidden. The hover\n   * zone is the whole `panel` (not just the pill) so the cursor moving\n   * between the pill and the peek doesn't trigger fade-out.\n   *\n   * Driven from a single `syncComposerBarPeek()` invoked from\n   * `onMessagesChanged`, `onStreamingChanged`, `updateOpenState`, the\n   * pointerenter/pointerleave on `panel`, and once at end-of-init.\n   */\n  let composerHovered = false;\n  // Track which peek-plugins we've already attached for this widget root.\n  // `ensurePluginActive` is idempotent, but the call is guarded behind a flag\n  // so we don't pay the lookup cost on every chunk.\n  const peekActivatedPlugins = new Set<string>();\n\n  /**\n   * Resolve the effective stream animation feature for the peek surface.\n   * `composerBar.peek.streamAnimation` overrides; otherwise the peek inherits\n   * `features.streamAnimation` so the surface for devs is consistent across\n   * the main bubble and the peek banner.\n   */\n  const resolvePeekStreamAnimationFeature = () => {\n    const peekFeature = config.launcher?.composerBar?.peek?.streamAnimation;\n    if (peekFeature) return peekFeature;\n    return config.features?.streamAnimation;\n  };\n\n  const syncComposerBarPeek = () => {\n    if (!isComposerBar()) return;\n    const peekBanner = panelElements.peekBanner;\n    const peekTextNode = panelElements.peekTextNode;\n    if (!peekBanner || !peekTextNode) return;\n\n    if (open) {\n      peekBanner.classList.remove(\"persona-pill-peek--visible\");\n      return;\n    }\n\n    const messages = session?.getMessages() ?? [];\n    let lastAssistant: AgentWidgetMessage | undefined;\n    for (let i = messages.length - 1; i >= 0; i--) {\n      const m = messages[i];\n      if (m.role === \"assistant\" && m.content) {\n        lastAssistant = m;\n        break;\n      }\n    }\n    if (!lastAssistant) {\n      peekBanner.classList.remove(\"persona-pill-peek--visible\");\n      return;\n    }\n\n    const text = lastAssistant.content;\n    const streaming = Boolean(lastAssistant.streaming);\n\n    // Resolve the same animation surface used by the main bubble. The peek\n    // ignores `bubbleClass` (carve-out: peek has no bubble) but honors\n    // `containerClass`, `wrap`, `useCaret`, `buffer`, `placeholder`,\n    // `speed`/`duration`, and custom plugins.\n    const feature = resolvePeekStreamAnimationFeature();\n    const streamAnimation = resolveStreamAnimation(feature);\n    const plugin =\n      streamAnimation.type !== \"none\"\n        ? resolveStreamAnimationPlugin(streamAnimation.type, feature?.plugins)\n        : null;\n    const pluginStillAnimating =\n      plugin?.isAnimating?.(lastAssistant) === true;\n    const animationActive =\n      plugin !== null && (streaming || pluginStillAnimating);\n\n    if (animationActive && plugin && !peekActivatedPlugins.has(plugin.name)) {\n      ensurePluginActive(plugin, mount);\n      peekActivatedPlugins.add(plugin.name);\n    }\n\n    // Manage `containerClass` on the peek text node. We track which class is\n    // currently applied so a config swap (or animation deactivating after\n    // stream completion) cleans up the previous class instead of stacking.\n    const desiredContainerClass =\n      animationActive && plugin?.containerClass ? plugin.containerClass : null;\n    const currentContainerClass =\n      peekTextNode.dataset.personaPeekStreamClass ?? null;\n    if (currentContainerClass && currentContainerClass !== desiredContainerClass) {\n      peekTextNode.classList.remove(currentContainerClass);\n      delete peekTextNode.dataset.personaPeekStreamClass;\n    }\n    if (desiredContainerClass && currentContainerClass !== desiredContainerClass) {\n      peekTextNode.classList.add(desiredContainerClass);\n      peekTextNode.dataset.personaPeekStreamClass = desiredContainerClass;\n    }\n\n    if (animationActive) {\n      peekTextNode.style.setProperty(\n        \"--persona-stream-step\",\n        `${streamAnimation.speed}ms`\n      );\n      peekTextNode.style.setProperty(\n        \"--persona-stream-duration\",\n        `${streamAnimation.duration}ms`\n      );\n    } else {\n      peekTextNode.style.removeProperty(\"--persona-stream-step\");\n      peekTextNode.style.removeProperty(\"--persona-stream-duration\");\n    }\n\n    // Apply buffering (word/line/plugin custom). If the buffer trims content\n    // to empty AND the placeholder is \"skeleton\", show the skeleton: that's\n    // the \"line buffer between completions\" affordance. Otherwise no\n    // pre-content placeholder on the peek (a typing-dots indicator inside a\n    // 1-line ticker would feel cramped).\n    const buffered = animationActive\n      ? applyStreamBuffer(text, streamAnimation.buffer, plugin, lastAssistant, streaming)\n      : text;\n\n    const skeletonEnabled =\n      animationActive && streamAnimation.placeholder === \"skeleton\";\n    const showSkeletonOnly =\n      skeletonEnabled && streaming && (!buffered || !buffered.trim());\n\n    if (showSkeletonOnly) {\n      // Replace text node contents with just a peek-sized skeleton bar. The\n      // bar carries `data-preserve-animation` so idiomorph keeps its shimmer\n      // running across morph passes.\n      const tempContainer = document.createElement(\"div\");\n      const skeleton = createSkeletonPlaceholder();\n      skeleton.classList.add(\"persona-pill-peek__skeleton\");\n      tempContainer.appendChild(skeleton);\n      morphMessages(peekTextNode, tempContainer);\n    } else {\n      // Trailing 100 chars; for animated modes we keep the slice but use\n      // ABSOLUTE indices so per-char/per-word span IDs stay stable as the\n      // window shifts each chunk: idiomorph then preserves animations on\n      // already-revealed units instead of restarting them. Plain \"none\" mode\n      // keeps the legacy `…` ellipsis prefix for visual continuity with the\n      // pre-animation behavior.\n      const sliceStart = Math.max(0, buffered.length - 100);\n      const slice = buffered.length > 100 ? buffered.slice(-100) : buffered;\n      const escaped = escapeHtml(slice);\n\n      if (!animationActive || !plugin) {\n        const preview = buffered.length > 100 ? `…${slice}` : slice;\n        if (peekTextNode.textContent !== preview) {\n          peekTextNode.textContent = preview;\n        }\n      } else {\n        let html = escaped;\n        if (plugin.wrap === \"char\" || plugin.wrap === \"word\") {\n          html = wrapStreamAnimation(\n            escaped,\n            plugin.wrap,\n            // Namespace span IDs to the peek surface so they don't collide\n            // with the main bubble's spans for the same message id.\n            `peek-${lastAssistant.id}`,\n            { skipTags: plugin.skipTags, startIndex: sliceStart }\n          );\n        }\n\n        const tempContainer = document.createElement(\"div\");\n        tempContainer.innerHTML = html;\n\n        if (plugin.useCaret && slice.length > 0) {\n          const caret = createStreamCaret();\n          const spans = tempContainer.querySelectorAll(\n            \".persona-stream-char, .persona-stream-word\"\n          );\n          const lastSpan = spans[spans.length - 1];\n          if (lastSpan?.parentNode) {\n            lastSpan.parentNode.insertBefore(caret, lastSpan.nextSibling);\n          } else {\n            tempContainer.appendChild(caret);\n          }\n        }\n\n        morphMessages(peekTextNode, tempContainer);\n\n        // Fire the plugin's per-render hook so glyph-cycle / wipe / custom\n        // plugins get a chance to mutate the peek's spans the same way they\n        // mutate the main bubble's. The carve-out: `bubble` here is the peek\n        // banner root, not a message bubble: plugins that target\n        // `bubbleClass` should no-op on that surface.\n        plugin.onAfterRender?.({\n          container: peekTextNode,\n          bubble: peekBanner,\n          messageId: lastAssistant.id,\n          message: lastAssistant,\n          speed: streamAnimation.speed,\n          duration: streamAnimation.duration,\n        });\n      }\n    }\n\n    const shouldShow = isStreaming || composerHovered;\n    peekBanner.classList.toggle(\"persona-pill-peek--visible\", shouldShow);\n  };\n\n  if (isComposerBar()) {\n    const peekBanner = panelElements.peekBanner;\n    if (peekBanner) {\n      // pointerdown (not click) so this competes correctly with the\n      // outside-click listener (also pointerdown, capture phase). The\n      // outside-click composedPath check passes for events inside `wrapper`\n      // or `pillRoot` (peek's parent), so the peek can stop propagation\n      // here without breaking dismissal.\n      const onPeekPointerDown = (e: PointerEvent) => {\n        e.preventDefault();\n        e.stopPropagation();\n        setOpenState(true, \"user\");\n      };\n      peekBanner.addEventListener(\"pointerdown\", onPeekPointerDown);\n      destroyCallbacks.push(() => {\n        peekBanner.removeEventListener(\"pointerdown\", onPeekPointerDown);\n      });\n    }\n\n    const onPanelPointerEnter = () => {\n      if (composerHovered) return;\n      composerHovered = true;\n      syncComposerBarPeek();\n    };\n    const onPanelPointerLeave = () => {\n      if (!composerHovered) return;\n      composerHovered = false;\n      syncComposerBarPeek();\n    };\n    panel.addEventListener(\"pointerenter\", onPanelPointerEnter);\n    panel.addEventListener(\"pointerleave\", onPanelPointerLeave);\n    destroyCallbacks.push(() => {\n      panel.removeEventListener(\"pointerenter\", onPanelPointerEnter);\n      panel.removeEventListener(\"pointerleave\", onPanelPointerLeave);\n    });\n\n    // pillRoot now hosts the pill + peek as viewport-level siblings, so the\n    // panel's pointerenter/leave above no longer fires when the cursor is\n    // over the pill area. Mirror the handlers onto pillRoot so hovering\n    // either surface still drives `composerHovered`. Both handlers are\n    // idempotent against the shared flag, so cross-traffic between panel\n    // and pillRoot doesn't cause spurious flips.\n    if (pillRoot) {\n      pillRoot.addEventListener(\"pointerenter\", onPanelPointerEnter);\n      pillRoot.addEventListener(\"pointerleave\", onPanelPointerLeave);\n      destroyCallbacks.push(() => {\n        pillRoot.removeEventListener(\"pointerenter\", onPanelPointerEnter);\n        pillRoot.removeEventListener(\"pointerleave\", onPanelPointerLeave);\n      });\n    }\n  }\n\n  /**\n   * Composer-bar geometry, owned in one place so collapsed → expanded (and\n   * back) transitions don't leave stale inline styles from a previous state.\n   * `createWrapper` no longer sets any geometry; everything flows through\n   * here.\n   *\n   * Width is expressed as `width: <configured>; max-width: calc(100vw -\n   * 32px)`. The two combine such that `width` wins on wide viewports and\n   * `max-width` clamps on narrow ones: same effect as `min(...)` but\n   * jsdom-compatible. `100vw` is always the viewport, so the containing-\n   * block edge case (host with `transform`/`filter` causing `100%` to\n   * resolve against the host instead of the viewport) is neutralized.\n   */\n  const applyComposerBarGeometry = (isOpen: boolean) => {\n    const cb = config.launcher?.composerBar ?? {};\n    const expandedSize = cb.expandedSize ?? \"anchored\";\n    const bottomOffset = cb.bottomOffset ?? \"16px\";\n    // No hardcoded default: when undefined, CSS media queries provide the\n    // responsive width (90vw / 70vw / 50vw at <640 / <1024 / >=1024) on\n    // pillRoot.\n    const collapsedMaxWidth = cb.collapsedMaxWidth;\n    const expandedMaxWidth = cb.expandedMaxWidth ?? \"880px\";\n    const expandedTopOffset = cb.expandedTopOffset ?? \"5vh\";\n    const modalMaxWidth = cb.modalMaxWidth ?? \"880px\";\n    const modalMaxHeight = cb.modalMaxHeight ?? \"min(90vh, 800px)\";\n    const viewportClamp = \"calc(100vw - 32px)\";\n    // Static fallback for the pill area's height (pill + 8px gap + peek\n    // slack). Anchored mode uses this to compute the wrapper's bottom edge\n    // so the chat panel chrome doesn't overlap the pill below. Defer\n    // ResizeObserver-based dynamic sizing until we see a real misalignment.\n    const pillAreaClearance = \"var(--persona-pill-area-height, 80px)\";\n\n    // Reset everything geometry-related so each branch sets exactly what it\n    // needs. Using empty strings drops the inline declaration entirely so\n    // CSS rules can take over (relevant for fullscreen).\n    const s = wrapper.style;\n    s.left = \"\";\n    s.right = \"\";\n    s.top = \"\";\n    s.bottom = \"\";\n    s.transform = \"\";\n    s.width = \"\";\n    s.maxWidth = \"\";\n    s.height = \"\";\n    s.maxHeight = \"\";\n\n    // pillRoot owns its own geometry (bottom offset + collapsed width\n    // override). Reset and re-apply per-config every call so config edits\n    // (e.g. via the demo's mode-switch) propagate cleanly.\n    if (pillRoot) {\n      const ps = pillRoot.style;\n      ps.bottom = bottomOffset;\n      // CSS media queries handle responsive width when no override is set.\n      ps.width = collapsedMaxWidth ?? \"\";\n    }\n\n    if (!isOpen) {\n      // Collapsed: wrapper has nothing visible to render: the container\n      // inside is `display: none` (via CSS keyed on `[data-state=\"collapsed\"]`)\n      // and the pill lives in pillRoot. Leave wrapper geometry empty so it\n      // collapses to a zero-size positioning frame at the default fixed\n      // origin. The container's fade-in keyframe handles the perceptible\n      // expand animation, so there's no chrome to lose during this state.\n      return;\n    }\n\n    if (expandedSize === \"fullscreen\") {\n      // Leave inline styles cleared so the CSS rule for fullscreen takes over.\n      return;\n    }\n\n    if (expandedSize === \"modal\") {\n      s.top = \"50%\";\n      s.left = \"50%\";\n      s.transform = \"translate(-50%, -50%)\";\n      s.bottom = \"auto\";\n      s.right = \"auto\";\n      s.width = modalMaxWidth;\n      s.maxWidth = viewportClamp;\n      s.maxHeight = modalMaxHeight;\n      s.height = modalMaxHeight;\n      return;\n    }\n\n    // Default: anchored: pill stays at the viewport bottom (in pillRoot);\n    // wrapper's bottom edge clears the pill area so the chrome doesn't\n    // overlap it.\n    s.left = \"50%\";\n    s.transform = \"translateX(-50%)\";\n    s.bottom = `calc(${bottomOffset} + ${pillAreaClearance})`;\n    s.top = expandedTopOffset;\n    s.width = expandedMaxWidth;\n    s.maxWidth = viewportClamp;\n  };\n\n  const updateOpenState = () => {\n    if (!isPanelToggleable()) return;\n\n    // Composer-bar mode morphs the wrapper between collapsed pill and\n    // expanded panel via data-attrs + per-state inline geometry. The chat\n    // body and header are hidden in the collapsed state so only the\n    // composer footer remains visible in the pill.\n    if (isComposerBar()) {\n      const cb = config.launcher?.composerBar ?? {};\n      const expandedSize = cb.expandedSize ?? \"anchored\";\n      const nextState = open ? \"expanded\" : \"collapsed\";\n      wrapper.dataset.state = nextState;\n      wrapper.dataset.expandedSize = expandedSize;\n      // pillRoot mirrors wrapper's state attributes so CSS rules keyed off\n      // [data-state] / [data-expanded-size] cascade to pill + peek even\n      // though they live outside the wrapper subtree.\n      if (pillRoot) {\n        pillRoot.dataset.state = nextState;\n        pillRoot.dataset.expandedSize = expandedSize;\n      }\n      wrapper.style.removeProperty(\"display\");\n      wrapper.classList.remove(\"persona-pointer-events-none\", \"persona-opacity-0\");\n      panel.classList.remove(\n        \"persona-scale-95\",\n        \"persona-opacity-0\",\n        \"persona-scale-100\",\n        \"persona-opacity-100\"\n      );\n\n      applyComposerBarGeometry(open);\n\n      // Toggle the entire container (chat chrome + body + close button) so\n      // the collapsed pill only shows the footer (which lives as a SIBLING\n      // of the container in the panel: see panel.appendChild(footer) above).\n      // The footer is always visible / interactive.\n      container.style.display = open ? \"flex\" : \"none\";\n\n      // Re-run chrome application now that data-state has flipped: collapsed\n      // clears container chrome (pill stands alone), expanded paints it via\n      // the same theme.components.panel.* contract as floating mode.\n      applyFullHeightStyles();\n\n      // Outside-click dismiss: while expanded, clicking anywhere outside the\n      // wrapper (panel chrome + pill) collapses back to just the pill.\n      if (open) {\n        attachComposerBarOutsideClickDismiss();\n        attachComposerBarEscapeDismiss();\n      } else {\n        detachComposerBarOutsideClickDismiss();\n        detachComposerBarEscapeDismiss();\n      }\n      // Peek banner is hidden when expanded (`open === true` short-circuits\n      // visibility); re-sync so collapsing back re-evaluates immediately.\n      syncComposerBarPeek();\n      return;\n    }\n\n    const dockedMode = isDockedMountMode(config);\n    const ownerWindow = mount.ownerDocument.defaultView ?? window;\n    const mobileBreakpoint = config.launcher?.mobileBreakpoint ?? 640;\n    const mobileFullscreen = config.launcher?.mobileFullscreen ?? true;\n    const isMobileViewport = ownerWindow.innerWidth <= mobileBreakpoint;\n    const shouldGoFullscreen = mobileFullscreen && isMobileViewport && launcherEnabled;\n    const dockReveal = resolveDockConfig(config).reveal;\n    const dockRevealUsesTransform =\n      dockedMode && (dockReveal === \"overlay\" || dockReveal === \"push\") && !shouldGoFullscreen;\n\n    if (open) {\n      // Clear any display:none !important from a closed docked state so mobile fullscreen\n      // (display:flex !important) and dock layout can apply in recalcPanelHeight.\n      wrapper.style.removeProperty(\"display\");\n      wrapper.style.display = dockedMode ? \"flex\" : \"\";\n      wrapper.classList.remove(\"persona-pointer-events-none\", \"persona-opacity-0\");\n      panel.classList.remove(\"persona-scale-95\", \"persona-opacity-0\");\n      panel.classList.add(\"persona-scale-100\", \"persona-opacity-100\");\n      // Hide launcher button when widget is open\n      if (launcherButtonInstance) {\n        launcherButtonInstance.element.style.display = \"none\";\n      } else if (customLauncherElement) {\n        customLauncherElement.style.display = \"none\";\n      }\n    } else {\n      if (dockedMode) {\n        if (dockRevealUsesTransform) {\n          // Slide/push reveal: keep the panel painted so host-layout `transform` can animate.\n          wrapper.style.removeProperty(\"display\");\n          wrapper.style.display = \"flex\";\n          wrapper.classList.remove(\"persona-pointer-events-none\", \"persona-opacity-0\");\n          panel.classList.remove(\"persona-scale-100\", \"persona-opacity-100\", \"persona-scale-95\", \"persona-opacity-0\");\n        } else {\n          // Must beat applyFullHeightStyles() mobile shell: display:flex !important on wrapper\n          wrapper.style.setProperty(\"display\", \"none\", \"important\");\n          wrapper.classList.remove(\"persona-pointer-events-none\", \"persona-opacity-0\");\n          panel.classList.remove(\"persona-scale-100\", \"persona-opacity-100\", \"persona-scale-95\", \"persona-opacity-0\");\n        }\n      } else {\n        wrapper.style.display = \"\";\n        wrapper.classList.add(\"persona-pointer-events-none\", \"persona-opacity-0\");\n        panel.classList.remove(\"persona-scale-100\", \"persona-opacity-100\");\n        panel.classList.add(\"persona-scale-95\", \"persona-opacity-0\");\n      }\n      // Show launcher when closed, except docked mode (0px column: use controller.open()).\n      if (launcherButtonInstance) {\n        launcherButtonInstance.element.style.display = dockedMode ? \"none\" : \"\";\n      } else if (customLauncherElement) {\n        customLauncherElement.style.display = dockedMode ? \"none\" : \"\";\n      }\n    }\n  };\n\n  const setOpenState = (nextOpen: boolean, source: \"user\" | \"auto\" | \"api\" | \"system\" = \"user\") => {\n    if (!isPanelToggleable()) return;\n    if (open === nextOpen) return;\n    \n    const prevOpen = open;\n    open = nextOpen;\n    updateOpenState();\n\n    // Sync host stacking and scroll lock for viewport-covering modes\n    const isViewportCovering = (() => {\n      const sm = config.launcher?.sidebarMode ?? false;\n      const ow = mount.ownerDocument.defaultView ?? window;\n      const mf = config.launcher?.mobileFullscreen ?? true;\n      const mb = config.launcher?.mobileBreakpoint ?? 640;\n      const isMobile = ow.innerWidth <= mb;\n      const dockedMF = isDockedMountMode(config) && mf && isMobile;\n      // Composer-bar in expanded fullscreen mode covers the viewport: lock\n      // background scroll and elevate host stacking to match other\n      // viewport-covering modes (mobile fullscreen, sidebar).\n      const composerBarFS =\n        isComposerBar() &&\n        (config.launcher?.composerBar?.expandedSize ?? \"fullscreen\") === \"fullscreen\";\n      return sm || (mf && isMobile && launcherEnabled) || dockedMF || composerBarFS;\n    })();\n\n    if (open && isViewportCovering) {\n      if (!teardownHostStacking) {\n        const root = mount.getRootNode();\n        const hostEl = root instanceof ShadowRoot\n          ? (root.host as HTMLElement)\n          : mount.closest<HTMLElement>(\".persona-host\");\n        if (hostEl) {\n          teardownHostStacking = syncOverlayHostStacking(\n            hostEl,\n            config.launcher?.zIndex ?? DEFAULT_OVERLAY_Z_INDEX\n          );\n        }\n      }\n      if (!releaseScrollLock) {\n        releaseScrollLock = acquireScrollLock(mount.ownerDocument);\n      }\n    } else if (!open) {\n      teardownHostStacking?.();\n      teardownHostStacking = null;\n      releaseScrollLock?.();\n      releaseScrollLock = null;\n    }\n\n    if (open) {\n      recalcPanelHeight();\n      // Reopen-where-left-off takes precedence when opted in (Principle 11);\n      // otherwise fall back to the historical per-mode positioning.\n      if (!restoreScrollPosition()) {\n        if (getScrollMode() === \"follow\") {\n          scheduleAutoScroll(true);\n        } else {\n          // Non-follow modes still start at the latest content when the panel\n          // opens; they just never chase it during streaming.\n          jumpToBottomInstant();\n        }\n      }\n    }\n\n    // Emit widget state events\n    const stateEvent: AgentWidgetStateEvent = {\n      open,\n      source,\n      timestamp: Date.now()\n    };\n    \n    if (open && !prevOpen) {\n      eventBus.emit(\"widget:opened\", stateEvent);\n    } else if (!open && prevOpen) {\n      eventBus.emit(\"widget:closed\", stateEvent);\n    }\n    \n    // Emit general state snapshot\n    eventBus.emit(\"widget:state\", {\n      open,\n      launcherEnabled,\n      voiceActive: voiceState.active,\n      streaming: session.isStreaming()\n    });\n  };\n\n  const setComposerDisabled = (disabled: boolean) => {\n    // The send button stays enabled while streaming: it doubles as a stop\n    // button. Ancillary controls (mic, suggestions, opt-in targets) still\n    // disable so the user can't race a send against an in-flight stream.\n    setSendButtonMode(disabled ? \"stop\" : \"send\");\n    if (micButton) {\n      micButton.disabled = disabled;\n    }\n    suggestionsManager.buttons.forEach((btn) => {\n      btn.disabled = disabled;\n    });\n    footer.dataset.personaComposerStreaming = disabled ? \"true\" : \"false\";\n    footer.querySelectorAll<HTMLElement>(\"[data-persona-composer-disable-when-streaming]\").forEach((el) => {\n      if (\n        el instanceof HTMLButtonElement ||\n        el instanceof HTMLInputElement ||\n        el instanceof HTMLTextAreaElement ||\n        el instanceof HTMLSelectElement\n      ) {\n        el.disabled = disabled;\n      }\n    });\n  };\n\n  const maybeFocusInput = () => {\n    if (voiceState.active) return;\n    if (!textarea) return;\n    textarea.focus();\n  };\n\n  eventBus.on(\"widget:opened\", () => {\n    if (config.autoFocusInput) setTimeout(() => maybeFocusInput(), 200);\n  });\n\n  const updateCopy = () => {\n    introTitle.textContent = config.copy?.welcomeTitle ?? \"Hello 👋\";\n    introSubtitle.textContent =\n      config.copy?.welcomeSubtitle ??\n      \"Ask anything about your account or products.\";\n    textarea.placeholder = config.copy?.inputPlaceholder ?? \"How can I help...\";\n\n    // Toggle welcome card visibility\n    const introCard = body.querySelector(\"[data-persona-intro-card]\") as HTMLElement | null;\n    if (introCard) {\n      const showCard = config.copy?.showWelcomeCard !== false;\n      introCard.style.display = showCard ? \"\" : \"none\";\n      if (showCard) {\n        body.classList.remove(\"persona-gap-3\");\n        body.classList.add(\"persona-gap-6\");\n      } else {\n        body.classList.remove(\"persona-gap-6\");\n        body.classList.add(\"persona-gap-3\");\n      }\n    }\n\n    // Only update send button text if NOT using icon mode. Skip while\n    // streaming so we don't stomp on the \"Stop\" label.\n    const useIcon = config.sendButton?.useIcon ?? false;\n    if (!useIcon && !session?.isStreaming()) {\n      sendButton.textContent = config.copy?.sendButtonLabel ?? \"Send\";\n    }\n\n    textarea.style.fontFamily =\n      'var(--persona-input-font-family, var(--persona-font-family, -apple-system, BlinkMacSystemFont, \"Segoe UI\", \"Helvetica Neue\", Arial, sans-serif))';\n    textarea.style.fontWeight = \"var(--persona-input-font-weight, var(--persona-font-weight, 400))\";\n  };\n\n  // Add session ID persistence callbacks for client token mode\n  // These allow the widget to resume conversations by passing session_id to /client/init\n  if (config.clientToken) {\n    config = {\n      ...config,\n      getStoredSessionId: () => {\n        const storedId = persistentMetadata['sessionId'];\n        return typeof storedId === 'string' ? storedId : null;\n      },\n      setStoredSessionId: (sessionId: string) => {\n        updateSessionMetadata((prev) => ({\n          ...prev,\n          sessionId: sessionId,\n        }));\n      },\n    };\n  }\n\n  // Global timer for live-updating tool elapsed time spans.\n  // Runs at 100ms while any [data-tool-elapsed] span exists in the message area,\n  // auto-stops when none remain. Operates on real DOM after morph, not temp elements.\n  let toolElapsedTimerId: ReturnType<typeof setInterval> | null = null;\n  const ensureToolElapsedTimer = () => {\n    if (toolElapsedTimerId != null) return;\n    toolElapsedTimerId = setInterval(() => {\n      const spans = messagesWrapper.querySelectorAll<HTMLElement>(\"[data-tool-elapsed]\");\n      if (spans.length === 0) {\n        clearInterval(toolElapsedTimerId!);\n        toolElapsedTimerId = null;\n        return;\n      }\n      const now = Date.now();\n      spans.forEach((span) => {\n        const startedAt = Number(span.getAttribute(\"data-tool-elapsed\"));\n        if (!startedAt) return;\n        span.textContent = formatElapsedMs(now - startedAt);\n      });\n    }, 100);\n  };\n\n  session = new AgentWidgetSession(config, {\n    onMessagesChanged(messages) {\n      renderMessagesWithPlugins(messagesWrapper, messages, postprocess);\n      // Start elapsed timer if any active tool has a live duration span\n      ensureToolElapsedTimer();\n      // Re-render suggestions: agent chips vs config chips, one shared rule.\n      // Pass messages directly to avoid calling session.getMessages() during construction\n      renderSuggestions(messages);\n      scheduleAutoScroll(!isStreaming);\n      trackMessages(messages);\n\n      const lastUserMessage = [...messages]\n        .reverse()\n        .find((msg) => msg.role === \"user\");\n      const lastAssistantMessage = [...messages]\n        .reverse()\n        .find((msg) => msg.role === \"assistant\");\n\n      // Scroll-on-send / anchor-top. Seeded so restored history (constructor\n      // initialMessages and async storage hydration) never reads as a fresh\n      // send; clearing the chat resets any anchor spacer.\n      if (messages.length === 0) {\n        resetAnchorState();\n        // Cleared: nothing anchored, so re-arm the no-anchor follow fallback.\n        followFallbackActive = true;\n        currentTurnAnchored = false;\n      }\n      if (!scrollSendSeeded || suppressScrollSend) {\n        scrollSendSeeded = true;\n        lastSentUserMessageId = lastUserMessage?.id ?? null;\n        // Seed assistant-turn tracking too, so restored history doesn't read\n        // as a fresh assistant turn and trigger the no-anchor fallback.\n        lastHandledAssistantId = lastAssistantMessage?.id ?? null;\n      } else if (lastUserMessage && lastUserMessage.id !== lastSentUserMessageId) {\n        lastSentUserMessageId = lastUserMessage.id;\n        handleUserMessageSent(lastUserMessage.id);\n      } else if (\n        lastAssistantMessage &&\n        lastAssistantMessage.id !== lastHandledAssistantId\n      ) {\n        // A new assistant turn with no fresh user send: the anchor-top\n        // no-anchor fallback (proactive/injected/resubmit/first-load streaming).\n        handleAssistantTurnStarted();\n      }\n      if (lastAssistantMessage) {\n        lastHandledAssistantId = lastAssistantMessage.id;\n      }\n\n      // Emit user:message event when a new user message is detected\n      const prevLastUserMessageId = voiceState.lastUserMessageId;\n      if (lastUserMessage && lastUserMessage.id !== prevLastUserMessageId) {\n        voiceState.lastUserMessageId = lastUserMessage.id;\n        eventBus.emit(\"user:message\", lastUserMessage);\n      }\n\n      voiceState.lastUserMessageWasVoice = Boolean(lastUserMessage?.viaVoice);\n      persistState(messages);\n      // Composer-bar peek: re-render the trailing-100-char preview and\n      // re-evaluate visibility (a new message may make it eligible to show\n      // during streaming, or update the preview text on each token).\n      syncComposerBarPeek();\n    },\n    onStatusChanged(status) {\n      const currentStatusConfig = config.statusIndicator ?? {};\n      const getCurrentStatusText = (s: AgentWidgetSessionStatus): string => {\n        if (s === \"idle\") return currentStatusConfig.idleText ?? statusCopy.idle;\n        if (s === \"connecting\") return currentStatusConfig.connectingText ?? statusCopy.connecting;\n        if (s === \"connected\") return currentStatusConfig.connectedText ?? statusCopy.connected;\n        if (s === \"error\") return currentStatusConfig.errorText ?? statusCopy.error;\n        return statusCopy[s];\n      };\n      applyStatusToElement(statusText, getCurrentStatusText(status), currentStatusConfig, status);\n    },\n    onStreamingChanged(streaming) {\n      isStreaming = streaming;\n      setComposerDisabled(streaming);\n      // Re-render messages to show/hide typing indicator\n      if (session) {\n        renderMessagesWithPlugins(messagesWrapper, session.getMessages(), postprocess);\n      }\n      if (!streaming) {\n        scheduleAutoScroll(true);\n      }\n      // Keep the \"streaming below\" hint and its announcement in sync with the\n      // streaming lifecycle (Principles 8 + 15).\n      syncScrollToBottomButton();\n      announce(streaming ? \"Responding…\" : \"Response complete.\");\n      // Composer-bar peek: streaming state is one of the two visibility\n      // triggers (the other is composer hover), so re-evaluate now.\n      syncComposerBarPeek();\n    },\n    onVoiceStatusChanged(status: VoiceStatus) {\n      // Surface the granular status publicly so consumers can render their own\n      // per-state UI (e.g. a listening/speaking status dock). Fires for every\n      // provider; the mic-button styling below is runtype-specific.\n      eventBus.emit(\"voice:status\", { status, timestamp: Date.now() });\n      if (config.voiceRecognition?.provider?.type !== 'runtype') return;\n\n      switch (status) {\n        case 'listening':\n          // A continuous realtime call re-enters `listening` after every spoken\n          // reply, so reassert the recording styles here (they were replaced by\n          // the `processing`/`speaking` states during the turn). The initial\n          // listen is also styled by the toggleVoice()/startVoiceRecognition()\n          // flows; reapplying is idempotent.\n          removeRuntypeMicStateStyles();\n          applyRuntypeMicRecordingStyles();\n          break;\n        case 'processing':\n          removeRuntypeMicStateStyles();\n          applyRuntypeMicProcessingStyles();\n          break;\n        case 'speaking':\n          removeRuntypeMicStateStyles();\n          applyRuntypeMicSpeakingStyles();\n          break;\n        default:\n          // idle, connected, disconnected, error\n          if (status === 'idle' && session.isBargeInActive()) {\n            // Barge-in mic is still hot between turns: show it as active\n            removeRuntypeMicStateStyles();\n            applyRuntypeMicRecordingStyles();\n            micButton?.setAttribute(\"aria-label\", \"End voice session\");\n          } else {\n            voiceState.active = false;\n            removeRuntypeMicStateStyles();\n            emitVoiceState(\"system\");\n            persistVoiceMetadata();\n          }\n          break;\n      }\n    },\n    onArtifactsState(state) {\n      lastArtifactsState = state;\n      syncArtifactPane();\n      persistState();\n    }\n  });\n\n  sessionRef.current = session;\n\n  // Mirror read-aloud playback state into the action buttons, and surface it as\n  // a controller event (parallel to message:copy / message:feedback).\n  let lastReadAloudId: string | null = null;\n  session.onReadAloudChange((activeId, state) => {\n    readAloudActiveId = activeId;\n    readAloudActiveState = state;\n    refreshReadAloudButtons();\n\n    // On the terminal `idle` transition activeId is null, so fall back to the\n    // last active id to identify the message that just finished/stopped.\n    const messageId = activeId ?? lastReadAloudId;\n    if (activeId) lastReadAloudId = activeId;\n    const message = messageId\n      ? session.getMessages().find((m) => m.id === messageId) ?? null\n      : null;\n    eventBus.emit(\"message:read-aloud\", {\n      messageId,\n      message,\n      state,\n      timestamp: Date.now(),\n    });\n    if (state === \"idle\") lastReadAloudId = null;\n  });\n\n  // The constructor only emits onMessagesChanged when it has initial\n  // messages, so seed send-detection explicitly for the empty-session case:  // otherwise the user's very first send would be mistaken for the seed.\n  scrollSendSeeded = true;\n\n  // Setup Runtype voice provider when configured (connects WebSocket for server-side STT)\n  if (config.voiceRecognition?.provider?.type === 'runtype') {\n    try {\n      session.setupVoice();\n    } catch (err) {\n      if (typeof console !== 'undefined') {\n        // eslint-disable-next-line no-console\n        console.warn('[AgentWidget] Runtype voice setup failed:', err);\n      }\n    }\n  }\n\n  // Pre-initialize client session when in client token mode so feedback works\n  // before the user sends their first message (e.g. on restored/persisted messages)\n  if (config.clientToken) {\n    session.initClientSession().catch((err) => {\n      if (config.debug) {\n        // eslint-disable-next-line no-console\n        console.warn(\"[AgentWidget] Pre-init client session failed:\", err);\n      }\n    });\n  }\n\n  // Wire up optional SSE tap (host) + event stream buffer to capture SSE events\n  if (eventStreamBuffer || config.onSSEEvent) {\n    session.setSSEEventCallback((type: string, payload: unknown) => {\n      config.onSSEEvent?.(type, payload);\n      throughputTracker?.processEvent(type, payload);\n      eventStreamBuffer?.push({\n        id: `evt-${Date.now()}-${Math.random().toString(36).slice(2, 8)}`,\n        type,\n        timestamp: Date.now(),\n        payload: JSON.stringify(payload)\n      });\n    });\n  }\n\n  if (pendingStoredState) {\n    pendingStoredState\n      .then((state) => {\n        if (!state) return;\n        if (state.metadata) {\n          persistentMetadata = ensureRecord(state.metadata);\n          actionManager.syncFromMetadata();\n        }\n        if (state.messages?.length) {\n          // Restored history must not read as a fresh send (scroll-on-send /\n          // anchor-top would fire for the last restored user message).\n          suppressScrollSend = true;\n          try {\n            session.hydrateMessages(state.messages);\n          } finally {\n            suppressScrollSend = false;\n          }\n        }\n        if (state.artifacts?.length) {\n          session.hydrateArtifacts(\n            state.artifacts,\n            state.selectedArtifactId ?? null\n          );\n        }\n      })\n      .catch((error) => {\n        if (typeof console !== \"undefined\") {\n          // eslint-disable-next-line no-console\n          console.error(\"[AgentWidget] Failed to hydrate stored state:\", error);\n        }\n      });\n  }\n\n  // Centralized so both the default composer (`handleSubmit`) and the plugin\n  // composer (`renderComposer.onSubmit`) auto-expand the composer-bar wrapper\n  // when a message is sent while the panel is collapsed. Without a single\n  // helper the two submit paths drift over time.\n  const maybeExpandComposerBar = () => {\n    if (!isComposerBar()) return;\n    if (open) return;\n    const expandOnSubmit = config.launcher?.composerBar?.expandOnSubmit ?? true;\n    if (!expandOnSubmit) return;\n    setOpenState(true, \"auto\");\n  };\n\n  const handleSubmit = (event: Event) => {\n    event.preventDefault();\n\n    // While a response is streaming, the submit button acts as a stop button.\n    // Abort the in-flight stream and leave textarea contents / attachments\n    // intact so the user can edit and resend without retyping.\n    if (session.isStreaming()) {\n      session.cancel();\n      // Cancelling emits no terminal/error SSE frame, so reset the throughput\n      // tracker (as clear-chat does) to avoid a stale `running` row lingering.\n      throughputTracker?.reset();\n      eventStreamView?.update();\n      return;\n    }\n\n    const value = textarea.value.trim();\n    const hasAttachments = attachmentManager?.hasAttachments() ?? false;\n\n    // Must have text or attachments to send\n    if (!value && !hasAttachments) return;\n\n    maybeExpandComposerBar();\n\n    // Build content parts if there are attachments\n    let contentParts: ContentPart[] | undefined;\n    if (hasAttachments) {\n      contentParts = [];\n      // Add image parts first\n      contentParts.push(...attachmentManager!.getContentParts());\n      // Add text part if there's text\n      if (value) {\n        contentParts.push(createTextPart(value));\n      }\n    }\n\n    textarea.value = \"\";\n    textarea.style.height = \"auto\"; // Reset height after clearing\n    resetHistoryNavigation();\n\n    // Send message with optional content parts\n    session.sendMessage(value, { contentParts });\n\n    // Clear attachments after sending\n    if (hasAttachments) {\n      attachmentManager!.clearAttachments();\n    }\n  };\n\n  // --- Composer message-history navigation (Up/Down arrows) ---\n  // Lets users recall and edit previously sent messages, shell/Slack style.\n  // The pure state machine lives in utils/composer-history.ts; here we feed it\n  // caret info and apply the value it returns. Text-only recall: attachments\n  // on past messages are not restored.\n  const historyNavigationEnabled = () =>\n    config.features?.composerHistory !== false;\n\n  let composerHistoryState: ComposerHistoryState = { ...INITIAL_HISTORY_STATE };\n  // Guards the reset-on-edit listener so our own programmatic value sets (which\n  // dispatch an `input` event for auto-resize) don't exit navigation mode.\n  let suppressHistoryReset = false;\n\n  const resetHistoryNavigation = () => {\n    composerHistoryState = { ...INITIAL_HISTORY_STATE };\n  };\n\n  const getUserMessageHistory = (): string[] =>\n    session\n      .getMessages()\n      .filter((message) => message.role === \"user\")\n      .map((message) => message.content ?? \"\")\n      .filter((text) => text.length > 0);\n\n  const applyHistoryValue = (value: string) => {\n    if (!textarea) return;\n    suppressHistoryReset = true;\n    textarea.value = value;\n    // Trigger the auto-resize handler (it listens on `input`).\n    textarea.dispatchEvent(new Event(\"input\", { bubbles: true }));\n    suppressHistoryReset = false;\n    // Caret to end for natural editing / appending.\n    const end = textarea.value.length;\n    textarea.setSelectionRange(end, end);\n  };\n\n  const handleComposerInput = () => {\n    // A real edit leaves history-navigation mode.\n    if (suppressHistoryReset) return;\n    resetHistoryNavigation();\n  };\n\n  const handleComposerKeydown = (event: KeyboardEvent) => {\n    if (!textarea) return;\n\n    // Up/Down: walk through previously sent user messages.\n    if (\n      historyNavigationEnabled() &&\n      (event.key === \"ArrowUp\" || event.key === \"ArrowDown\") &&\n      !event.shiftKey &&\n      !event.metaKey &&\n      !event.ctrlKey &&\n      !event.altKey &&\n      !event.isComposing\n    ) {\n      const atStart =\n        textarea.selectionStart === 0 && textarea.selectionEnd === 0;\n      const result = navigateComposerHistory({\n        direction: event.key === \"ArrowUp\" ? \"up\" : \"down\",\n        history: getUserMessageHistory(),\n        currentValue: textarea.value,\n        atStart,\n        state: composerHistoryState\n      });\n      composerHistoryState = result.state;\n      if (result.handled) {\n        event.preventDefault();\n        if (result.value !== undefined) {\n          applyHistoryValue(result.value);\n        }\n        return;\n      }\n      // Not handled: fall through to default cursor movement.\n    }\n\n    // Enter: send, unless a response is streaming. While streaming, Enter is\n    // inert (never a stop trigger): the visible Stop button / Esc stop it.\n    if (event.key === \"Enter\" && !event.shiftKey) {\n      if (session.isStreaming()) {\n        event.preventDefault();\n        return;\n      }\n      resetHistoryNavigation();\n      event.preventDefault();\n      sendButton.click();\n    }\n  };\n\n  // Esc-to-stop: while a response streams, Escape within this widget aborts it.\n  // Capture phase + registered at init so it runs before the composer-bar Esc\n  // collapse listener (attached later on open); stopImmediatePropagation keeps\n  // a stream-stop from also collapsing the panel. Scoped via composedPath so a\n  // page-wide Escape elsewhere doesn't hijack.\n  const handleEscStop = (event: KeyboardEvent) => {\n    if (event.key !== \"Escape\" || event.isComposing) return;\n    if (!session.isStreaming()) return;\n    if (!event.composedPath().includes(container)) return;\n    session.cancel();\n    // Cancelling emits no terminal/error SSE frame: reset throughput so the\n    // Events row doesn't keep showing a live rate from the stopped stream.\n    throughputTracker?.reset();\n    eventStreamView?.update();\n    resetHistoryNavigation();\n    event.preventDefault();\n    event.stopImmediatePropagation();\n  };\n\n  const handleInputPaste = async (event: ClipboardEvent) => {\n    if (config.attachments?.enabled !== true || !attachmentManager) return;\n\n    const clipboardImageFiles = getClipboardImageFiles(event.clipboardData);\n    if (clipboardImageFiles.length === 0) return;\n\n    // Prevent browser text/html paste when handling clipboard images as attachments.\n    event.preventDefault();\n    await attachmentManager.handleFiles(clipboardImageFiles);\n  };\n\n  // Voice recognition state and logic\n  let speechRecognition: any = null;\n  let isRecording = false;\n  let pauseTimer: number | null = null;\n  let originalMicStyles: {\n    backgroundColor: string;\n    color: string;\n    borderColor: string;\n    iconName: string;\n    iconSize: number;\n  } | null = null;\n\n  const getSpeechRecognitionClass = (): any => {\n    if (typeof window === 'undefined') return null;\n    return (window as any).webkitSpeechRecognition || (window as any).SpeechRecognition || null;\n  };\n\n  const startVoiceRecognition = (\n    source: AgentWidgetVoiceStateEvent[\"source\"] = \"user\"\n  ) => {\n    if (isRecording || session.isStreaming()) return;\n\n    const SpeechRecognitionClass = getSpeechRecognitionClass();\n    if (!SpeechRecognitionClass) return;\n\n    speechRecognition = new SpeechRecognitionClass();\n    const voiceConfig = config.voiceRecognition ?? {};\n    const pauseDuration = voiceConfig.pauseDuration ?? 2000;\n\n    speechRecognition.continuous = true;\n    speechRecognition.interimResults = true;\n    speechRecognition.lang = 'en-US';\n\n    // Store the initial text that was in the textarea\n    const initialText = textarea.value;\n\n    speechRecognition.onresult = (event: any) => {\n      // Build the complete transcript from all results\n      let fullTranscript = \"\";\n      let interimTranscript = \"\";\n      \n      // Process all results from the beginning\n      for (let i = 0; i < event.results.length; i++) {\n        const result = event.results[i];\n        const transcript = result[0].transcript;\n        \n        if (result.isFinal) {\n          fullTranscript += transcript + \" \";\n        } else {\n          // Only take the last interim result\n          interimTranscript = transcript;\n        }\n      }\n      \n      // Update textarea with initial text + full transcript + interim\n      const newValue = initialText + fullTranscript + interimTranscript;\n      textarea.value = newValue;\n\n      // Reset pause timer on each result\n      if (pauseTimer) {\n        clearTimeout(pauseTimer);\n      }\n\n      // Set timer to auto-submit after pause when we have any speech\n      if (fullTranscript || interimTranscript) {\n        pauseTimer = window.setTimeout(() => {\n          const finalValue = textarea.value.trim();\n          if (finalValue && speechRecognition && isRecording) {\n            stopVoiceRecognition();\n            textarea.value = \"\";\n            textarea.style.height = \"auto\"; // Reset height after clearing\n            session.sendMessage(finalValue, { viaVoice: true });\n          }\n        }, pauseDuration);\n      }\n    };\n\n    speechRecognition.onerror = (event: any) => {\n      // Don't stop on \"no-speech\" error, just ignore it\n      if (event.error !== 'no-speech') {\n        stopVoiceRecognition();\n      }\n    };\n\n    speechRecognition.onend = () => {\n      // If recognition ended naturally (not manually stopped), submit if there's text\n      if (isRecording) {\n        const finalValue = textarea.value.trim();\n        if (finalValue && finalValue !== initialText.trim()) {\n          textarea.value = \"\";\n          textarea.style.height = \"auto\"; // Reset height after clearing\n          session.sendMessage(finalValue, { viaVoice: true });\n        }\n        stopVoiceRecognition();\n      }\n    };\n\n    try {\n      speechRecognition.start();\n      isRecording = true;\n      voiceState.active = true;\n      if (source !== \"system\") {\n        voiceState.manuallyDeactivated = false;\n      }\n      emitVoiceState(source);\n      persistVoiceMetadata();\n      if (micButton) {\n        // Store original styles (including icon info for restoration)\n        const voiceConfig = config.voiceRecognition ?? {};\n        originalMicStyles = {\n          backgroundColor: micButton.style.backgroundColor,\n          color: micButton.style.color,\n          borderColor: micButton.style.borderColor,\n          iconName: voiceConfig.iconName ?? \"mic\",\n          iconSize: parseFloat(voiceConfig.iconSize ?? config.sendButton?.size ?? \"40\") || 24,\n        };\n\n        // Apply recording state styles from config or theme tokens\n        const recordingBackgroundColor = voiceConfig.recordingBackgroundColor;\n        const recordingIconColor = voiceConfig.recordingIconColor;\n        const recordingBorderColor = voiceConfig.recordingBorderColor;\n\n        micButton.classList.add(\"persona-voice-recording\");\n        micButton.style.backgroundColor = recordingBackgroundColor ?? \"var(--persona-voice-recording-bg, #ef4444)\";\n        micButton.style.color = recordingIconColor ?? \"var(--persona-voice-recording-indicator, #ffffff)\";\n\n        if (recordingIconColor) {\n          const svg = micButton.querySelector(\"svg\");\n          if (svg) {\n            svg.setAttribute(\"stroke\", recordingIconColor);\n          }\n        }\n        \n        if (recordingBorderColor) {\n          micButton.style.borderColor = recordingBorderColor;\n        }\n        \n        micButton.setAttribute(\"aria-label\", \"Stop voice recognition\");\n      }\n    } catch (error) {\n      stopVoiceRecognition(\"system\");\n    }\n  };\n\n  const stopVoiceRecognition = (\n    source: AgentWidgetVoiceStateEvent[\"source\"] = \"user\"\n  ) => {\n    if (!isRecording) return;\n\n    isRecording = false;\n    if (pauseTimer) {\n      clearTimeout(pauseTimer);\n      pauseTimer = null;\n    }\n\n    if (speechRecognition) {\n      try {\n        speechRecognition.stop();\n      } catch (error) {\n        // Ignore errors when stopping\n      }\n      speechRecognition = null;\n    }\n\n    voiceState.active = false;\n    emitVoiceState(source);\n    persistVoiceMetadata();\n\n    if (micButton) {\n      micButton.classList.remove(\"persona-voice-recording\");\n      \n      // Restore original styles\n      if (originalMicStyles) {\n        micButton.style.backgroundColor = originalMicStyles.backgroundColor;\n        micButton.style.color = originalMicStyles.color;\n        micButton.style.borderColor = originalMicStyles.borderColor;\n        \n        // Restore SVG stroke color if present\n        const svg = micButton.querySelector(\"svg\");\n        if (svg) {\n          svg.setAttribute(\"stroke\", originalMicStyles.color || \"currentColor\");\n        }\n        \n        originalMicStyles = null;\n      }\n      \n      micButton.setAttribute(\"aria-label\", \"Start voice recognition\");\n    }\n  };\n\n  // Function to create mic button dynamically\n  const createMicButton = (voiceConfig: AgentWidgetConfig['voiceRecognition'], sendButtonConfig: AgentWidgetConfig['sendButton']): { micButton: HTMLButtonElement; micButtonWrapper: HTMLElement } | null => {\n    const hasSpeechRecognition =\n      typeof window !== 'undefined' &&\n      (typeof (window as any).webkitSpeechRecognition !== 'undefined' ||\n       typeof (window as any).SpeechRecognition !== 'undefined');\n    const hasRuntypeProvider = voiceConfig?.provider?.type === 'runtype';\n    // Bring-your-own (`custom`) providers own their own input pipeline (cloud\n    // STT, etc.), so the mic should render regardless of Web Speech support.\n    const hasCustomProvider = voiceConfig?.provider?.type === 'custom';\n    const hasVoiceInput = hasSpeechRecognition || hasRuntypeProvider || hasCustomProvider;\n\n    if (!hasVoiceInput) return null;\n\n    const micButtonWrapper = createElement(\"div\", \"persona-send-button-wrapper\");\n    const micButton = createElement(\n      \"button\",\n      \"persona-rounded-button persona-flex persona-items-center persona-justify-center disabled:persona-opacity-50 persona-cursor-pointer\"\n    ) as HTMLButtonElement;\n    \n    micButton.type = \"button\";\n    micButton.setAttribute(\"aria-label\", \"Start voice recognition\");\n    \n    const micIconName = voiceConfig?.iconName ?? \"mic\";\n    const buttonSize = sendButtonConfig?.size ?? \"40px\";\n    const micIconSize = voiceConfig?.iconSize ?? buttonSize;\n    const micIconSizeNum = parseFloat(micIconSize) || 24;\n    \n    // Use dedicated colors from voice recognition config, fallback to send button colors\n    const backgroundColor = voiceConfig?.backgroundColor ?? sendButtonConfig?.backgroundColor;\n    const iconColor = voiceConfig?.iconColor ?? sendButtonConfig?.textColor;\n    \n    micButton.style.width = micIconSize;\n    micButton.style.height = micIconSize;\n    micButton.style.minWidth = micIconSize;\n    micButton.style.minHeight = micIconSize;\n    micButton.style.fontSize = \"18px\";\n    micButton.style.lineHeight = \"1\";\n    \n    // Set mic button foreground from config or theme token\n    if (iconColor) {\n      micButton.style.color = iconColor;\n    } else {\n      micButton.style.color = \"var(--persona-text, #111827)\";\n    }\n\n    // Use Lucide mic icon (stroke width 1.5 for minimalist outline style)\n    const iconColorValue = iconColor || \"currentColor\";\n    const micIconSvg = renderLucideIcon(micIconName, micIconSizeNum, iconColorValue, 1.5);\n    if (micIconSvg) {\n      micButton.appendChild(micIconSvg);\n    } else {\n      micButton.textContent = \"🎤\";\n    }\n\n    // Apply background color\n    if (backgroundColor) {\n      micButton.style.backgroundColor = backgroundColor;\n    } else {\n      micButton.style.backgroundColor = \"\";\n    }\n    \n    // Apply border styling\n    if (voiceConfig?.borderWidth) {\n      micButton.style.borderWidth = voiceConfig.borderWidth;\n      micButton.style.borderStyle = \"solid\";\n    }\n    if (voiceConfig?.borderColor) {\n      micButton.style.borderColor = voiceConfig.borderColor;\n    }\n    \n    // Apply padding styling\n    if (voiceConfig?.paddingX) {\n      micButton.style.paddingLeft = voiceConfig.paddingX;\n      micButton.style.paddingRight = voiceConfig.paddingX;\n    }\n    if (voiceConfig?.paddingY) {\n      micButton.style.paddingTop = voiceConfig.paddingY;\n      micButton.style.paddingBottom = voiceConfig.paddingY;\n    }\n    \n    micButtonWrapper.appendChild(micButton);\n    \n    // Add tooltip if enabled\n    const tooltipText = voiceConfig?.tooltipText ?? \"Start voice recognition\";\n    const showTooltip = voiceConfig?.showTooltip ?? false;\n    if (showTooltip && tooltipText) {\n      const tooltip = createElement(\"div\", \"persona-send-button-tooltip\");\n      tooltip.textContent = tooltipText;\n      micButtonWrapper.appendChild(tooltip);\n    }\n    \n    return { micButton, micButtonWrapper };\n  };\n\n  // --- Helpers to store/restore original mic button state ---\n\n  const storeOriginalMicStyles = () => {\n    if (!micButton || originalMicStyles) return; // Already stored\n    const voiceConfig = config.voiceRecognition ?? {};\n    originalMicStyles = {\n      backgroundColor: micButton.style.backgroundColor,\n      color: micButton.style.color,\n      borderColor: micButton.style.borderColor,\n      iconName: voiceConfig.iconName ?? \"mic\",\n      iconSize: parseFloat(voiceConfig.iconSize ?? config.sendButton?.size ?? \"40\") || 24,\n    };\n  };\n\n  /** Swap the mic button's SVG icon */\n  const swapMicIcon = (iconName: string, color: string) => {\n    if (!micButton) return;\n    const existingSvg = micButton.querySelector(\"svg\");\n    if (existingSvg) existingSvg.remove();\n    const size = originalMicStyles?.iconSize ?? (parseFloat(config.voiceRecognition?.iconSize ?? config.sendButton?.size ?? \"40\") || 24);\n    const newSvg = renderLucideIcon(iconName, size, color, 1.5);\n    if (newSvg) micButton.appendChild(newSvg);\n  };\n\n  /** Remove all voice state CSS classes */\n  const removeAllVoiceStateClasses = () => {\n    if (!micButton) return;\n    micButton.classList.remove(\"persona-voice-recording\", \"persona-voice-processing\", \"persona-voice-speaking\");\n  };\n\n  // --- Per-state style application ---\n\n  const applyRuntypeMicRecordingStyles = () => {\n    if (!micButton) return;\n    storeOriginalMicStyles();\n    const voiceConfig = config.voiceRecognition ?? {};\n    const recordingBackgroundColor = voiceConfig.recordingBackgroundColor;\n    const recordingIconColor = voiceConfig.recordingIconColor;\n    const recordingBorderColor = voiceConfig.recordingBorderColor;\n    removeAllVoiceStateClasses();\n    micButton.classList.add(\"persona-voice-recording\");\n    micButton.style.backgroundColor = recordingBackgroundColor ?? \"var(--persona-voice-recording-bg, #ef4444)\";\n    micButton.style.color = recordingIconColor ?? \"var(--persona-voice-recording-indicator, #ffffff)\";\n    if (recordingIconColor) {\n      const svg = micButton.querySelector(\"svg\");\n      if (svg) svg.setAttribute(\"stroke\", recordingIconColor);\n    }\n    if (recordingBorderColor) micButton.style.borderColor = recordingBorderColor;\n    micButton.setAttribute(\"aria-label\", \"Stop voice recognition\");\n  };\n\n  const applyRuntypeMicProcessingStyles = () => {\n    if (!micButton) return;\n    storeOriginalMicStyles();\n    const voiceConfig = config.voiceRecognition ?? {};\n    const interruptionMode = session.getVoiceInterruptionMode();\n    const iconName = voiceConfig.processingIconName ?? \"loader\";\n    const iconColor = voiceConfig.processingIconColor ?? originalMicStyles?.color ?? \"\";\n    const bgColor = voiceConfig.processingBackgroundColor ?? originalMicStyles?.backgroundColor ?? \"\";\n    const borderColor = voiceConfig.processingBorderColor ?? originalMicStyles?.borderColor ?? \"\";\n\n    removeAllVoiceStateClasses();\n    micButton.classList.add(\"persona-voice-processing\");\n    micButton.style.backgroundColor = bgColor;\n    micButton.style.borderColor = borderColor;\n    const resolvedColor = iconColor || \"currentColor\";\n    micButton.style.color = resolvedColor;\n    swapMicIcon(iconName, resolvedColor);\n    micButton.setAttribute(\"aria-label\", \"Processing voice input\");\n    // In \"none\" mode the button is not actionable during processing\n    if (interruptionMode === \"none\") {\n      micButton.style.cursor = \"default\";\n    }\n  };\n\n  const applyRuntypeMicSpeakingStyles = () => {\n    if (!micButton) return;\n    storeOriginalMicStyles();\n    const voiceConfig = config.voiceRecognition ?? {};\n    const interruptionMode = session.getVoiceInterruptionMode();\n    // Default icon depends on interruption mode:\n    // \"square\" for cancel, \"mic\" for barge-in (hot mic), \"volume-2\" otherwise\n    const defaultSpeakingIcon = interruptionMode === \"cancel\" ? \"square\"\n      : interruptionMode === \"barge-in\" ? \"mic\"\n      : \"volume-2\";\n    const iconName = voiceConfig.speakingIconName ?? defaultSpeakingIcon;\n    const iconColor = voiceConfig.speakingIconColor\n      ?? (interruptionMode === \"barge-in\" ? (voiceConfig.recordingIconColor ?? originalMicStyles?.color ?? \"\") : (originalMicStyles?.color ?? \"\"));\n    const bgColor = voiceConfig.speakingBackgroundColor\n      ?? (interruptionMode === \"barge-in\" ? (voiceConfig.recordingBackgroundColor ?? \"var(--persona-voice-recording-bg, #ef4444)\") : (originalMicStyles?.backgroundColor ?? \"\"));\n    const borderColor = voiceConfig.speakingBorderColor\n      ?? (interruptionMode === \"barge-in\" ? (voiceConfig.recordingBorderColor ?? \"\") : (originalMicStyles?.borderColor ?? \"\"));\n\n    removeAllVoiceStateClasses();\n    micButton.classList.add(\"persona-voice-speaking\");\n    micButton.style.backgroundColor = bgColor;\n    micButton.style.borderColor = borderColor;\n    const resolvedColor = iconColor || \"currentColor\";\n    micButton.style.color = resolvedColor;\n    swapMicIcon(iconName, resolvedColor);\n\n    // aria-label varies by interruption mode\n    const ariaLabel = interruptionMode === \"cancel\"\n      ? \"Stop playback and re-record\"\n      : interruptionMode === \"barge-in\"\n      ? \"Speak to interrupt\"\n      : \"Agent is speaking\";\n    micButton.setAttribute(\"aria-label\", ariaLabel);\n    // In \"none\" mode the button is not actionable during speaking\n    if (interruptionMode === \"none\") {\n      micButton.style.cursor = \"default\";\n    }\n    // In \"barge-in\" mode, add recording class to show mic is hot\n    if (interruptionMode === \"barge-in\") {\n      micButton.classList.add(\"persona-voice-recording\");\n    }\n  };\n\n  /** Restore mic button to idle state (icon, colors, aria-label, cursor) */\n  const removeRuntypeMicStateStyles = () => {\n    if (!micButton) return;\n    removeAllVoiceStateClasses();\n    if (originalMicStyles) {\n      micButton.style.backgroundColor = originalMicStyles.backgroundColor ?? \"\";\n      micButton.style.color = originalMicStyles.color ?? \"\";\n      micButton.style.borderColor = originalMicStyles.borderColor ?? \"\";\n      swapMicIcon(originalMicStyles.iconName, originalMicStyles.color || \"currentColor\");\n      originalMicStyles = null;\n    }\n    micButton.style.cursor = \"\";\n    micButton.setAttribute(\"aria-label\", \"Start voice recognition\");\n  };\n\n  // Wire up mic button click handler\n  const handleMicButtonClick = () => {\n    // Runtype provider: use session.toggleVoice() (WebSocket-based STT)\n    if (config.voiceRecognition?.provider?.type === 'runtype') {\n      const voiceStatus = session.getVoiceStatus();\n      const interruptionMode = session.getVoiceInterruptionMode();\n\n      // In \"none\" mode, ignore clicks while processing or speaking\n      if (interruptionMode === \"none\" &&\n          (voiceStatus === \"processing\" || voiceStatus === \"speaking\")) {\n        return;\n      }\n\n      // In \"cancel\" mode during processing/speaking: stop playback only\n      if (interruptionMode === \"cancel\" &&\n          (voiceStatus === \"processing\" || voiceStatus === \"speaking\")) {\n        session.stopVoicePlayback();\n        return;\n      }\n\n      // In barge-in mode, clicking mic = \"hang up\" (any state: speaking, idle, etc.)\n      // Stops playback if active, tears down the always-on mic.\n      if (session.isBargeInActive()) {\n        session.stopVoicePlayback();\n        session.deactivateBargeIn().then(() => {\n          voiceState.active = false;\n          voiceState.manuallyDeactivated = true;\n          persistVoiceMetadata();\n          emitVoiceState(\"user\");\n          removeRuntypeMicStateStyles();\n        });\n        return;\n      }\n\n      session.toggleVoice().then(() => {\n        voiceState.active = session.isVoiceActive();\n        voiceState.manuallyDeactivated = !session.isVoiceActive();\n        persistVoiceMetadata();\n        emitVoiceState(\"user\");\n        if (session.isVoiceActive()) {\n          applyRuntypeMicRecordingStyles();\n        } else {\n          removeRuntypeMicStateStyles();\n        }\n      });\n      return;\n    }\n\n    // Browser provider: use SpeechRecognition\n    if (isRecording) {\n      // Stop recording and submit\n      const finalValue = textarea.value.trim();\n      voiceState.manuallyDeactivated = true;\n      persistVoiceMetadata();\n      stopVoiceRecognition(\"user\");\n      if (finalValue) {\n        textarea.value = \"\";\n        textarea.style.height = \"auto\"; // Reset height after clearing\n        session.sendMessage(finalValue);\n      }\n    } else {\n      // Start recording\n      voiceState.manuallyDeactivated = false;\n      persistVoiceMetadata();\n      startVoiceRecognition(\"user\");\n    }\n  };\n\n  composerVoiceBridge = handleMicButtonClick;\n\n  if (micButton) {\n    micButton.addEventListener(\"click\", handleMicButtonClick);\n\n    destroyCallbacks.push(() => {\n      if (config.voiceRecognition?.provider?.type === 'runtype') {\n        if (session.isVoiceActive()) session.toggleVoice();\n        removeRuntypeMicStateStyles();\n      } else {\n        stopVoiceRecognition(\"system\");\n      }\n      if (micButton) {\n        micButton.removeEventListener(\"click\", handleMicButtonClick);\n      }\n    });\n  }\n\n  const autoResumeUnsub = eventBus.on(\"assistant:complete\", () => {\n    if (!voiceAutoResumeMode) return;\n    if (voiceState.active || voiceState.manuallyDeactivated) return;\n    if (voiceAutoResumeMode === \"assistant\" && !voiceState.lastUserMessageWasVoice) {\n      return;\n    }\n    setTimeout(() => {\n      if (!voiceState.active && !voiceState.manuallyDeactivated) {\n        if (config.voiceRecognition?.provider?.type === 'runtype') {\n          session.toggleVoice().then(() => {\n            voiceState.active = session.isVoiceActive();\n            emitVoiceState(\"auto\");\n            if (session.isVoiceActive()) applyRuntypeMicRecordingStyles();\n          });\n        } else {\n          startVoiceRecognition(\"auto\");\n        }\n      }\n    }, 600);\n  });\n  destroyCallbacks.push(autoResumeUnsub);\n\n  // Handle action:resubmit event - automatically trigger another model call\n  // when an action handler needs the model to continue processing (e.g., analyzing search results)\n  const resubmitUnsub = eventBus.on(\"action:resubmit\", () => {\n    // Short delay to allow UI to update with any injected messages\n    // Handlers should call context.triggerResubmit() AFTER their async work completes\n    setTimeout(() => {\n      if (session && !session.isStreaming()) {\n        // Continue conversation without adding a visible user message\n        session.continueConversation();\n      }\n    }, 100);\n  });\n  destroyCallbacks.push(resubmitUnsub);\n\n  const toggleOpen = () => {\n    setOpenState(!open, \"user\");\n  };\n\n  // Plugin hook: renderLauncher - allow plugins to provide custom launcher\n  let launcherButtonInstance: LauncherButton | null = null;\n  let customLauncherElement: HTMLElement | null = null;\n  \n  // Composer-bar mode is launcher-less by design: the persistent pill IS the\n  // entry point, so skip creating any launcher button (default or plugin).\n  if (launcherEnabled && !isComposerBar()) {\n    const { instance, element } = resolveLauncher({ config, plugins, onToggle: toggleOpen });\n    launcherButtonInstance = instance;\n    // A plugin-provided launcher returns no controller instance; track its\n    // element separately so the update path can manage it.\n    if (!instance) customLauncherElement = element;\n  }\n\n  if (launcherButtonInstance) {\n    mount.appendChild(launcherButtonInstance.element);\n  } else if (customLauncherElement) {\n    mount.appendChild(customLauncherElement);\n  }\n  updateOpenState();\n  renderSuggestions();\n  updateCopy();\n  setComposerDisabled(session.isStreaming());\n  // Reopen-where-left-off takes precedence when opted in (Principle 11);\n  // otherwise fall back to the historical per-mode positioning.\n  if (!restoreScrollPosition()) {\n    if (getScrollMode() === \"follow\") {\n      scheduleAutoScroll(true);\n    } else {\n      jumpToBottomInstant();\n    }\n  }\n  maybeRestoreVoiceFromMetadata();\n\n  if (autoFocusInput) {\n    // Composer-bar's pill exposes the textarea immediately, so focus it on\n    // init like the inline embed does: even though the panel is collapsed.\n    if (!launcherEnabled || isComposerBar()) {\n      setTimeout(() => maybeFocusInput(), 0);\n    } else if (open) {\n      setTimeout(() => maybeFocusInput(), 200);\n    }\n  }\n\n  const recalcPanelHeight = () => {\n    // Composer-bar mode lets CSS own all sizing: collapsed pill is auto-sized\n    // by the footer; expanded fullscreen/modal are driven by CSS attribute\n    // selectors plus inline maxWidth/maxHeight set in updateOpenState. JS\n    // sizing here would fight the morph transitions.\n    if (isComposerBar()) {\n      updateScrollToBottomButtonOffset();\n      updateOpenState();\n      return;\n    }\n\n    const dockedMode = isDockedMountMode(config);\n    const sidebarMode = config.launcher?.sidebarMode ?? false;\n    const fullHeight = dockedMode || sidebarMode || (config.launcher?.fullHeight ?? false);\n\n    // Mobile fullscreen: re-apply fullscreen styles on resize (handles orientation changes)\n    const ownerWindow = mount.ownerDocument.defaultView ?? window;\n    const mobileFullscreen = config.launcher?.mobileFullscreen ?? true;\n    const mobileBreakpoint = config.launcher?.mobileBreakpoint ?? 640;\n    const isMobileViewport = ownerWindow.innerWidth <= mobileBreakpoint;\n    const shouldGoFullscreen = mobileFullscreen && isMobileViewport && launcherEnabled;\n\n    try {\n      if (shouldGoFullscreen) {\n        applyFullHeightStyles();\n        applyThemeVariables(mount, config);\n        return;\n      }\n\n      // Exiting mobile fullscreen (e.g., orientation change to landscape): reset all styles\n      if (wasMobileFullscreen) {\n        wasMobileFullscreen = false;\n        applyFullHeightStyles();\n        applyThemeVariables(mount, config);\n      }\n\n      if (!launcherEnabled && !dockedMode) {\n        panel.style.height = \"\";\n        panel.style.width = \"\";\n        return;\n      }\n\n      // In sidebar/fullHeight mode, don't override the width - it's handled by applyFullHeightStyles\n      if (!sidebarMode && !dockedMode) {\n        const launcherWidth = config?.launcher?.width ?? config?.launcherWidth;\n        const width = launcherWidth ?? DEFAULT_FLOATING_LAUNCHER_WIDTH;\n        panel.style.width = width;\n        panel.style.maxWidth = width;\n      }\n      applyLauncherArtifactPanelWidth();\n\n      // In fullHeight mode, don't set a fixed height\n      if (!fullHeight) {\n        const viewportHeight = ownerWindow.innerHeight;\n        const verticalMargin = 64; // leave space for launcher's offset\n        const heightOffset = config.launcher?.heightOffset ?? 0;\n        const available = Math.max(200, viewportHeight - verticalMargin);\n        const clamped = Math.min(640, available);\n        const finalHeight = Math.max(200, clamped - heightOffset);\n        panel.style.height = `${finalHeight}px`;\n      }\n    } finally {\n      // applyFullHeightStyles() assigns wrapper.style.cssText (e.g. display:flex !important), which\n      // overwrites updateOpenState()'s display:none when docked+closed. Re-sync after every recalc.\n      updateScrollToBottomButtonOffset();\n      updateOpenState();\n\n      // Sync scroll lock and host stacking when viewport mode changes (e.g. orientation change)\n      if (open && launcherEnabled) {\n        const ow = mount.ownerDocument.defaultView ?? window;\n        const isMobile = ow.innerWidth <= (config.launcher?.mobileBreakpoint ?? 640);\n        const sm = config.launcher?.sidebarMode ?? false;\n        const mf = config.launcher?.mobileFullscreen ?? true;\n        const dockedMF = isDockedMountMode(config) && mf && isMobile;\n        const isVC = sm || (mf && isMobile && launcherEnabled) || dockedMF;\n\n        if (isVC && !releaseScrollLock) {\n          const root = mount.getRootNode();\n          const hostEl = root instanceof ShadowRoot\n            ? (root.host as HTMLElement)\n            : mount.closest<HTMLElement>(\".persona-host\");\n          if (hostEl && !teardownHostStacking) {\n            teardownHostStacking = syncOverlayHostStacking(\n              hostEl,\n              config.launcher?.zIndex ?? DEFAULT_OVERLAY_Z_INDEX\n            );\n          }\n          releaseScrollLock = acquireScrollLock(mount.ownerDocument);\n        } else if (!isVC) {\n          teardownHostStacking?.();\n          teardownHostStacking = null;\n          releaseScrollLock?.();\n          releaseScrollLock = null;\n        }\n      }\n    }\n  };\n\n  recalcPanelHeight();\n  const ownerWindow = mount.ownerDocument.defaultView ?? window;\n  ownerWindow.addEventListener(\"resize\", recalcPanelHeight);\n  destroyCallbacks.push(() => ownerWindow.removeEventListener(\"resize\", recalcPanelHeight));\n  if (typeof ResizeObserver !== \"undefined\") {\n    const footerResizeObserver = new ResizeObserver(() => {\n      updateScrollToBottomButtonOffset();\n    });\n    footerResizeObserver.observe(footer);\n    destroyCallbacks.push(() => footerResizeObserver.disconnect());\n  }\n\n  lastScrollTop = body.scrollTop;\n  let lastBottomOffset = getScrollBottomOffset(body);\n\n  const getTranscriptSelection = (): Selection | null => {\n    // Selections inside a shadow root are not always reflected by\n    // document.getSelection(); prefer the shadow root's view when available\n    // (non-standard but supported where it matters).\n    const root = body.getRootNode();\n    const shadowSelection =\n      typeof (root as ShadowRoot & { getSelection?: () => Selection | null })\n        .getSelection === \"function\"\n        ? (root as ShadowRoot & { getSelection: () => Selection | null }).getSelection()\n        : null;\n    return shadowSelection ?? body.ownerDocument.getSelection();\n  };\n  const hasActiveTranscriptSelection = () =>\n    hasSelectionWithin(getTranscriptSelection(), body);\n\n  const handleScroll = () => {\n    const scrollTop = body.scrollTop;\n    // When content mutates (e.g. stream-animation plugins re-rendering text)\n    // or the viewport grows (composer shrinking back), the maximum scroll\n    // position can shrink and force the browser to clamp scrollTop downward.\n    // That emits a scroll event with a negative delta that would otherwise be\n    // misread as the user scrolling up, pausing auto-follow and flashing the\n    // scroll-to-bottom button. Treat those as non-user events. Tracking the\n    // bottom offset (scrollHeight - clientHeight) rather than scrollHeight\n    // alone also covers clientHeight-driven clamps.\n    const currentBottomOffset = getScrollBottomOffset(body);\n    const bottomOffsetShrank = currentBottomOffset < lastBottomOffset;\n    lastBottomOffset = currentBottomOffset;\n\n    if (!isFollowEffective()) {\n      // No follow state to manage (anchored anchor-top / none): just keep the\n      // scroll-to-bottom affordance in sync with the user's position.\n      lastScrollTop = scrollTop;\n      syncScrollToBottomButton();\n      return;\n    }\n\n    const { action, nextLastScrollTop } = resolveFollowStateFromScroll({\n      following: autoFollow.isFollowing(),\n      currentScrollTop: scrollTop,\n      lastScrollTop,\n      nearBottom: isElementNearBottom(body, BOTTOM_THRESHOLD),\n      userScrollThreshold: USER_SCROLL_THRESHOLD,\n      isAutoScrolling: isAutoScrolling || hasPendingAutoScroll || bottomOffsetShrank,\n      pauseOnUpwardScroll: true,\n      pauseWhenAwayFromBottom: false,\n      resumeRequiresDownwardScroll: true\n    });\n    lastScrollTop = nextLastScrollTop;\n\n    if (action === \"resume\") {\n      // Drag-selecting downward near the bottom edge auto-scrolls down and\n      // would otherwise read as a resume gesture; keep follow paused while a\n      // transcript selection is active so it isn't yanked mid-drag.\n      if (!hasActiveTranscriptSelection()) {\n        resumeAutoScroll();\n      }\n      return;\n    }\n\n    if (action === \"pause\") {\n      pauseAutoScroll();\n    }\n  };\n\n  body.addEventListener(\"scroll\", handleScroll, { passive: true });\n  destroyCallbacks.push(() => body.removeEventListener(\"scroll\", handleScroll));\n\n  // Content-growth follow. Render events already schedule auto-scroll, but\n  // content can also grow without one: images/embeds finishing loading\n  // mid-stream, web fonts swapping, the panel or composer resizing. Observe\n  // the messages wrapper (content growth) and the scroll container itself\n  // (viewport resize) so the pin survives all of them.\n  if (typeof ResizeObserver !== \"undefined\") {\n    const contentResizeObserver = new ResizeObserver(() => {\n      handleContentResize();\n    });\n    contentResizeObserver.observe(messagesWrapper);\n    contentResizeObserver.observe(body);\n    destroyCallbacks.push(() => contentResizeObserver.disconnect());\n  }\n\n  // Pause auto-follow while the user selects transcript text so the\n  // streaming scroll doesn't move content out from under the selection.\n  // Driven purely by selectionchange (no pointer gating) so keyboard\n  // selection (Shift+arrows, select-all) pauses too; a stale selection\n  // left in the transcript fires no further events, so it can't re-pause\n  // after the user resumes following.\n  const handleSelectionChange = () => {\n    if (!isFollowEffective()) return;\n    if (!autoFollow.isFollowing()) return;\n    if (hasActiveTranscriptSelection()) {\n      pauseAutoScroll();\n    }\n  };\n  const selectionDocument = body.ownerDocument;\n  selectionDocument.addEventListener(\"selectionchange\", handleSelectionChange);\n  destroyCallbacks.push(() => {\n    selectionDocument.removeEventListener(\"selectionchange\", handleSelectionChange);\n  });\n\n  // Principle 3: every interaction is intent. Beyond wheel/scroll/selection,\n  // opting into `pauseOnInteraction` also treats keyboard navigation within the\n  // transcript and focusing an interactive element (a link, button, etc.) as\n  // \"the reader is doing something here\" — pause auto-follow so the stream\n  // doesn't move content out from under them. Opt-in; off by default.\n  const NAV_KEYS = new Set([\n    \"PageUp\",\n    \"PageDown\",\n    \"Home\",\n    \"End\",\n    \"ArrowUp\",\n    \"ArrowDown\",\n  ]);\n  const handleTranscriptKeydown = (event: KeyboardEvent) => {\n    if (!isPauseOnInteractionEnabled()) return;\n    if (!isFollowEffective()) return;\n    if (!autoFollow.isFollowing()) return;\n    if (NAV_KEYS.has(event.key)) {\n      pauseAutoScroll();\n    }\n  };\n  const handleTranscriptFocusIn = (event: FocusEvent) => {\n    if (!isPauseOnInteractionEnabled()) return;\n    if (!isFollowEffective()) return;\n    if (!autoFollow.isFollowing()) return;\n    const target = event.target as Element | null;\n    if (target && target.closest(\"a, button, [tabindex], input, textarea, select\")) {\n      pauseAutoScroll();\n    }\n  };\n  body.addEventListener(\"keydown\", handleTranscriptKeydown);\n  body.addEventListener(\"focusin\", handleTranscriptFocusIn);\n  destroyCallbacks.push(() => {\n    body.removeEventListener(\"keydown\", handleTranscriptKeydown);\n    body.removeEventListener(\"focusin\", handleTranscriptFocusIn);\n  });\n\n  const handleWheel = (event: WheelEvent) => {\n    if (!isFollowEffective()) return;\n    const action = resolveFollowStateFromWheel({\n      following: autoFollow.isFollowing(),\n      deltaY: event.deltaY,\n      nearBottom: isElementNearBottom(body, BOTTOM_THRESHOLD),\n      resumeWhenNearBottom: true\n    });\n\n    if (action === \"pause\") {\n      pauseAutoScroll();\n    } else if (action === \"resume\" && !hasActiveTranscriptSelection()) {\n      resumeAutoScroll();\n    }\n  };\n  body.addEventListener(\"wheel\", handleWheel, { passive: true });\n  destroyCallbacks.push(() => body.removeEventListener(\"wheel\", handleWheel));\n  scrollToBottomButton.addEventListener(\"click\", () => {\n    // Jumping to the latest abandons the current anchor: drop the spacer\n    // first so \"bottom\" means the real end of content, not spacer padding\n    // that would keep shrinking underneath the reader.\n    resetAnchorState();\n    body.scrollTop = body.scrollHeight;\n    lastScrollTop = body.scrollTop;\n    resumeAutoScroll();\n    scheduleAutoScroll(true);\n    syncScrollToBottomButton();\n  });\n  destroyCallbacks.push(() => scrollToBottomButton.remove());\n  destroyCallbacks.push(() => {\n    cancelAutoScroll();\n    resetAnchorState();\n  });\n\n  const refreshCloseButton = () => {\n    if (!closeButton) return;\n    if (closeHandler) {\n      closeButton.removeEventListener(\"click\", closeHandler);\n      closeHandler = null;\n    }\n    if (isPanelToggleable()) {\n      closeButton.style.display = \"\";\n      closeHandler = () => {\n        setOpenState(false, \"user\");\n      };\n      closeButton.addEventListener(\"click\", closeHandler);\n    } else {\n      closeButton.style.display = \"none\";\n    }\n  };\n\n  refreshCloseButton();\n\n  // Setup clear chat button click handler\n  const setupClearChatButton = () => {\n    const { clearChatButton } = panelElements;\n    if (!clearChatButton) return;\n\n    clearChatButton.addEventListener(\"click\", () => {\n      // Clear messages in session (this will trigger onMessagesChanged which re-renders)\n      session.clearMessages();\n      messageCache.clear();\n      resumeAutoScroll();\n\n      // Drop any open ask_user_question sheets: their source messages are gone.\n      removeAskUserQuestionSheet(panelElements.composerOverlay);\n\n      // Always clear the default localStorage key\n      try {\n        localStorage.removeItem(DEFAULT_CHAT_HISTORY_STORAGE_KEY);\n        if (config.debug) {\n          console.log(`[AgentWidget] Cleared default localStorage key: ${DEFAULT_CHAT_HISTORY_STORAGE_KEY}`);\n        }\n      } catch (error) {\n        console.error(\"[AgentWidget] Failed to clear default localStorage:\", error);\n      }\n\n      // Also clear custom localStorage key if configured\n      if (config.clearChatHistoryStorageKey && config.clearChatHistoryStorageKey !== DEFAULT_CHAT_HISTORY_STORAGE_KEY) {\n        try {\n          localStorage.removeItem(config.clearChatHistoryStorageKey);\n          if (config.debug) {\n            console.log(`[AgentWidget] Cleared custom localStorage key: ${config.clearChatHistoryStorageKey}`);\n          }\n        } catch (error) {\n          console.error(\"[AgentWidget] Failed to clear custom localStorage:\", error);\n        }\n      }\n\n      // Dispatch custom event for external handlers (e.g., localStorage clearing in examples)\n      const clearEvent = new CustomEvent(\"persona:clear-chat\", {\n        detail: { timestamp: new Date().toISOString() }\n      });\n      window.dispatchEvent(clearEvent);\n\n      if (storageAdapter?.clear) {\n        try {\n          const result = storageAdapter.clear();\n          if (result instanceof Promise) {\n            result.catch((error) => {\n              if (typeof console !== \"undefined\") {\n                // eslint-disable-next-line no-console\n                console.error(\"[AgentWidget] Failed to clear storage adapter:\", error);\n              }\n            });\n          }\n        } catch (error) {\n          if (typeof console !== \"undefined\") {\n            // eslint-disable-next-line no-console\n            console.error(\"[AgentWidget] Failed to clear storage adapter:\", error);\n          }\n        }\n      }\n      persistentMetadata = {};\n      actionManager.syncFromMetadata();\n\n      // Clear event stream buffer and store, and reset throughput tracking\n      eventStreamBuffer?.clear();\n      throughputTracker?.reset();\n      eventStreamView?.update();\n    });\n  };\n\n  setupClearChatButton();\n\n  if (composerForm) {\n    composerForm.addEventListener(\"submit\", handleSubmit);\n  }\n  textarea?.addEventListener(\"keydown\", handleComposerKeydown);\n  textarea?.addEventListener(\"input\", handleComposerInput);\n  textarea?.addEventListener(\"paste\", handleInputPaste);\n\n  const escStopDoc = mount.ownerDocument ?? document;\n  escStopDoc.addEventListener(\"keydown\", handleEscStop, true);\n\n  const ATTACHMENT_DROP_ACTIVE_CLASS = \"persona-attachment-drop-active\";\n  let attachmentFileDragDepth = 0;\n\n  const clearAttachmentDropVisual = () => {\n    attachmentFileDragDepth = 0;\n    container.classList.remove(ATTACHMENT_DROP_ACTIVE_CLASS);\n  };\n\n  const attachmentDropHandlingActive = (): boolean =>\n    config.attachments?.enabled === true && attachmentManager !== null;\n\n  // Visual highlight tracked on `container` (the chat column).\n  const handleAttachmentDragEnterCapture = (e: DragEvent) => {\n    if (!dataTransferHasFiles(e.dataTransfer) || !attachmentDropHandlingActive()) return;\n    attachmentFileDragDepth++;\n    if (attachmentFileDragDepth === 1) {\n      container.classList.add(ATTACHMENT_DROP_ACTIVE_CLASS);\n    }\n  };\n\n  const handleAttachmentDragLeaveCapture = (e: DragEvent) => {\n    if (!dataTransferHasFiles(e.dataTransfer) || !attachmentDropHandlingActive()) return;\n    attachmentFileDragDepth--;\n    if (attachmentFileDragDepth <= 0) {\n      clearAttachmentDropVisual();\n    }\n  };\n\n  // dragover + drop registered on `mount` so the browser default (open file)\n  // is suppressed across the entire widget surface (artifact pane, gaps, etc.).\n  const handleAttachmentDragOverCapture = (e: DragEvent) => {\n    if (!dataTransferHasFiles(e.dataTransfer) || !attachmentDropHandlingActive()) return;\n    e.preventDefault();\n    e.dataTransfer.dropEffect = \"copy\";\n  };\n\n  const handleAttachmentDropCapture = (e: DragEvent) => {\n    if (!dataTransferHasFiles(e.dataTransfer) || !attachmentDropHandlingActive()) return;\n    e.preventDefault();\n    e.stopPropagation();\n    clearAttachmentDropVisual();\n    const files = Array.from(e.dataTransfer.files ?? []);\n    if (files.length === 0) return;\n    void attachmentManager!.handleFiles(files);\n  };\n\n  const attachmentDropCapture = true;\n  container.addEventListener(\"dragenter\", handleAttachmentDragEnterCapture, attachmentDropCapture);\n  container.addEventListener(\"dragleave\", handleAttachmentDragLeaveCapture, attachmentDropCapture);\n  mount.addEventListener(\"dragover\", handleAttachmentDragOverCapture, attachmentDropCapture);\n  mount.addEventListener(\"drop\", handleAttachmentDropCapture, attachmentDropCapture);\n\n  // Prevent the browser from navigating to/opening a dropped file anywhere on\n  // the page while this widget instance has attachments enabled.  These guards\n  // intentionally skip the `dataTransferHasFiles` check because real OS drags\n  // may expose `dataTransfer.types` as a DOMStringList or restrict access\n  // during certain drag phases.  The cost is minimal: we suppress the native\n  // \"open file\" default for ALL drag-overs while the widget is alive and\n  // attachments are on: text drags into the textarea still work because\n  // element-level handlers are unaffected (we don't stopPropagation here).\n  const ownerDoc = mount.ownerDocument;\n  const handleDocDragOver = (e: DragEvent) => {\n    if (!attachmentDropHandlingActive()) return;\n    e.preventDefault();\n  };\n  const handleDocDrop = (e: DragEvent) => {\n    if (!attachmentDropHandlingActive()) return;\n    e.preventDefault();\n  };\n  ownerDoc.addEventListener(\"dragover\", handleDocDragOver);\n  ownerDoc.addEventListener(\"drop\", handleDocDrop);\n\n  destroyCallbacks.push(() => {\n    if (composerForm) {\n      composerForm.removeEventListener(\"submit\", handleSubmit);\n    }\n    textarea?.removeEventListener(\"keydown\", handleComposerKeydown);\n    textarea?.removeEventListener(\"input\", handleComposerInput);\n    textarea?.removeEventListener(\"paste\", handleInputPaste);\n    escStopDoc.removeEventListener(\"keydown\", handleEscStop, true);\n  });\n\n  destroyCallbacks.push(() => {\n    container.removeEventListener(\"dragenter\", handleAttachmentDragEnterCapture, attachmentDropCapture);\n    container.removeEventListener(\"dragleave\", handleAttachmentDragLeaveCapture, attachmentDropCapture);\n    mount.removeEventListener(\"dragover\", handleAttachmentDragOverCapture, attachmentDropCapture);\n    mount.removeEventListener(\"drop\", handleAttachmentDropCapture, attachmentDropCapture);\n    ownerDoc.removeEventListener(\"dragover\", handleDocDragOver);\n    ownerDoc.removeEventListener(\"drop\", handleDocDrop);\n    clearAttachmentDropVisual();\n  });\n\n  destroyCallbacks.push(() => {\n    session.cancel();\n  });\n\n  if (launcherButtonInstance) {\n    destroyCallbacks.push(() => {\n      launcherButtonInstance?.destroy();\n    });\n  } else if (customLauncherElement) {\n    destroyCallbacks.push(() => {\n      customLauncherElement?.remove();\n    });\n  }\n\n  const controller: Controller = {\n    update(nextConfig: AgentWidgetConfig) {\n      const previousToolCallConfig = config.toolCall;\n      const previousMessageActions = config.messageActions;\n      const previousLayoutMessages = config.layout?.messages;\n      const previousColorScheme = config.colorScheme;\n      const previousLoadingIndicator = config.loadingIndicator;\n      const previousIterationDisplay = config.iterationDisplay;\n      const previousShowReasoning = config.features?.showReasoning;\n      const previousShowToolCalls = config.features?.showToolCalls;\n      const previousToolCallDisplay = config.features?.toolCallDisplay;\n      const previousReasoningDisplay = config.features?.reasoningDisplay;\n      config = { ...config, ...nextConfig };\n      // applyFullHeightStyles resets mount.style.cssText, so call it before applyThemeVariables\n      applyFullHeightStyles();\n      applyThemeVariables(mount, config);\n      applyArtifactLayoutCssVars(mount, config);\n      applyArtifactPaneAppearance(mount, config);\n      syncArtifactPane();\n\n      // Re-setup theme observer if colorScheme changed\n      if (config.colorScheme !== previousColorScheme) {\n        setupThemeObserver();\n      }\n\n      // Update plugins\n      const newPlugins = pluginRegistry.getForInstance(config.plugins);\n      plugins.length = 0;\n      plugins.push(...newPlugins);\n\n      launcherEnabled = config.launcher?.enabled ?? true;\n      autoExpand = config.launcher?.autoExpand ?? false;\n      showReasoning = config.features?.showReasoning ?? true;\n      showToolCalls = config.features?.showToolCalls ?? true;\n      scrollToBottomFeature = config.features?.scrollToBottom ?? {};\n      const prevScrollMode = getScrollMode();\n      scrollBehaviorFeature = config.features?.scrollBehavior ?? {};\n      if (prevScrollMode !== getScrollMode()) {\n        // Leaving anchor-top drops any live spacer; entering a new mode\n        // starts from a clean follow state.\n        resetAnchorState();\n        resumeAutoScroll();\n      }\n      renderScrollToBottomButton();\n      syncScrollToBottomButton();\n      const prevShowEventStreamToggle = showEventStreamToggle;\n      showEventStreamToggle = config.features?.showEventStreamToggle ?? false;\n\n      // Handle dynamic event stream feature flag toggling\n      if (showEventStreamToggle && !prevShowEventStreamToggle) {\n        // Flag changed from false to true - create buffer/store if needed\n        if (!eventStreamBuffer) {\n          eventStreamStore = new EventStreamStore(eventStreamDbName);\n          eventStreamBuffer = new EventStreamBuffer(eventStreamMaxEvents, eventStreamStore);\n          throughputTracker = throughputTracker ?? new ThroughputTracker();\n          eventStreamStore.open().then(() => eventStreamBuffer?.restore()).catch(() => {});\n          // Register the SSE event callback (host tap + buffer + throughput)\n          session.setSSEEventCallback((type: string, payload: unknown) => {\n            config.onSSEEvent?.(type, payload);\n            throughputTracker?.processEvent(type, payload);\n            eventStreamBuffer!.push({\n              id: `evt-${Date.now()}-${Math.random().toString(36).slice(2, 8)}`,\n              type,\n              timestamp: Date.now(),\n              payload: JSON.stringify(payload)\n            });\n          });\n        }\n        // Add header toggle button if not present\n        if (!eventStreamToggleBtn && header) {\n          const dynEsClassNames = config.features?.eventStream?.classNames;\n          const dynToggleBtnClasses = \"persona-inline-flex persona-items-center persona-justify-center persona-rounded-full hover:persona-opacity-80 persona-cursor-pointer persona-border-none persona-bg-transparent persona-p-1\" + (dynEsClassNames?.toggleButton ? \" \" + dynEsClassNames.toggleButton : \"\");\n          eventStreamToggleBtn = createElement(\"button\", dynToggleBtnClasses) as HTMLButtonElement;\n          eventStreamToggleBtn.style.width = \"28px\";\n          eventStreamToggleBtn.style.height = \"28px\";\n          eventStreamToggleBtn.style.color = HEADER_THEME_CSS.actionIconColor;\n          eventStreamToggleBtn.type = \"button\";\n          eventStreamToggleBtn.setAttribute(\"aria-label\", \"Event Stream\");\n          eventStreamToggleBtn.title = \"Event Stream\";\n          const activityIcon = renderLucideIcon(\"activity\", \"18px\", \"currentColor\", 1.5);\n          if (activityIcon) eventStreamToggleBtn.appendChild(activityIcon);\n          const clearChatWrapper = panelElements.clearChatButtonWrapper;\n          const closeWrapper = panelElements.closeButtonWrapper;\n          const insertBefore = clearChatWrapper || closeWrapper;\n          if (insertBefore && insertBefore.parentNode === header) {\n            header.insertBefore(eventStreamToggleBtn, insertBefore);\n          } else {\n            header.appendChild(eventStreamToggleBtn);\n          }\n          eventStreamToggleBtn.addEventListener(\"click\", () => {\n            if (eventStreamVisible) {\n              toggleEventStreamOff();\n            } else {\n              toggleEventStreamOn();\n            }\n          });\n        }\n      } else if (!showEventStreamToggle && prevShowEventStreamToggle) {\n        // Flag changed from true to false - hide and clean up\n        toggleEventStreamOff();\n        if (eventStreamToggleBtn) {\n          eventStreamToggleBtn.remove();\n          eventStreamToggleBtn = null;\n        }\n        eventStreamBuffer?.clear();\n        eventStreamStore?.destroy();\n        eventStreamBuffer = null;\n        eventStreamStore = null;\n        throughputTracker?.reset();\n        throughputTracker = null;\n      }\n\n      if (config.launcher?.enabled === false && launcherButtonInstance) {\n        launcherButtonInstance.destroy();\n        launcherButtonInstance = null;\n      }\n      if (config.launcher?.enabled === false && customLauncherElement) {\n        customLauncherElement.remove();\n        customLauncherElement = null;\n      }\n\n      if (config.launcher?.enabled !== false && !launcherButtonInstance && !customLauncherElement) {\n        // Resolve the launcher again when re-enabling (honors renderLauncher plugin).\n        const { instance, element } = resolveLauncher({ config, plugins, onToggle: toggleOpen });\n        launcherButtonInstance = instance;\n        if (!instance) customLauncherElement = element;\n        mount.appendChild(element);\n      }\n\n      if (launcherButtonInstance) {\n        launcherButtonInstance.update(config);\n      }\n      // Note: Custom launcher updates are handled by the plugin's own logic\n\n      // Update panel header title and subtitle\n      if (headerTitle && config.launcher?.title !== undefined) {\n        headerTitle.textContent = config.launcher.title;\n      }\n      if (headerSubtitle && config.launcher?.subtitle !== undefined) {\n        headerSubtitle.textContent = config.launcher.subtitle;\n      }\n\n      // Update header layout if it changed\n      const headerLayoutConfig = config.layout?.header;\n      const headerLayoutChanged = headerLayoutConfig?.layout !== prevHeaderLayout;\n\n      if (headerLayoutChanged && header) {\n        // Rebuild header with new layout\n        const newHeaderElements = headerLayoutConfig\n          ? buildHeaderWithLayout(config, headerLayoutConfig, {\n              showClose: isPanelToggleable(),\n              onClose: () => setOpenState(false, \"user\")\n            })\n          : buildHeader({\n              config,\n              showClose: isPanelToggleable(),\n              onClose: () => setOpenState(false, \"user\")\n            });\n\n        // Replace the old header with the new one (keeps view.header in sync).\n        view.replaceHeader(newHeaderElements);\n\n        // Mirror the view's refreshed header refs into the local bindings.\n        header = view.header.element;\n        iconHolder = view.header.iconHolder;\n        headerTitle = view.header.headerTitle;\n        headerSubtitle = view.header.headerSubtitle;\n        closeButton = view.header.closeButton;\n\n        prevHeaderLayout = headerLayoutConfig?.layout;\n      } else if (headerLayoutConfig) {\n        // Apply visibility settings without rebuilding\n        if (iconHolder) {\n          iconHolder.style.display = headerLayoutConfig.showIcon === false ? \"none\" : \"\";\n        }\n        if (headerTitle) {\n          headerTitle.style.display = headerLayoutConfig.showTitle === false ? \"none\" : \"\";\n        }\n        if (headerSubtitle) {\n          headerSubtitle.style.display = headerLayoutConfig.showSubtitle === false ? \"none\" : \"\";\n        }\n        if (closeButton) {\n          closeButton.style.display = headerLayoutConfig.showCloseButton === false ? \"none\" : \"\";\n        }\n        if (panelElements.clearChatButtonWrapper) {\n          // showClearChat explicitly controls visibility when set\n          const showClearChat = headerLayoutConfig.showClearChat;\n          if (showClearChat !== undefined) {\n            panelElements.clearChatButtonWrapper.style.display = showClearChat ? \"\" : \"none\";\n            // When clear chat is hidden, close button needs ml-auto to stay right-aligned\n            const { closeButtonWrapper } = panelElements;\n            if (closeButtonWrapper && !closeButtonWrapper.classList.contains(\"persona-absolute\")) {\n              if (showClearChat) {\n                closeButtonWrapper.classList.remove(\"persona-ml-auto\");\n              } else {\n                closeButtonWrapper.classList.add(\"persona-ml-auto\");\n              }\n            }\n          }\n        }\n      }\n\n      // Update header visibility based on layout.showHeader\n      const showHeader = config.layout?.showHeader !== false; // default to true\n      if (header) {\n        header.style.display = showHeader ? \"\" : \"none\";\n      }\n\n      // Update footer visibility based on layout.showFooter\n      const showFooter = config.layout?.showFooter !== false; // default to true\n      if (footer) {\n        footer.style.display = showFooter ? \"\" : \"none\";\n      }\n      updateScrollToBottomButtonOffset();\n      syncScrollToBottomButton();\n\n      // Only update open state if launcher enabled state changed or autoExpand value changed\n      const launcherEnabledChanged = launcherEnabled !== prevLauncherEnabled;\n      const autoExpandChanged = autoExpand !== prevAutoExpand;\n\n      if (launcherEnabledChanged) {\n        // Launcher was enabled/disabled - update state accordingly\n        if (!launcherEnabled) {\n          // When launcher is disabled, always keep panel open\n          open = true;\n          updateOpenState();\n        } else {\n          // Launcher was just enabled - respect autoExpand setting\n          setOpenState(autoExpand, \"auto\");\n        }\n      } else if (autoExpandChanged) {\n        // autoExpand value changed - update state to match\n        setOpenState(autoExpand, \"auto\");\n      }\n      // Otherwise, preserve current open state (user may have manually opened/closed)\n\n      // Update previous values for next comparison\n      prevAutoExpand = autoExpand;\n      prevLauncherEnabled = launcherEnabled;\n      recalcPanelHeight();\n      refreshCloseButton();\n\n      // Re-render messages if config affecting message rendering changed\n      const toolCallConfigChanged = JSON.stringify(nextConfig.toolCall) !== JSON.stringify(previousToolCallConfig);\n      const messageActionsChanged = JSON.stringify(config.messageActions) !== JSON.stringify(previousMessageActions);\n      const layoutMessagesChanged = JSON.stringify(config.layout?.messages) !== JSON.stringify(previousLayoutMessages);\n      const loadingIndicatorChanged = config.loadingIndicator?.render !== previousLoadingIndicator?.render\n        || config.loadingIndicator?.renderIdle !== previousLoadingIndicator?.renderIdle\n        || config.loadingIndicator?.showBubble !== previousLoadingIndicator?.showBubble;\n      const iterationDisplayChanged = config.iterationDisplay !== previousIterationDisplay;\n      const featuresChanged = (config.features?.showReasoning ?? true) !== (previousShowReasoning ?? true)\n        || (config.features?.showToolCalls ?? true) !== (previousShowToolCalls ?? true)\n        || JSON.stringify(config.features?.toolCallDisplay) !== JSON.stringify(previousToolCallDisplay)\n        || JSON.stringify(config.features?.reasoningDisplay) !== JSON.stringify(previousReasoningDisplay);\n      const messagesConfigChanged = toolCallConfigChanged || messageActionsChanged || layoutMessagesChanged\n        || loadingIndicatorChanged || iterationDisplayChanged || featuresChanged;\n      if (messagesConfigChanged && session) {\n        configVersion++;\n        renderMessagesWithPlugins(messagesWrapper, session.getMessages(), postprocess);\n      }\n\n      // Update panel icon sizes\n      const launcher = config.launcher ?? {};\n      const headerIconHidden = launcher.headerIconHidden ?? false;\n      const layoutShowIcon = config.layout?.header?.showIcon;\n      // Hide icon if either headerIconHidden is true OR layout.header.showIcon is false\n      const shouldHideIcon = headerIconHidden || layoutShowIcon === false;\n      const headerIconName = launcher.headerIconName;\n      const headerIconSize = launcher.headerIconSize ?? \"48px\";\n\n      if (iconHolder) {\n        const headerEl = container.querySelector(\".persona-border-b-persona-divider\");\n        const headerCopy = headerEl?.querySelector(\".persona-flex-col\");\n\n        // Handle hide/show\n        if (shouldHideIcon) {\n          // Hide iconHolder\n          iconHolder.style.display = \"none\";\n          // Ensure headerCopy is still in header\n          if (headerEl && headerCopy && !headerEl.contains(headerCopy)) {\n            headerEl.insertBefore(headerCopy, headerEl.firstChild);\n          }\n        } else {\n          // Show iconHolder\n          iconHolder.style.display = \"\";\n          iconHolder.style.height = headerIconSize;\n          iconHolder.style.width = headerIconSize;\n          \n          // Ensure iconHolder is before headerCopy in header\n          if (headerEl && headerCopy) {\n            if (!headerEl.contains(iconHolder)) {\n              headerEl.insertBefore(iconHolder, headerCopy);\n            } else if (iconHolder.nextSibling !== headerCopy) {\n              // Reorder if needed\n              iconHolder.remove();\n              headerEl.insertBefore(iconHolder, headerCopy);\n            }\n          }\n          \n          // Update icon content based on priority: Lucide icon > iconUrl > agentIconText\n          if (headerIconName) {\n            // Use Lucide icon. Stroke `currentColor` (not a hardcoded white) so the\n            // glyph inherits iconHolder's `color: var(--persona-header-icon-fg, …)`,\n            // matching the initial render in header-builder.ts. Without this, any\n            // controller.update() (e.g. a theme-editor change) re-rendered the icon\n            // as white and the configured header icon color \"wouldn't stick\".\n            const iconSize = parseFloat(headerIconSize) || 24;\n            const iconSvg = renderLucideIcon(headerIconName, iconSize * 0.6, \"currentColor\", 1);\n            if (iconSvg) {\n              iconHolder.replaceChildren(iconSvg);\n            } else {\n              // Fallback to agentIconText if Lucide icon fails\n              iconHolder.textContent = launcher.agentIconText ?? \"💬\";\n            }\n          } else if (launcher.iconUrl) {\n            // Use image URL\n            const img = iconHolder.querySelector(\"img\");\n            if (img) {\n              img.src = launcher.iconUrl;\n              img.style.height = headerIconSize;\n              img.style.width = headerIconSize;\n            } else {\n              // Create new img if it doesn't exist\n              const newImg = document.createElement(\"img\");\n              newImg.src = launcher.iconUrl;\n              newImg.alt = \"\";\n              newImg.className = \"persona-rounded-xl persona-object-cover\";\n              newImg.style.height = headerIconSize;\n              newImg.style.width = headerIconSize;\n              iconHolder.replaceChildren(newImg);\n            }\n          } else {\n            // Use text/emoji - clear any SVG or img first\n            const existingSvg = iconHolder.querySelector(\"svg\");\n            const existingImg = iconHolder.querySelector(\"img\");\n            if (existingSvg || existingImg) {\n              iconHolder.replaceChildren();\n            }\n            iconHolder.textContent = launcher.agentIconText ?? \"💬\";\n          }\n          \n          // Update image size if present\n          const img = iconHolder.querySelector(\"img\");\n          if (img) {\n            img.style.height = headerIconSize;\n            img.style.width = headerIconSize;\n          }\n        }\n      }\n\n      // Handle title/subtitle visibility from layout config\n      const layoutShowTitle = config.layout?.header?.showTitle;\n      const layoutShowSubtitle = config.layout?.header?.showSubtitle;\n      if (headerTitle) {\n        headerTitle.style.display = layoutShowTitle === false ? \"none\" : \"\";\n      }\n      if (headerSubtitle) {\n        headerSubtitle.style.display = layoutShowSubtitle === false ? \"none\" : \"\";\n      }\n\n      if (closeButton) {\n        // Handle close button visibility from layout config\n        const layoutShowCloseButton = config.layout?.header?.showCloseButton;\n        if (layoutShowCloseButton === false) {\n          closeButton.style.display = \"none\";\n        } else {\n          closeButton.style.display = \"\";\n        }\n\n        const closeButtonSize = launcher.closeButtonSize ?? \"32px\";\n        const closeButtonPlacement = launcher.closeButtonPlacement ?? \"inline\";\n        closeButton.style.height = closeButtonSize;\n        closeButton.style.width = closeButtonSize;\n        \n        // Update placement if changed - move the wrapper (not just the button) to preserve tooltip\n        const { closeButtonWrapper } = panelElements;\n        const isTopRight = closeButtonPlacement === \"top-right\";\n        const currentlyTopRight = closeButtonWrapper?.classList.contains(\"persona-absolute\");\n        \n        if (closeButtonWrapper && isTopRight !== currentlyTopRight) {\n          // Placement changed - need to move wrapper and update classes\n          closeButtonWrapper.remove();\n          \n          // Update wrapper classes\n          if (isTopRight) {\n            closeButtonWrapper.className = \"persona-absolute persona-top-4 persona-right-4 persona-z-50\";\n            container.style.position = \"relative\";\n            container.appendChild(closeButtonWrapper);\n          } else {\n            // Check if clear chat is inline to determine if we need ml-auto\n            const clearChatPlacement = launcher.clearChat?.placement ?? \"inline\";\n            const clearChatEnabled = launcher.clearChat?.enabled ?? true;\n            closeButtonWrapper.className = (clearChatEnabled && clearChatPlacement === \"inline\") ? \"\" : \"persona-ml-auto\";\n            // Find header element\n            const header = container.querySelector(\".persona-border-b-persona-divider\");\n            if (header) {\n              header.appendChild(closeButtonWrapper);\n            }\n          }\n        }\n        \n        // Close icon: launcher color wins; else theme.components.header.actionIconForeground\n        closeButton.style.color =\n          launcher.closeButtonColor || HEADER_THEME_CSS.actionIconColor;\n        \n        if (launcher.closeButtonBackgroundColor) {\n          closeButton.style.backgroundColor = launcher.closeButtonBackgroundColor;\n          closeButton.classList.remove(\"hover:persona-bg-gray-100\");\n        } else {\n          closeButton.style.backgroundColor = \"\";\n          closeButton.classList.add(\"hover:persona-bg-gray-100\");\n        }\n        \n        // Apply border if width and/or color are provided\n        if (launcher.closeButtonBorderWidth || launcher.closeButtonBorderColor) {\n          const borderWidth = launcher.closeButtonBorderWidth || \"0px\";\n          const borderColor = launcher.closeButtonBorderColor || \"transparent\";\n          closeButton.style.border = `${borderWidth} solid ${borderColor}`;\n          closeButton.classList.remove(\"persona-border-none\");\n        } else {\n          closeButton.style.border = \"\";\n          closeButton.classList.add(\"persona-border-none\");\n        }\n        \n        if (launcher.closeButtonBorderRadius) {\n          closeButton.style.borderRadius = launcher.closeButtonBorderRadius;\n          closeButton.classList.remove(\"persona-rounded-full\");\n        } else {\n          closeButton.style.borderRadius = \"\";\n          closeButton.classList.add(\"persona-rounded-full\");\n        }\n\n        // Update padding\n        if (launcher.closeButtonPaddingX) {\n          closeButton.style.paddingLeft = launcher.closeButtonPaddingX;\n          closeButton.style.paddingRight = launcher.closeButtonPaddingX;\n        } else {\n          closeButton.style.paddingLeft = \"\";\n          closeButton.style.paddingRight = \"\";\n        }\n        if (launcher.closeButtonPaddingY) {\n          closeButton.style.paddingTop = launcher.closeButtonPaddingY;\n          closeButton.style.paddingBottom = launcher.closeButtonPaddingY;\n        } else {\n          closeButton.style.paddingTop = \"\";\n          closeButton.style.paddingBottom = \"\";\n        }\n\n        // Update icon\n        const closeButtonIconName = launcher.closeButtonIconName ?? \"x\";\n        const closeButtonIconText = launcher.closeButtonIconText ?? \"×\";\n\n        // Clear existing content and render new icon.\n        // Larger intrinsic size compensates for the X glyph's sparse\n        // viewBox so the close button visually matches sibling icons.\n        closeButton.innerHTML = \"\";\n        const iconSvg = renderLucideIcon(closeButtonIconName, \"28px\", \"currentColor\", 1);\n        if (iconSvg) {\n          closeButton.appendChild(iconSvg);\n        } else {\n          closeButton.textContent = closeButtonIconText;\n        }\n\n        // Update tooltip\n        const closeButtonTooltipText = launcher.closeButtonTooltipText ?? \"Close chat\";\n        const closeButtonShowTooltip = launcher.closeButtonShowTooltip ?? true;\n\n        closeButton.setAttribute(\"aria-label\", closeButtonTooltipText);\n\n        if (closeButtonWrapper) {\n          // Clean up old tooltip event listeners if they exist\n          if ((closeButtonWrapper as any)._cleanupTooltip) {\n            (closeButtonWrapper as any)._cleanupTooltip();\n            delete (closeButtonWrapper as any)._cleanupTooltip;\n          }\n\n          // Set up new portaled tooltip with event listeners\n          if (closeButtonShowTooltip && closeButtonTooltipText) {\n            let portaledTooltip: HTMLElement | null = null;\n\n            const showTooltip = () => {\n              if (portaledTooltip || !closeButton) return; // Already showing or button doesn't exist\n\n              const tooltipDocument = closeButton.ownerDocument;\n              const tooltipContainer = tooltipDocument.body;\n              if (!tooltipContainer) return;\n\n              // Create tooltip element\n              portaledTooltip = createElementInDocument(\n                tooltipDocument,\n                \"div\",\n                \"persona-clear-chat-tooltip\"\n              );\n              portaledTooltip.textContent = closeButtonTooltipText;\n\n              // Add arrow\n              const arrow = createElementInDocument(tooltipDocument, \"div\");\n              arrow.className = \"persona-clear-chat-tooltip-arrow\";\n              portaledTooltip.appendChild(arrow);\n\n              // Get button position\n              const buttonRect = closeButton.getBoundingClientRect();\n\n              // Position tooltip above button\n              portaledTooltip.style.position = \"fixed\";\n              portaledTooltip.style.zIndex = String(PORTALED_OVERLAY_Z_INDEX);\n              portaledTooltip.style.left = `${buttonRect.left + buttonRect.width / 2}px`;\n              portaledTooltip.style.top = `${buttonRect.top - 8}px`;\n              portaledTooltip.style.transform = \"translate(-50%, -100%)\";\n\n              // Append to body\n              tooltipContainer.appendChild(portaledTooltip);\n            };\n\n            const hideTooltip = () => {\n              if (portaledTooltip && portaledTooltip.parentNode) {\n                portaledTooltip.parentNode.removeChild(portaledTooltip);\n                portaledTooltip = null;\n              }\n            };\n\n            // Add event listeners\n            closeButtonWrapper.addEventListener(\"mouseenter\", showTooltip);\n            closeButtonWrapper.addEventListener(\"mouseleave\", hideTooltip);\n            closeButton.addEventListener(\"focus\", showTooltip);\n            closeButton.addEventListener(\"blur\", hideTooltip);\n\n            // Store cleanup function on the wrapper for later use\n            (closeButtonWrapper as any)._cleanupTooltip = () => {\n              hideTooltip();\n              if (closeButtonWrapper) {\n                closeButtonWrapper.removeEventListener(\"mouseenter\", showTooltip);\n                closeButtonWrapper.removeEventListener(\"mouseleave\", hideTooltip);\n              }\n              if (closeButton) {\n                closeButton.removeEventListener(\"focus\", showTooltip);\n                closeButton.removeEventListener(\"blur\", hideTooltip);\n              }\n            };\n          }\n        }\n      }\n\n      // Update clear chat button styling from config\n      const { clearChatButton, clearChatButtonWrapper } = panelElements;\n      if (clearChatButton) {\n        const clearChatConfig = launcher.clearChat ?? {};\n        const clearChatEnabled = clearChatConfig.enabled ?? true;\n        const layoutShowClearChat = config.layout?.header?.showClearChat;\n        // layout.header.showClearChat takes precedence if explicitly set\n        // Otherwise fall back to launcher.clearChat.enabled\n        const shouldShowClearChat = layoutShowClearChat !== undefined\n          ? layoutShowClearChat\n          : clearChatEnabled;\n        const clearChatPlacement = clearChatConfig.placement ?? \"inline\";\n\n        // Show/hide button based on layout config (primary) or launcher config (fallback)\n        if (clearChatButtonWrapper) {\n          clearChatButtonWrapper.style.display = shouldShowClearChat ? \"\" : \"none\";\n\n          // When clear chat is hidden, close button needs ml-auto to stay right-aligned.\n          // Composer-bar mode positions the close button absolutely, so the\n          // ml-auto layout shim doesn't apply and is skipped below.\n          const { closeButtonWrapper } = panelElements;\n          if (\n            !isComposerBar() &&\n            closeButtonWrapper &&\n            !closeButtonWrapper.classList.contains(\"persona-absolute\")\n          ) {\n            if (shouldShowClearChat) {\n              closeButtonWrapper.classList.remove(\"persona-ml-auto\");\n            } else {\n              closeButtonWrapper.classList.add(\"persona-ml-auto\");\n            }\n          }\n\n          // Update placement if changed. Composer-bar mode owns the clear\n          // button's position via panel.ts (absolute, top-right next to ×)\n          // and must not get reshuffled into the floating launcher's\n          // header strip.\n          const isTopRight = clearChatPlacement === \"top-right\";\n          const currentlyTopRight = clearChatButtonWrapper.classList.contains(\"persona-absolute\");\n\n          if (!isComposerBar() && isTopRight !== currentlyTopRight && shouldShowClearChat) {\n            clearChatButtonWrapper.remove();\n\n            if (isTopRight) {\n              // Don't use persona-clear-chat-button-wrapper class for top-right mode as its\n              // display: inline-flex causes alignment issues with the close button\n              clearChatButtonWrapper.className = \"persona-absolute persona-top-4 persona-z-50\";\n              // Position to the left of the close button (which is at right: 1rem/16px)\n              // Close button is ~32px wide, plus small gap = 48px from right\n              clearChatButtonWrapper.style.right = \"48px\";\n              container.style.position = \"relative\";\n              container.appendChild(clearChatButtonWrapper);\n            } else {\n              clearChatButtonWrapper.className = \"persona-relative persona-ml-auto persona-clear-chat-button-wrapper\";\n              // Clear the inline right style when switching back to inline mode\n              clearChatButtonWrapper.style.right = \"\";\n              // Find header and insert before close button\n              const header = container.querySelector(\".persona-border-b-persona-divider\");\n              const closeButtonWrapperEl = panelElements.closeButtonWrapper;\n              if (header && closeButtonWrapperEl && closeButtonWrapperEl.parentElement === header) {\n                header.insertBefore(clearChatButtonWrapper, closeButtonWrapperEl);\n              } else if (header) {\n                header.appendChild(clearChatButtonWrapper);\n              }\n            }\n\n            // Also update close button's ml-auto class based on clear chat position\n            const closeButtonWrapperEl = panelElements.closeButtonWrapper;\n            if (closeButtonWrapperEl && !closeButtonWrapperEl.classList.contains(\"persona-absolute\")) {\n              if (isTopRight) {\n                // Clear chat moved to top-right, close needs ml-auto\n                closeButtonWrapperEl.classList.add(\"persona-ml-auto\");\n              } else {\n                // Clear chat is inline, close doesn't need ml-auto\n                closeButtonWrapperEl.classList.remove(\"persona-ml-auto\");\n              }\n            }\n          }\n        }\n\n        if (shouldShowClearChat) {\n          // Update size: composer-bar mode owns its sizing (16px to match\n          // the close icon), so leave size alone there. Floating-launcher\n          // and other modes still honor `launcher.clearChat.size`.\n          if (!isComposerBar()) {\n            const clearChatSize = clearChatConfig.size ?? \"32px\";\n            clearChatButton.style.height = clearChatSize;\n            clearChatButton.style.width = clearChatSize;\n          }\n\n          // Update icon\n          const clearChatIconName = clearChatConfig.iconName ?? \"refresh-cw\";\n          const clearChatIconColor = clearChatConfig.iconColor ?? \"\";\n\n          clearChatButton.style.color =\n            clearChatIconColor || HEADER_THEME_CSS.actionIconColor;\n\n          // Clear existing icon and render new one. Composer-bar shrinks\n          // the icon to match its 16px button.\n          clearChatButton.innerHTML = \"\";\n          const clearChatIconSize = isComposerBar() ? \"14px\" : \"20px\";\n          const iconSvg = renderLucideIcon(clearChatIconName, clearChatIconSize, \"currentColor\", 2);\n          if (iconSvg) {\n            clearChatButton.appendChild(iconSvg);\n          }\n\n          // Update background color\n          if (clearChatConfig.backgroundColor) {\n            clearChatButton.style.backgroundColor = clearChatConfig.backgroundColor;\n            clearChatButton.classList.remove(\"hover:persona-bg-gray-100\");\n          } else {\n            clearChatButton.style.backgroundColor = \"\";\n            clearChatButton.classList.add(\"hover:persona-bg-gray-100\");\n          }\n\n          // Update border\n          if (clearChatConfig.borderWidth || clearChatConfig.borderColor) {\n            const borderWidth = clearChatConfig.borderWidth || \"0px\";\n            const borderColor = clearChatConfig.borderColor || \"transparent\";\n            clearChatButton.style.border = `${borderWidth} solid ${borderColor}`;\n            clearChatButton.classList.remove(\"persona-border-none\");\n          } else {\n            clearChatButton.style.border = \"\";\n            clearChatButton.classList.add(\"persona-border-none\");\n          }\n\n          // Update border radius\n          if (clearChatConfig.borderRadius) {\n            clearChatButton.style.borderRadius = clearChatConfig.borderRadius;\n            clearChatButton.classList.remove(\"persona-rounded-full\");\n          } else {\n            clearChatButton.style.borderRadius = \"\";\n            clearChatButton.classList.add(\"persona-rounded-full\");\n          }\n\n          // Update padding\n          if (clearChatConfig.paddingX) {\n            clearChatButton.style.paddingLeft = clearChatConfig.paddingX;\n            clearChatButton.style.paddingRight = clearChatConfig.paddingX;\n          } else {\n            clearChatButton.style.paddingLeft = \"\";\n            clearChatButton.style.paddingRight = \"\";\n          }\n          if (clearChatConfig.paddingY) {\n            clearChatButton.style.paddingTop = clearChatConfig.paddingY;\n            clearChatButton.style.paddingBottom = clearChatConfig.paddingY;\n          } else {\n            clearChatButton.style.paddingTop = \"\";\n            clearChatButton.style.paddingBottom = \"\";\n          }\n\n          const clearChatTooltipText = clearChatConfig.tooltipText ?? \"Clear chat\";\n          const clearChatShowTooltip = clearChatConfig.showTooltip ?? true;\n\n          clearChatButton.setAttribute(\"aria-label\", clearChatTooltipText);\n\n          if (clearChatButtonWrapper) {\n            // Clean up old tooltip event listeners if they exist\n            if ((clearChatButtonWrapper as any)._cleanupTooltip) {\n              (clearChatButtonWrapper as any)._cleanupTooltip();\n              delete (clearChatButtonWrapper as any)._cleanupTooltip;\n            }\n\n            // Set up new portaled tooltip with event listeners\n            if (clearChatShowTooltip && clearChatTooltipText) {\n              let portaledTooltip: HTMLElement | null = null;\n\n              const showTooltip = () => {\n                if (portaledTooltip || !clearChatButton) return; // Already showing or button doesn't exist\n\n                const tooltipDocument = clearChatButton.ownerDocument;\n                const tooltipContainer = tooltipDocument.body;\n                if (!tooltipContainer) return;\n\n                // Create tooltip element\n                portaledTooltip = createElementInDocument(\n                  tooltipDocument,\n                  \"div\",\n                  \"persona-clear-chat-tooltip\"\n                );\n                portaledTooltip.textContent = clearChatTooltipText;\n\n                // Add arrow\n                const arrow = createElementInDocument(tooltipDocument, \"div\");\n                arrow.className = \"persona-clear-chat-tooltip-arrow\";\n                portaledTooltip.appendChild(arrow);\n\n                // Get button position\n                const buttonRect = clearChatButton.getBoundingClientRect();\n\n                // Position tooltip above button\n                portaledTooltip.style.position = \"fixed\";\n                portaledTooltip.style.zIndex = String(PORTALED_OVERLAY_Z_INDEX);\n                portaledTooltip.style.left = `${buttonRect.left + buttonRect.width / 2}px`;\n                portaledTooltip.style.top = `${buttonRect.top - 8}px`;\n                portaledTooltip.style.transform = \"translate(-50%, -100%)\";\n\n                // Append to body\n                tooltipContainer.appendChild(portaledTooltip);\n              };\n\n              const hideTooltip = () => {\n                if (portaledTooltip && portaledTooltip.parentNode) {\n                  portaledTooltip.parentNode.removeChild(portaledTooltip);\n                  portaledTooltip = null;\n                }\n              };\n\n              // Add event listeners\n              clearChatButtonWrapper.addEventListener(\"mouseenter\", showTooltip);\n              clearChatButtonWrapper.addEventListener(\"mouseleave\", hideTooltip);\n              clearChatButton.addEventListener(\"focus\", showTooltip);\n              clearChatButton.addEventListener(\"blur\", hideTooltip);\n\n              // Store cleanup function on the button for later use\n              (clearChatButtonWrapper as any)._cleanupTooltip = () => {\n                hideTooltip();\n                if (clearChatButtonWrapper) {\n                  clearChatButtonWrapper.removeEventListener(\"mouseenter\", showTooltip);\n                  clearChatButtonWrapper.removeEventListener(\"mouseleave\", hideTooltip);\n                }\n                if (clearChatButton) {\n                  clearChatButton.removeEventListener(\"focus\", showTooltip);\n                  clearChatButton.removeEventListener(\"blur\", hideTooltip);\n                }\n              };\n            }\n          }\n        }\n      }\n\n      const nextParsers =\n        config.actionParsers && config.actionParsers.length\n          ? config.actionParsers\n          : [defaultJsonActionParser];\n      const nextHandlers =\n        config.actionHandlers && config.actionHandlers.length\n          ? config.actionHandlers\n          : [defaultActionHandlers.message, defaultActionHandlers.messageAndClick];\n\n      actionManager = createActionManager({\n        parsers: nextParsers,\n        handlers: nextHandlers,\n        getSessionMetadata,\n        updateSessionMetadata,\n        emit: eventBus.emit,\n        documentRef: typeof document !== \"undefined\" ? document : null\n      });\n\n      postprocess = buildPostprocessor(config, actionManager, handleResubmitRequested);\n      session.updateConfig(config);\n      renderMessagesWithPlugins(\n        messagesWrapper,\n        session.getMessages(),\n        postprocess\n      );\n      renderSuggestions();\n      updateCopy();\n      setComposerDisabled(session.isStreaming());\n      \n      // Update voice recognition mic button visibility\n      const voiceRecognitionEnabled = config.voiceRecognition?.enabled === true;\n      const hasSpeechRecognition =\n        typeof window !== 'undefined' &&\n        (typeof (window as any).webkitSpeechRecognition !== 'undefined' ||\n         typeof (window as any).SpeechRecognition !== 'undefined');\n      const hasRuntypeProvider =\n        config.voiceRecognition?.provider?.type === 'runtype';\n      const hasVoiceInput = hasSpeechRecognition || hasRuntypeProvider;\n\n      if (voiceRecognitionEnabled && hasVoiceInput) {\n        // Create or update mic button\n        if (!micButton || !micButtonWrapper) {\n          // Create new mic button\n          const micButtonResult = createMicButton(config.voiceRecognition, config.sendButton);\n          if (micButtonResult) {\n            // Update the mutable references\n            micButton = micButtonResult.micButton;\n            micButtonWrapper = micButtonResult.micButtonWrapper;\n            \n            // Insert into right actions before send button wrapper\n            rightActions.insertBefore(micButtonWrapper, sendButtonWrapper);\n            \n            // Wire up click handler\n            micButton.addEventListener(\"click\", handleMicButtonClick);\n            \n            // Set disabled state\n            micButton.disabled = session.isStreaming();\n          }\n        } else {\n          // Update existing mic button with new config\n          const voiceConfig = config.voiceRecognition ?? {};\n          const sendButtonConfig = config.sendButton ?? {};\n          \n          // Update icon name and size\n          const micIconName = voiceConfig.iconName ?? \"mic\";\n          const buttonSize = sendButtonConfig.size ?? \"40px\";\n          const micIconSize = voiceConfig.iconSize ?? buttonSize;\n          const micIconSizeNum = parseFloat(micIconSize) || 24;\n          \n          micButton.style.width = micIconSize;\n          micButton.style.height = micIconSize;\n          micButton.style.minWidth = micIconSize;\n          micButton.style.minHeight = micIconSize;\n          \n          // Update icon\n          const iconColor = voiceConfig.iconColor ?? sendButtonConfig.textColor ?? \"currentColor\";\n          micButton.innerHTML = \"\";\n          const micIconSvg = renderLucideIcon(micIconName, micIconSizeNum, iconColor, 2);\n          if (micIconSvg) {\n            micButton.appendChild(micIconSvg);\n          } else {\n            micButton.textContent = \"🎤\";\n          }\n          \n          // Update colors from config or theme tokens\n          const backgroundColor = voiceConfig.backgroundColor ?? sendButtonConfig.backgroundColor;\n          if (backgroundColor) {\n            micButton.style.backgroundColor = backgroundColor;\n          } else {\n            micButton.style.backgroundColor = \"\";\n          }\n\n          if (iconColor) {\n            micButton.style.color = iconColor;\n          } else {\n            micButton.style.color = \"var(--persona-text, #111827)\";\n          }\n          \n          // Update border styling\n          if (voiceConfig.borderWidth) {\n            micButton.style.borderWidth = voiceConfig.borderWidth;\n            micButton.style.borderStyle = \"solid\";\n          } else {\n            micButton.style.borderWidth = \"\";\n            micButton.style.borderStyle = \"\";\n          }\n          if (voiceConfig.borderColor) {\n            micButton.style.borderColor = voiceConfig.borderColor;\n          } else {\n            micButton.style.borderColor = \"\";\n          }\n          \n          // Update padding styling\n          if (voiceConfig.paddingX) {\n            micButton.style.paddingLeft = voiceConfig.paddingX;\n            micButton.style.paddingRight = voiceConfig.paddingX;\n          } else {\n            micButton.style.paddingLeft = \"\";\n            micButton.style.paddingRight = \"\";\n          }\n          if (voiceConfig.paddingY) {\n            micButton.style.paddingTop = voiceConfig.paddingY;\n            micButton.style.paddingBottom = voiceConfig.paddingY;\n          } else {\n            micButton.style.paddingTop = \"\";\n            micButton.style.paddingBottom = \"\";\n          }\n          \n          // Update tooltip\n          const tooltip = micButtonWrapper?.querySelector(\".persona-send-button-tooltip\") as HTMLElement | null;\n          const tooltipText = voiceConfig.tooltipText ?? \"Start voice recognition\";\n          const showTooltip = voiceConfig.showTooltip ?? false;\n          if (showTooltip && tooltipText) {\n            if (!tooltip) {\n              // Create tooltip if it doesn't exist\n              const newTooltip = document.createElement(\"div\");\n              newTooltip.className = \"persona-send-button-tooltip\";\n              newTooltip.textContent = tooltipText;\n              micButtonWrapper?.insertBefore(newTooltip, micButton);\n            } else {\n              tooltip.textContent = tooltipText;\n              tooltip.style.display = \"\";\n            }\n          } else if (tooltip) {\n            // Hide tooltip if disabled\n            tooltip.style.display = \"none\";\n          }\n          \n          // Show and update disabled state\n          micButtonWrapper.style.display = \"\";\n          micButton.disabled = session.isStreaming();\n        }\n      } else {\n        // Hide mic button\n        if (micButton && micButtonWrapper) {\n          micButtonWrapper.style.display = \"none\";\n          // Stop any active recording if disabling\n          if (config.voiceRecognition?.provider?.type === 'runtype') {\n            if (session.isVoiceActive()) session.toggleVoice();\n          } else if (isRecording) {\n            stopVoiceRecognition();\n          }\n        }\n      }\n\n      // Update attachment button visibility based on attachments config\n      const attachmentsEnabled = config.attachments?.enabled === true;\n      if (attachmentsEnabled) {\n        // Create or show attachment button\n        if (!attachmentButtonWrapper || !attachmentButton) {\n          // Need to create the attachment elements dynamically\n          const attachmentsConfig = config.attachments ?? {};\n          const sendButtonConfig = config.sendButton ?? {};\n          const buttonSize = sendButtonConfig.size ?? \"40px\";\n\n          // Create previews container if not exists\n          if (!attachmentPreviewsContainer) {\n            attachmentPreviewsContainer = createElement(\"div\", \"persona-attachment-previews persona-flex persona-flex-wrap persona-gap-2 persona-mb-2\");\n            attachmentPreviewsContainer.style.display = \"none\";\n            composerForm.insertBefore(attachmentPreviewsContainer, textarea);\n          }\n\n          // Create file input if not exists\n          if (!attachmentInput) {\n            attachmentInput = document.createElement(\"input\");\n            attachmentInput.type = \"file\";\n            attachmentInput.accept = (attachmentsConfig.allowedTypes ?? ALL_SUPPORTED_MIME_TYPES).join(\",\");\n            attachmentInput.multiple = (attachmentsConfig.maxFiles ?? 4) > 1;\n            attachmentInput.style.display = \"none\";\n            attachmentInput.setAttribute(\"aria-label\", \"Attach files\");\n            composerForm.insertBefore(attachmentInput, textarea);\n          }\n\n          // Create attachment button wrapper\n          attachmentButtonWrapper = createElement(\"div\", \"persona-send-button-wrapper\");\n\n          // Create attachment button\n          attachmentButton = createElement(\n            \"button\",\n            \"persona-rounded-button persona-flex persona-items-center persona-justify-center disabled:persona-opacity-50 persona-cursor-pointer persona-attachment-button\"\n          ) as HTMLButtonElement;\n          attachmentButton.type = \"button\";\n          attachmentButton.setAttribute(\"aria-label\", attachmentsConfig.buttonTooltipText ?? \"Attach file\");\n\n          // Default to paperclip icon\n          const attachIconName = attachmentsConfig.buttonIconName ?? \"paperclip\";\n          const attachIconSize = buttonSize;\n          const buttonSizeNum = parseFloat(attachIconSize) || 40;\n          // Icon should be ~60% of button size to match other icons visually\n          const attachIconSizeNum = Math.round(buttonSizeNum * 0.6);\n\n          attachmentButton.style.width = attachIconSize;\n          attachmentButton.style.height = attachIconSize;\n          attachmentButton.style.minWidth = attachIconSize;\n          attachmentButton.style.minHeight = attachIconSize;\n          attachmentButton.style.fontSize = \"18px\";\n          attachmentButton.style.lineHeight = \"1\";\n          attachmentButton.style.backgroundColor = \"transparent\";\n          attachmentButton.style.color = \"var(--persona-primary, #111827)\";\n          attachmentButton.style.border = \"none\";\n          attachmentButton.style.borderRadius = \"6px\";\n          attachmentButton.style.transition = \"background-color 0.15s ease\";\n\n          // Add hover effect via mouseenter/mouseleave\n          attachmentButton.addEventListener(\"mouseenter\", () => {\n            attachmentButton!.style.backgroundColor = \"var(--persona-palette-colors-black-alpha-50, rgba(0, 0, 0, 0.05))\";\n          });\n          attachmentButton.addEventListener(\"mouseleave\", () => {\n            attachmentButton!.style.backgroundColor = \"transparent\";\n          });\n\n          const attachIconSvg = renderLucideIcon(attachIconName, attachIconSizeNum, \"currentColor\", 1.5);\n          if (attachIconSvg) {\n            attachmentButton.appendChild(attachIconSvg);\n          } else {\n            attachmentButton.textContent = \"📎\";\n          }\n\n          attachmentButton.addEventListener(\"click\", (e) => {\n            e.preventDefault();\n            attachmentInput?.click();\n          });\n\n          attachmentButtonWrapper.appendChild(attachmentButton);\n\n          // Add tooltip\n          const attachTooltipText = attachmentsConfig.buttonTooltipText ?? \"Attach file\";\n          const tooltip = createElement(\"div\", \"persona-send-button-tooltip\");\n          tooltip.textContent = attachTooltipText;\n          attachmentButtonWrapper.appendChild(tooltip);\n\n          // Insert into left actions container\n          leftActions.append(attachmentButtonWrapper);\n\n          // Initialize attachment manager\n          if (!attachmentManager && attachmentInput && attachmentPreviewsContainer) {\n            attachmentManager = AttachmentManager.fromConfig(attachmentsConfig);\n            attachmentManager.setPreviewsContainer(attachmentPreviewsContainer);\n\n            attachmentInput.addEventListener(\"change\", async () => {\n              if (attachmentManager && attachmentInput?.files) {\n                await attachmentManager.handleFileSelect(attachmentInput.files);\n                attachmentInput.value = \"\";\n              }\n            });\n          }\n\n          // Create drop overlay if missing\n          if (!container.querySelector(\".persona-attachment-drop-overlay\")) {\n            container.appendChild(buildDropOverlay(attachmentsConfig.dropOverlay));\n          }\n        } else {\n          // Show existing attachment button and update config\n          attachmentButtonWrapper.style.display = \"\";\n\n          // Update file input accept attribute when config changes\n          const attachmentsConfig = config.attachments ?? {};\n          if (attachmentInput) {\n            attachmentInput.accept = (attachmentsConfig.allowedTypes ?? ALL_SUPPORTED_MIME_TYPES).join(\",\");\n            attachmentInput.multiple = (attachmentsConfig.maxFiles ?? 4) > 1;\n          }\n\n          // Update attachment manager config\n          if (attachmentManager) {\n            attachmentManager.updateConfig({\n              allowedTypes: attachmentsConfig.allowedTypes,\n              maxFileSize: attachmentsConfig.maxFileSize,\n              maxFiles: attachmentsConfig.maxFiles\n            });\n          }\n        }\n      } else {\n        // Hide attachment button if disabled\n        if (attachmentButtonWrapper) {\n          attachmentButtonWrapper.style.display = \"none\";\n        }\n        // Clear any pending attachments\n        if (attachmentManager) {\n          attachmentManager.clearAttachments();\n        }\n        // Remove drop overlay\n        container.querySelector(\".persona-attachment-drop-overlay\")?.remove();\n      }\n\n      // Update send button styling\n      const sendButtonConfig = config.sendButton ?? {};\n      const useIcon = sendButtonConfig.useIcon ?? false;\n      const iconText = sendButtonConfig.iconText ?? \"↑\";\n      const iconName = sendButtonConfig.iconName;\n      const tooltipText = sendButtonConfig.tooltipText ?? \"Send message\";\n      const showTooltip = sendButtonConfig.showTooltip ?? false;\n      const buttonSize = sendButtonConfig.size ?? \"40px\";\n      const backgroundColor = sendButtonConfig.backgroundColor;\n      const textColor = sendButtonConfig.textColor;\n\n      // Update button content and styling based on mode\n      if (useIcon) {\n        // Icon mode: circular button\n        sendButton.style.width = buttonSize;\n        sendButton.style.height = buttonSize;\n        sendButton.style.minWidth = buttonSize;\n        sendButton.style.minHeight = buttonSize;\n        sendButton.style.fontSize = \"18px\";\n        sendButton.style.lineHeight = \"1\";\n        \n        // Clear existing content\n        sendButton.innerHTML = \"\";\n        \n        // Set foreground color from config or theme token\n        if (textColor) {\n          sendButton.style.color = textColor;\n        } else {\n          sendButton.style.color = \"var(--persona-button-primary-fg, #ffffff)\";\n        }\n\n        // Use Lucide icon if iconName is provided, otherwise fall back to iconText\n        if (iconName) {\n          const iconSize = parseFloat(buttonSize) || 24;\n          const iconColor = textColor?.trim() || \"currentColor\";\n          const iconSvg = renderLucideIcon(iconName, iconSize, iconColor, 2);\n          if (iconSvg) {\n            sendButton.appendChild(iconSvg);\n          } else {\n            sendButton.textContent = iconText;\n          }\n        } else {\n          sendButton.textContent = iconText;\n        }\n        \n        // Update classes\n        sendButton.className = \"persona-rounded-button persona-flex persona-items-center persona-justify-center disabled:persona-opacity-50 persona-cursor-pointer\";\n        \n        if (backgroundColor) {\n          sendButton.style.backgroundColor = backgroundColor;\n          sendButton.classList.remove(\"persona-bg-persona-primary\");\n        } else {\n          sendButton.style.backgroundColor = \"\";\n          sendButton.classList.add(\"persona-bg-persona-primary\");\n        }\n      } else {\n        // Text mode: existing behavior\n        sendButton.textContent = config.copy?.sendButtonLabel ?? \"Send\";\n        sendButton.style.width = \"\";\n        sendButton.style.height = \"\";\n        sendButton.style.minWidth = \"\";\n        sendButton.style.minHeight = \"\";\n        sendButton.style.fontSize = \"\";\n        sendButton.style.lineHeight = \"\";\n        \n        // Update classes\n        sendButton.className = \"persona-rounded-button persona-bg-persona-accent persona-px-4 persona-py-2 persona-text-sm persona-font-semibold persona-text-white disabled:persona-opacity-50 persona-cursor-pointer\";\n        \n        if (backgroundColor) {\n          sendButton.style.backgroundColor = backgroundColor;\n          sendButton.classList.remove(\"persona-bg-persona-accent\");\n        } else {\n          sendButton.classList.add(\"persona-bg-persona-accent\");\n        }\n        \n        if (textColor) {\n          sendButton.style.color = textColor;\n        } else {\n          sendButton.classList.add(\"persona-text-white\");\n        }\n      }\n\n      // Apply border styling\n      if (sendButtonConfig.borderWidth) {\n        sendButton.style.borderWidth = sendButtonConfig.borderWidth;\n        sendButton.style.borderStyle = \"solid\";\n      } else {\n        sendButton.style.borderWidth = \"\";\n        sendButton.style.borderStyle = \"\";\n      }\n      if (sendButtonConfig.borderColor) {\n        sendButton.style.borderColor = sendButtonConfig.borderColor;\n      } else {\n        sendButton.style.borderColor = \"\";\n      }\n\n      // Apply padding styling (works in both icon and text mode)\n      if (sendButtonConfig.paddingX) {\n        sendButton.style.paddingLeft = sendButtonConfig.paddingX;\n        sendButton.style.paddingRight = sendButtonConfig.paddingX;\n      } else {\n        sendButton.style.paddingLeft = \"\";\n        sendButton.style.paddingRight = \"\";\n      }\n      if (sendButtonConfig.paddingY) {\n        sendButton.style.paddingTop = sendButtonConfig.paddingY;\n        sendButton.style.paddingBottom = sendButtonConfig.paddingY;\n      } else {\n        sendButton.style.paddingTop = \"\";\n        sendButton.style.paddingBottom = \"\";\n      }\n\n      // Update tooltip\n      const tooltip = sendButtonWrapper?.querySelector(\".persona-send-button-tooltip\") as HTMLElement | null;\n      if (showTooltip && tooltipText) {\n        if (!tooltip) {\n          // Create tooltip if it doesn't exist\n          const newTooltip = document.createElement(\"div\");\n          newTooltip.className = \"persona-send-button-tooltip\";\n          newTooltip.textContent = tooltipText;\n          sendButtonWrapper?.insertBefore(newTooltip, sendButton);\n        } else {\n          tooltip.textContent = tooltipText;\n          tooltip.style.display = \"\";\n        }\n      } else if (tooltip) {\n        tooltip.style.display = \"none\";\n      }\n      \n      // Update contentMaxWidth on messages wrapper and composer. Same\n      // composer-bar fallback as the initial read above.\n      const updatedContentMaxWidth =\n        config.layout?.contentMaxWidth ??\n        (isComposerBar()\n          ? config.launcher?.composerBar?.contentMaxWidth ?? \"720px\"\n          : undefined);\n      if (updatedContentMaxWidth) {\n        messagesWrapper.style.maxWidth = updatedContentMaxWidth;\n        messagesWrapper.style.marginLeft = \"auto\";\n        messagesWrapper.style.marginRight = \"auto\";\n        messagesWrapper.style.width = \"100%\";\n        if (composerForm) {\n          composerForm.style.maxWidth = updatedContentMaxWidth;\n          composerForm.style.marginLeft = \"auto\";\n          composerForm.style.marginRight = \"auto\";\n        }\n        if (suggestions) {\n          suggestions.style.maxWidth = updatedContentMaxWidth;\n          suggestions.style.marginLeft = \"auto\";\n          suggestions.style.marginRight = \"auto\";\n        }\n      } else {\n        messagesWrapper.style.maxWidth = \"\";\n        messagesWrapper.style.marginLeft = \"\";\n        messagesWrapper.style.marginRight = \"\";\n        messagesWrapper.style.width = \"\";\n        if (composerForm) {\n          composerForm.style.maxWidth = \"\";\n          composerForm.style.marginLeft = \"\";\n          composerForm.style.marginRight = \"\";\n        }\n        if (suggestions) {\n          suggestions.style.maxWidth = \"\";\n          suggestions.style.marginLeft = \"\";\n          suggestions.style.marginRight = \"\";\n        }\n      }\n\n      // Update status indicator visibility and text\n      const statusIndicatorConfig = config.statusIndicator ?? {};\n      const isVisible = statusIndicatorConfig.visible ?? true;\n      statusText.style.display = isVisible ? \"\" : \"none\";\n      \n      // Update status text if status is currently set\n      if (session) {\n        const currentStatus = session.getStatus();\n        const getCurrentStatusText = (s: AgentWidgetSessionStatus): string => {\n          if (s === \"idle\") return statusIndicatorConfig.idleText ?? statusCopy.idle;\n          if (s === \"connecting\") return statusIndicatorConfig.connectingText ?? statusCopy.connecting;\n          if (s === \"connected\") return statusIndicatorConfig.connectedText ?? statusCopy.connected;\n          if (s === \"error\") return statusIndicatorConfig.errorText ?? statusCopy.error;\n          return statusCopy[s];\n        };\n        applyStatusToElement(statusText, getCurrentStatusText(currentStatus), statusIndicatorConfig, currentStatus);\n      }\n\n      // Update status text alignment\n      statusText.classList.remove(\"persona-text-left\", \"persona-text-center\", \"persona-text-right\");\n      const alignClass = statusIndicatorConfig.align === \"left\" ? \"persona-text-left\"\n        : statusIndicatorConfig.align === \"center\" ? \"persona-text-center\"\n        : \"persona-text-right\";\n      statusText.classList.add(alignClass);\n    },\n    open() {\n      if (!isPanelToggleable()) return;\n      setOpenState(true, \"api\");\n    },\n    close() {\n      if (!isPanelToggleable()) return;\n      setOpenState(false, \"api\");\n    },\n    toggle() {\n      if (!isPanelToggleable()) return;\n      setOpenState(!open, \"api\");\n    },\n    clearChat() {\n      // Clear messages in session (this will trigger onMessagesChanged which re-renders)\n      artifactsPaneUserHidden = false;\n      session.clearMessages();\n      messageCache.clear();\n      resumeAutoScroll();\n\n      // Always clear the default localStorage key\n      try {\n        localStorage.removeItem(DEFAULT_CHAT_HISTORY_STORAGE_KEY);\n        if (config.debug) {\n          console.log(`[AgentWidget] Cleared default localStorage key: ${DEFAULT_CHAT_HISTORY_STORAGE_KEY}`);\n        }\n      } catch (error) {\n        console.error(\"[AgentWidget] Failed to clear default localStorage:\", error);\n      }\n\n      // Also clear custom localStorage key if configured\n      if (config.clearChatHistoryStorageKey && config.clearChatHistoryStorageKey !== DEFAULT_CHAT_HISTORY_STORAGE_KEY) {\n        try {\n          localStorage.removeItem(config.clearChatHistoryStorageKey);\n          if (config.debug) {\n            console.log(`[AgentWidget] Cleared custom localStorage key: ${config.clearChatHistoryStorageKey}`);\n          }\n        } catch (error) {\n          console.error(\"[AgentWidget] Failed to clear custom localStorage:\", error);\n        }\n      }\n\n      // Dispatch custom event for external handlers (e.g., localStorage clearing in examples)\n      const clearEvent = new CustomEvent(\"persona:clear-chat\", {\n        detail: { timestamp: new Date().toISOString() }\n      });\n      window.dispatchEvent(clearEvent);\n\n      if (storageAdapter?.clear) {\n        try {\n          const result = storageAdapter.clear();\n          if (result instanceof Promise) {\n            result.catch((error) => {\n              if (typeof console !== \"undefined\") {\n                // eslint-disable-next-line no-console\n                console.error(\"[AgentWidget] Failed to clear storage adapter:\", error);\n              }\n            });\n          }\n        } catch (error) {\n          if (typeof console !== \"undefined\") {\n            // eslint-disable-next-line no-console\n            console.error(\"[AgentWidget] Failed to clear storage adapter:\", error);\n          }\n        }\n      }\n      persistentMetadata = {};\n      actionManager.syncFromMetadata();\n\n      // Clear event stream buffer and store, and reset throughput tracking\n      eventStreamBuffer?.clear();\n      throughputTracker?.reset();\n      eventStreamView?.update();\n    },\n    setMessage(message: string): boolean {\n      if (!textarea) return false;\n      if (session.isStreaming()) return false;\n      \n      // Auto-open widget if closed and the panel is toggleable\n      if (!open && isPanelToggleable()) {\n        setOpenState(true, \"system\");\n      }\n      \n      textarea.value = message;\n      // Trigger input event for any listeners\n      textarea.dispatchEvent(new Event('input', { bubbles: true }));\n      return true;\n    },\n    submitMessage(message?: string): boolean {\n      if (session.isStreaming()) return false;\n      \n      const valueToSubmit = message?.trim() || textarea.value.trim();\n      if (!valueToSubmit) return false;\n      \n      // Auto-open widget if closed and the panel is toggleable\n      if (!open && isPanelToggleable()) {\n        setOpenState(true, \"system\");\n      }\n      \n      textarea.value = \"\";\n      textarea.style.height = \"auto\"; // Reset height after clearing\n      session.sendMessage(valueToSubmit);\n      return true;\n    },\n    startVoiceRecognition(): boolean {\n      if (session.isStreaming()) return false;\n      if (config.voiceRecognition?.provider?.type === 'runtype') {\n        if (session.isVoiceActive()) return true;\n        if (!open && isPanelToggleable()) setOpenState(true, \"system\");\n        voiceState.manuallyDeactivated = false;\n        persistVoiceMetadata();\n        session.toggleVoice().then(() => {\n          voiceState.active = session.isVoiceActive();\n          emitVoiceState(\"user\");\n          if (session.isVoiceActive()) applyRuntypeMicRecordingStyles();\n        });\n        return true;\n      }\n      if (isRecording) return true;\n      const SpeechRecognitionClass = getSpeechRecognitionClass();\n      if (!SpeechRecognitionClass) return false;\n      if (!open && isPanelToggleable()) setOpenState(true, \"system\");\n      voiceState.manuallyDeactivated = false;\n      persistVoiceMetadata();\n      startVoiceRecognition(\"user\");\n      return true;\n    },\n    stopVoiceRecognition(): boolean {\n      if (config.voiceRecognition?.provider?.type === 'runtype') {\n        if (!session.isVoiceActive()) return false;\n        session.toggleVoice().then(() => {\n          voiceState.active = false;\n          voiceState.manuallyDeactivated = true;\n          persistVoiceMetadata();\n          emitVoiceState(\"user\");\n          removeRuntypeMicStateStyles();\n        });\n        return true;\n      }\n      if (!isRecording) return false;\n\n      voiceState.manuallyDeactivated = true;\n      persistVoiceMetadata();\n      stopVoiceRecognition(\"user\");\n      return true;\n    },\n    injectMessage(options: InjectMessageOptions): AgentWidgetMessage {\n      // Auto-open widget if closed and the panel is toggleable\n      if (!open && isPanelToggleable()) {\n        setOpenState(true, \"system\");\n      }\n      return session.injectMessage(options);\n    },\n    injectAssistantMessage(options: InjectAssistantMessageOptions): AgentWidgetMessage {\n      // Auto-open widget if closed and the panel is toggleable\n      if (!open && isPanelToggleable()) {\n        setOpenState(true, \"system\");\n      }\n      const result = session.injectAssistantMessage(options);\n\n      // Check if we should trigger resubmit after injection\n      // This handles the case where a handler returned resubmit: true and then\n      // injected a message - we wait until after injection to trigger resubmit\n      if (pendingResubmit) {\n        pendingResubmit = false;\n        if (pendingResubmitTimeout) {\n          clearTimeout(pendingResubmitTimeout);\n          pendingResubmitTimeout = null;\n        }\n        // Short delay to ensure message is in context\n        setTimeout(() => {\n          if (session && !session.isStreaming()) {\n            session.continueConversation();\n          }\n        }, 100);\n      }\n\n      return result;\n    },\n    injectUserMessage(options: InjectUserMessageOptions): AgentWidgetMessage {\n      // Auto-open widget if closed and the panel is toggleable\n      if (!open && isPanelToggleable()) {\n        setOpenState(true, \"system\");\n      }\n      return session.injectUserMessage(options);\n    },\n    injectSystemMessage(options: InjectSystemMessageOptions): AgentWidgetMessage {\n      // Auto-open widget if closed and the panel is toggleable\n      if (!open && isPanelToggleable()) {\n        setOpenState(true, \"system\");\n      }\n      return session.injectSystemMessage(options);\n    },\n    injectMessageBatch(optionsList: InjectMessageOptions[]): AgentWidgetMessage[] {\n      if (!open && isPanelToggleable()) {\n        setOpenState(true, \"system\");\n      }\n      return session.injectMessageBatch(optionsList);\n    },\n    injectComponentDirective(\n      options: InjectComponentDirectiveOptions\n    ): AgentWidgetMessage {\n      // Auto-open widget if closed and the panel is toggleable\n      if (!open && isPanelToggleable()) {\n        setOpenState(true, \"system\");\n      }\n      return session.injectComponentDirective(options);\n    },\n    /** @deprecated Use injectMessage() instead */\n    injectTestMessage(event: AgentWidgetEvent) {\n      // Auto-open widget if closed and the panel is toggleable\n      if (!open && isPanelToggleable()) {\n        setOpenState(true, \"system\");\n      }\n      session.injectTestEvent(event);\n    },\n    async connectStream(\n      stream: ReadableStream<Uint8Array>,\n      options?: { assistantMessageId?: string }\n    ): Promise<void> {\n      return session.connectStream(stream, options);\n    },\n    /** Push a raw event into the event stream buffer (for testing/debugging) */\n    __pushEventStreamEvent(event: { type: string; payload: unknown }): void {\n      if (eventStreamBuffer) {\n        throughputTracker?.processEvent(event.type, event.payload);\n        eventStreamBuffer.push({\n          id: `evt-${Date.now()}-${Math.random().toString(36).slice(2, 8)}`,\n          type: event.type,\n          timestamp: Date.now(),\n          payload: JSON.stringify(event.payload)\n        });\n      }\n    },\n    showEventStream(): void {\n      if (!showEventStreamToggle || !eventStreamBuffer) return;\n      toggleEventStreamOn();\n    },\n    hideEventStream(): void {\n      if (!eventStreamVisible) return;\n      toggleEventStreamOff();\n    },\n    isEventStreamVisible(): boolean {\n      return eventStreamVisible;\n    },\n    showArtifacts(): void {\n      if (!artifactsSidebarEnabled(config)) return;\n      artifactsPaneUserHidden = false;\n      syncArtifactPane();\n      artifactPaneApi?.setMobileOpen(true);\n    },\n    hideArtifacts(): void {\n      if (!artifactsSidebarEnabled(config)) return;\n      artifactsPaneUserHidden = true;\n      syncArtifactPane();\n    },\n    upsertArtifact(manual: PersonaArtifactManualUpsert): PersonaArtifactRecord | null {\n      if (!artifactsSidebarEnabled(config)) return null;\n      // Programmatic adds should surface the pane even if the user previously hit Close.\n      artifactsPaneUserHidden = false;\n      return session.upsertArtifact(manual);\n    },\n    selectArtifact(id: string): void {\n      if (!artifactsSidebarEnabled(config)) return;\n      session.selectArtifact(id);\n    },\n    clearArtifacts(): void {\n      if (!artifactsSidebarEnabled(config)) return;\n      session.clearArtifacts();\n    },\n    getArtifacts(): PersonaArtifactRecord[] {\n      return session?.getArtifacts() ?? [];\n    },\n    getSelectedArtifactId(): string | null {\n      return session?.getSelectedArtifactId() ?? null;\n    },\n    focusInput(): boolean {\n      // Composer-bar's textarea is always reachable in the collapsed pill,\n      // so don't gate focus behind `open` for that mode.\n      if (launcherEnabled && !open && !isComposerBar()) return false;\n      if (!textarea) return false;\n      textarea.focus();\n      return true;\n    },\n    async resolveApproval(\n      approvalId: string,\n      decision: 'approved' | 'denied',\n      options?: AgentWidgetApprovalDecisionOptions\n    ): Promise<void> {\n      const messages = session.getMessages();\n      const approvalMessage = messages.find(\n        m => m.variant === \"approval\" && m.approval?.id === approvalId\n      );\n      if (!approvalMessage?.approval) {\n        throw new Error(`Approval not found: ${approvalId}`);\n      }\n      // Mirror the in-panel click handler: WebMCP gate bubbles resolve a local\n      // Promise the bridge is parked on (no server round-trip and they carry an\n      // empty executionId/agentId), so they must NOT hit the server approval\n      // API. Route by the `toolType` marker set in `requestWebMcpApproval`.\n      if (approvalMessage.approval.toolType === \"webmcp\") {\n        session.resolveWebMcpApproval(approvalMessage.id, decision);\n        return;\n      }\n      return session.resolveApproval(approvalMessage.approval, decision, options);\n    },\n    getMessages() {\n      return session.getMessages();\n    },\n    getStatus() {\n      return session.getStatus();\n    },\n    getPersistentMetadata() {\n      return { ...persistentMetadata };\n    },\n    updatePersistentMetadata(\n      updater: (prev: Record<string, unknown>) => Record<string, unknown>\n    ) {\n      updateSessionMetadata(updater);\n    },\n    on(event, handler) {\n      return eventBus.on(event, handler);\n    },\n    off(event, handler) {\n      eventBus.off(event, handler);\n    },\n    // State query methods\n    isOpen(): boolean {\n      return isPanelToggleable() && open;\n    },\n    isVoiceActive(): boolean {\n      return voiceState.active;\n    },\n    /**\n     * Toggle \"Read aloud\" for an assistant message: play → pause → resume (or\n     * play → stop when the engine can't pause). Speaks via the configured\n     * speech engine (browser Web Speech API by default).\n     */\n    toggleReadAloud(messageId: string): void {\n      session.toggleReadAloud(messageId);\n    },\n    /** Stop any in-progress read-aloud / text-to-speech playback. */\n    stopReadAloud(): void {\n      session.stopSpeaking();\n    },\n    /** Current read-aloud playback state for a message (`idle` unless active). */\n    getReadAloudState(messageId: string): ReadAloudState {\n      return session.getReadAloudState(messageId);\n    },\n    /** Subscribe to read-aloud state changes. Returns an unsubscribe function. */\n    onReadAloudChange(\n      listener: (activeId: string | null, state: ReadAloudState) => void\n    ): () => void {\n      return session.onReadAloudChange(listener);\n    },\n    getState(): AgentWidgetStateSnapshot {\n      return {\n        open: isPanelToggleable() && open,\n        launcherEnabled,\n        voiceActive: voiceState.active,\n        streaming: session.isStreaming()\n      };\n    },\n    // Feedback methods (CSAT/NPS)\n    showCSATFeedback(options?: Partial<CSATFeedbackOptions>) {\n      // Auto-open widget if closed and the panel is toggleable\n      if (!open && isPanelToggleable()) {\n        setOpenState(true, \"system\");\n      }\n      \n      // Remove any existing feedback forms\n      const existingFeedback = messagesWrapper.querySelector('.persona-feedback-container');\n      if (existingFeedback) {\n        existingFeedback.remove();\n      }\n      \n      const feedbackEl = createCSATFeedback({\n        onSubmit: async (rating, comment) => {\n          if (session.isClientTokenMode()) {\n            await session.submitCSATFeedback(rating, comment);\n          }\n          options?.onSubmit?.(rating, comment);\n        },\n        onDismiss: options?.onDismiss,\n        ...options,\n      });\n      \n      // Append to messages area at the bottom\n      messagesWrapper.appendChild(feedbackEl);\n      feedbackEl.scrollIntoView({ behavior: 'smooth', block: 'end' });\n    },\n    showNPSFeedback(options?: Partial<NPSFeedbackOptions>) {\n      // Auto-open widget if closed and the panel is toggleable\n      if (!open && isPanelToggleable()) {\n        setOpenState(true, \"system\");\n      }\n      \n      // Remove any existing feedback forms\n      const existingFeedback = messagesWrapper.querySelector('.persona-feedback-container');\n      if (existingFeedback) {\n        existingFeedback.remove();\n      }\n      \n      const feedbackEl = createNPSFeedback({\n        onSubmit: async (rating, comment) => {\n          if (session.isClientTokenMode()) {\n            await session.submitNPSFeedback(rating, comment);\n          }\n          options?.onSubmit?.(rating, comment);\n        },\n        onDismiss: options?.onDismiss,\n        ...options,\n      });\n      \n      // Append to messages area at the bottom\n      messagesWrapper.appendChild(feedbackEl);\n      feedbackEl.scrollIntoView({ behavior: 'smooth', block: 'end' });\n    },\n    async submitCSATFeedback(rating: number, comment?: string): Promise<void> {\n      return session.submitCSATFeedback(rating, comment);\n    },\n    async submitNPSFeedback(rating: number, comment?: string): Promise<void> {\n      return session.submitNPSFeedback(rating, comment);\n    },\n    destroy() {\n      if (toolElapsedTimerId != null) {\n        clearInterval(toolElapsedTimerId);\n        toolElapsedTimerId = null;\n      }\n      destroyCallbacks.forEach((cb) => cb());\n      wrapper.remove();\n      pillRoot?.remove();\n      launcherButtonInstance?.destroy();\n      customLauncherElement?.remove();\n      if (closeHandler) {\n        closeButton.removeEventListener(\"click\", closeHandler);\n      }\n    }\n  };\n\n  const shouldExposeDebugApi =\n    (runtimeOptions?.debugTools ?? false) || Boolean(config.debug);\n\n  if (shouldExposeDebugApi && typeof window !== \"undefined\") {\n    const previousDebug = (window as any).AgentWidgetBrowser;\n    const debugApi = {\n      controller,\n      getMessages: controller.getMessages,\n      getStatus: controller.getStatus,\n      getMetadata: controller.getPersistentMetadata,\n      updateMetadata: controller.updatePersistentMetadata,\n      clearHistory: () => controller.clearChat(),\n      setVoiceActive: (active: boolean) =>\n        active\n          ? controller.startVoiceRecognition()\n          : controller.stopVoiceRecognition()\n    };\n    (window as any).AgentWidgetBrowser = debugApi;\n    destroyCallbacks.push(() => {\n      if ((window as any).AgentWidgetBrowser === debugApi) {\n        (window as any).AgentWidgetBrowser = previousDebug;\n      }\n    });\n  }\n\n  // ============================================================================\n  // INSTANCE-SCOPED WINDOW EVENTS FOR PROGRAMMATIC CONTROL\n  // ============================================================================\n  if (typeof window !== \"undefined\") {\n    const instanceId = mount.getAttribute(\"data-persona-instance\") || mount.id || \"persona-\" + Math.random().toString(36).slice(2, 8);\n\n    const handleFocusInput = (e: Event) => {\n      const detail = (e as CustomEvent).detail;\n      if (!detail?.instanceId || detail.instanceId === instanceId) {\n        controller.focusInput();\n      }\n    };\n    window.addEventListener(\"persona:focusInput\", handleFocusInput);\n    destroyCallbacks.push(() => {\n      window.removeEventListener(\"persona:focusInput\", handleFocusInput);\n    });\n\n    if (showEventStreamToggle) {\n      const handleShowEvent = (e: Event) => {\n        const detail = (e as CustomEvent).detail;\n        if (!detail?.instanceId || detail.instanceId === instanceId) {\n          controller.showEventStream();\n        }\n      };\n      const handleHideEvent = (e: Event) => {\n        const detail = (e as CustomEvent).detail;\n        if (!detail?.instanceId || detail.instanceId === instanceId) {\n          controller.hideEventStream();\n        }\n      };\n      window.addEventListener(\"persona:showEventStream\", handleShowEvent);\n      window.addEventListener(\"persona:hideEventStream\", handleHideEvent);\n      destroyCallbacks.push(() => {\n        window.removeEventListener(\"persona:showEventStream\", handleShowEvent);\n        window.removeEventListener(\"persona:hideEventStream\", handleHideEvent);\n      });\n    }\n\n    const handleShowArtifacts = (e: Event) => {\n      const detail = (e as CustomEvent).detail;\n      if (!detail?.instanceId || detail.instanceId === instanceId) {\n        controller.showArtifacts();\n      }\n    };\n    const handleHideArtifacts = (e: Event) => {\n      const detail = (e as CustomEvent).detail;\n      if (!detail?.instanceId || detail.instanceId === instanceId) {\n        controller.hideArtifacts();\n      }\n    };\n    const handleUpsertArtifact = (e: Event) => {\n      const detail = (e as CustomEvent).detail;\n      if (detail?.instanceId && detail.instanceId !== instanceId) return;\n      if (detail?.artifact) {\n        controller.upsertArtifact(detail.artifact as PersonaArtifactManualUpsert);\n      }\n    };\n    const handleSelectArtifact = (e: Event) => {\n      const detail = (e as CustomEvent).detail;\n      if (detail?.instanceId && detail.instanceId !== instanceId) return;\n      if (typeof detail?.id === \"string\") {\n        controller.selectArtifact(detail.id);\n      }\n    };\n    const handleClearArtifacts = (e: Event) => {\n      const detail = (e as CustomEvent).detail;\n      if (!detail?.instanceId || detail.instanceId === instanceId) {\n        controller.clearArtifacts();\n      }\n    };\n    window.addEventListener(\"persona:showArtifacts\", handleShowArtifacts);\n    window.addEventListener(\"persona:hideArtifacts\", handleHideArtifacts);\n    window.addEventListener(\"persona:upsertArtifact\", handleUpsertArtifact);\n    window.addEventListener(\"persona:selectArtifact\", handleSelectArtifact);\n    window.addEventListener(\"persona:clearArtifacts\", handleClearArtifacts);\n    destroyCallbacks.push(() => {\n      window.removeEventListener(\"persona:showArtifacts\", handleShowArtifacts);\n      window.removeEventListener(\"persona:hideArtifacts\", handleHideArtifacts);\n      window.removeEventListener(\"persona:upsertArtifact\", handleUpsertArtifact);\n      window.removeEventListener(\"persona:selectArtifact\", handleSelectArtifact);\n      window.removeEventListener(\"persona:clearArtifacts\", handleClearArtifacts);\n    });\n  }\n\n  // ============================================================================\n  // STATE PERSISTENCE ACROSS PAGE NAVIGATIONS\n  // ============================================================================\n  const persistConfig = normalizePersistStateConfig(config.persistState);\n  \n  if (persistConfig && isPanelToggleable()) {\n    const storage = getPersistStorage(persistConfig.storage!);\n    const openKey = `${persistConfig.keyPrefix}widget-open`;\n    const voiceKey = `${persistConfig.keyPrefix}widget-voice`;\n    const voiceModeKey = `${persistConfig.keyPrefix}widget-voice-mode`;\n\n    if (storage) {\n      // Restore state from previous page\n      const wasOpen = persistConfig.persist?.openState && storage.getItem(openKey) === 'true';\n      const wasVoiceActive = persistConfig.persist?.voiceState && storage.getItem(voiceKey) === 'true';\n      // Also check if user was in voice mode (last message was via voice)\n      const wasInVoiceMode = persistConfig.persist?.voiceState && storage.getItem(voiceModeKey) === 'true';\n\n      if (wasOpen) {\n        // Use setTimeout to ensure DOM is ready\n        setTimeout(() => {\n          controller.open();\n\n          // After opening, restore input mode\n          setTimeout(() => {\n            // Restore voice if it was actively recording OR if user was in voice mode\n            if (wasVoiceActive || wasInVoiceMode) {\n              controller.startVoiceRecognition();\n            } else if (persistConfig.persist?.focusInput) {\n              const textarea = mount.querySelector('textarea') as HTMLTextAreaElement | null;\n              if (textarea) {\n                textarea.focus();\n              }\n            }\n          }, 100);\n        }, 0);\n      }\n\n      // Persist open/close state changes\n      if (persistConfig.persist?.openState) {\n        eventBus.on('widget:opened', () => {\n          storage.setItem(openKey, 'true');\n        });\n        eventBus.on('widget:closed', () => {\n          storage.setItem(openKey, 'false');\n        });\n      }\n\n      // Persist voice state changes\n      if (persistConfig.persist?.voiceState) {\n        eventBus.on('voice:state', (event) => {\n          storage.setItem(voiceKey, event.active ? 'true' : 'false');\n        });\n\n        // Persist whether user is in voice mode based on their messages\n        // This allows voice to resume after navigation even when recording was stopped for submission\n        eventBus.on('user:message', (message) => {\n          storage.setItem(voiceModeKey, message.viaVoice ? 'true' : 'false');\n        });\n      }\n\n      // Clear persisted state on chat clear\n      if (persistConfig.clearOnChatClear) {\n        const clearPersistState = () => {\n          storage.removeItem(openKey);\n          storage.removeItem(voiceKey);\n          storage.removeItem(voiceModeKey);\n        };\n\n        // Listen for clear chat event\n        const handleClearChat = () => clearPersistState();\n        window.addEventListener('persona:clear-chat', handleClearChat);\n\n        // Clean up listener on destroy\n        destroyCallbacks.push(() => {\n          window.removeEventListener('persona:clear-chat', handleClearChat);\n        });\n      }\n    }\n  }\n\n  // If onStateLoaded signalled open: true, open the panel after init.\n  // Mirrors the same setTimeout(0) pattern used by persistState restore so both\n  // can fire independently without interfering with each other.\n  if (shouldOpenAfterStateLoaded && isPanelToggleable()) {\n    setTimeout(() => { controller.open(); }, 0);\n  }\n\n  // Initial sync of the composer-bar peek banner so it reflects any\n  // restored history. Subsequent updates flow through `onMessagesChanged`,\n  // `onStreamingChanged`, `updateOpenState`, and pointerenter/leave on\n  // the panel.\n  syncComposerBarPeek();\n\n  // IIFE/CDN lazy path only: the parsers were not ready at mount, so any\n  // messages rendered so far (restored history, eager intro/injected messages)\n  // were escaped to plain text. Once the `markdown-parsers.js` chunk resolves,\n  // bust the message cache and re-render so they pick up real markdown. Bumping\n  // `configVersion` + clearing the cache is required because the message\n  // content is unchanged, so the fingerprint cache would otherwise reuse the\n  // stale escaped wrappers. No-op for the ESM build (parsers ready at init).\n  if (!markdownReadyAtInit) {\n    loadMarkdownParsers()\n      .then(() => {\n        if (!session) return;\n        configVersion++;\n        messageCache.clear();\n        renderMessagesWithPlugins(messagesWrapper, session.getMessages(), postprocess);\n      })\n      .catch(() => {\n        /* chunk failed to load (e.g. ad blocker): keep the escaped fallback */\n      });\n  }\n\n  return controller;\n};\n\nexport type AgentWidgetController = Controller;\n","import type {\n  AgentWidgetConfig,\n  AgentWidgetDockConfig,\n  AgentWidgetStateSnapshot,\n} from \"../types\";\nimport { isDockedMountMode, resolveDockConfig } from \"../utils/dock\";\nimport { DEFAULT_OVERLAY_Z_INDEX } from \"../utils/constants\";\n\nexport type WidgetHostLayoutMode = \"direct\" | \"docked\";\n\nexport type WidgetHostLayout = {\n  mode: WidgetHostLayoutMode;\n  host: HTMLElement;\n  shell: HTMLElement | null;\n  syncWidgetState: (state: Pick<AgentWidgetStateSnapshot, \"open\" | \"launcherEnabled\">) => void;\n  updateConfig: (config?: AgentWidgetConfig) => void;\n  destroy: () => void;\n};\n\n/** Parse `dock.width` for push layout math (px or % of shell). Fallback: 420. */\nconst parseDockWidthToPx = (width: string, shellClientWidth: number): number => {\n  const w = width.trim();\n  const px = /^(\\d+(?:\\.\\d+)?)px$/i.exec(w);\n  if (px) return Math.max(0, parseFloat(px[1]));\n  const pct = /^(\\d+(?:\\.\\d+)?)%$/i.exec(w);\n  if (pct) return Math.max(0, (shellClientWidth * parseFloat(pct[1])) / 100);\n  return 420;\n};\n\n/**\n * Viewport-overflow guard. The docked shell sizes itself with `height: 100%`,\n * which only resolves when an ancestor provides a definite height; without one\n * the dock column would grow with the conversation and scroll off the page.\n * Clamping the slot keeps the panel viewport-sized (messages scroll\n * internally) even when the page's height chain is missing. `100vh` is set\n * first as a fallback for engines without `dvh` support: an invalid value\n * leaves the previous one in place.\n */\nconst applyDockSlotMaxHeight = (\n  dockSlot: HTMLElement,\n  maxHeight: string | false\n): void => {\n  if (maxHeight === false) {\n    dockSlot.style.maxHeight = \"\";\n    return;\n  }\n  dockSlot.style.maxHeight = \"100vh\";\n  dockSlot.style.maxHeight = maxHeight;\n};\n\n/**\n * Sticky keeps the resize/emerge dock column pinned to the top of the viewport\n * when the surrounding page is taller than the screen (e.g. a missing height\n * chain or a deliberately scrolling page). With a properly sized shell it\n * behaves exactly like the previous `position: relative`. Not used for push:\n * push gets the max-height cap only, like overlay, since the dock slot is an\n * in-flow `position: relative` column there rather than a viewport-pinned one.\n * (The push track itself is offset with `margin-left`, not a transform, so it\n * no longer establishes a containing block for fixed/sticky descendants.)\n */\nconst applyInFlowDockSlotPosition = (\n  dockSlot: HTMLElement,\n  maxHeight: string | false\n): void => {\n  if (maxHeight === false) {\n    dockSlot.style.position = \"relative\";\n    dockSlot.style.top = \"\";\n  } else {\n    dockSlot.style.position = \"sticky\";\n    dockSlot.style.top = \"0\";\n  }\n};\n\n/**\n * Warns once per docked mount when no ancestor of the dock target provides a\n * definite height, which is the common misconfiguration behind a dock panel\n * that grows past the viewport. Probes with a throwaway `height: 100%` child\n * of the shell's parent; a fixed-height reference probe first checks the\n * environment can measure at all (display: none subtrees and jsdom measure\n * everything as 0, and must not produce a false warning).\n */\nconst warnIfDockHeightChainUnresolved = (\n  shell: HTMLElement,\n  dock: Required<AgentWidgetDockConfig>\n): void => {\n  const parent = shell.parentElement;\n  if (!parent) return;\n  const probe = shell.ownerDocument.createElement(\"div\");\n  probe.style.cssText =\n    \"width:0;height:1px;margin:0;padding:0;border:0;visibility:hidden;\";\n  parent.appendChild(probe);\n  const measurable = probe.offsetHeight > 0;\n  probe.style.height = \"100%\";\n  const resolved = probe.offsetHeight > 0;\n  probe.remove();\n  if (!measurable || resolved) return;\n  console.warn(\n    \"[AgentWidget] Docked mode: no ancestor of the dock target provides a definite height, \" +\n      \"so the dock panel cannot size to your layout.\" +\n      (dock.maxHeight === false\n        ? \" The viewport guard is disabled (dock.maxHeight: false), so the panel will grow with the conversation and overflow the viewport.\"\n        : ` Falling back to clamping the panel to ${dock.maxHeight} (configurable via launcher.dock.maxHeight).`) +\n      \" To size the panel from your layout instead, give the height chain a definite height \" +\n      \"(e.g. `html, body { height: 100% }`) down to the dock target's parent.\"\n  );\n};\n\nconst setDirectHostStyles = (host: HTMLElement, config?: AgentWidgetConfig): void => {\n  const launcherEnabled = config?.launcher?.enabled ?? true;\n  host.className = \"persona-host\";\n  host.style.height = launcherEnabled ? \"\" : \"100%\";\n  host.style.display = launcherEnabled ? \"\" : \"flex\";\n  host.style.flexDirection = launcherEnabled ? \"\" : \"column\";\n  host.style.flex = launcherEnabled ? \"\" : \"1 1 auto\";\n  host.style.minHeight = launcherEnabled ? \"\" : \"0\";\n};\n\nconst clearOverlayDockSlotStyles = (dockSlot: HTMLElement): void => {\n  dockSlot.style.position = \"\";\n  dockSlot.style.top = \"\";\n  dockSlot.style.bottom = \"\";\n  dockSlot.style.left = \"\";\n  dockSlot.style.right = \"\";\n  dockSlot.style.zIndex = \"\";\n  dockSlot.style.transform = \"\";\n  dockSlot.style.pointerEvents = \"\";\n};\n\n/** Clears viewport-escape fullscreen styles so reveal modes can re-apply dock layout. */\nconst clearMobileFullscreenDockSlotStyles = (dockSlot: HTMLElement): void => {\n  dockSlot.style.inset = \"\";\n  dockSlot.style.width = \"\";\n  dockSlot.style.height = \"\";\n  dockSlot.style.maxWidth = \"\";\n  dockSlot.style.maxHeight = \"\";\n  dockSlot.style.minWidth = \"\";\n  clearOverlayDockSlotStyles(dockSlot);\n};\n\nconst clearResizeDockSlotTransition = (dockSlot: HTMLElement): void => {\n  dockSlot.style.transition = \"\";\n};\n\nconst clearPushTrackStyles = (pushTrack: HTMLElement): void => {\n  pushTrack.style.display = \"\";\n  pushTrack.style.flexDirection = \"\";\n  pushTrack.style.flex = \"\";\n  pushTrack.style.minHeight = \"\";\n  pushTrack.style.minWidth = \"\";\n  pushTrack.style.width = \"\";\n  pushTrack.style.height = \"\";\n  pushTrack.style.alignItems = \"\";\n  pushTrack.style.transition = \"\";\n  pushTrack.style.transform = \"\";\n  pushTrack.style.marginLeft = \"\";\n};\n\nconst resetContentSlotFlexSizing = (contentSlot: HTMLElement): void => {\n  contentSlot.style.width = \"\";\n  contentSlot.style.maxWidth = \"\";\n  contentSlot.style.minWidth = \"\";\n  contentSlot.style.flex = \"1 1 auto\";\n};\n\nconst clearEmergeDockStyles = (host: HTMLElement, dockSlot: HTMLElement): void => {\n  host.style.width = \"\";\n  host.style.minWidth = \"\";\n  host.style.maxWidth = \"\";\n  host.style.boxSizing = \"\";\n  dockSlot.style.alignItems = \"\";\n};\n\nconst migrateDockChildren = (\n  shell: HTMLElement,\n  pushTrack: HTMLElement,\n  contentSlot: HTMLElement,\n  dockSlot: HTMLElement,\n  usePush: boolean\n): void => {\n  if (usePush) {\n    if (contentSlot.parentElement !== pushTrack) {\n      shell.replaceChildren();\n      pushTrack.replaceChildren(contentSlot, dockSlot);\n      shell.appendChild(pushTrack);\n    }\n  } else if (contentSlot.parentElement === pushTrack) {\n    pushTrack.replaceChildren();\n    shell.appendChild(contentSlot);\n    shell.appendChild(dockSlot);\n  }\n};\n\nconst orderDockChildren = (\n  shell: HTMLElement,\n  pushTrack: HTMLElement,\n  contentSlot: HTMLElement,\n  dockSlot: HTMLElement,\n  side: \"left\" | \"right\",\n  usePush: boolean\n): void => {\n  const parent = usePush ? pushTrack : shell;\n  if (side === \"left\") {\n    if (parent.firstElementChild !== dockSlot) {\n      parent.replaceChildren(dockSlot, contentSlot);\n    }\n  } else if (parent.lastElementChild !== dockSlot) {\n    parent.replaceChildren(contentSlot, dockSlot);\n  }\n};\n\nconst applyDockStyles = (\n  shell: HTMLElement,\n  pushTrack: HTMLElement,\n  contentSlot: HTMLElement,\n  dockSlot: HTMLElement,\n  host: HTMLElement,\n  config: AgentWidgetConfig | undefined,\n  expanded: boolean\n): void => {\n  const dock = resolveDockConfig(config);\n  const usePush = dock.reveal === \"push\";\n\n  migrateDockChildren(shell, pushTrack, contentSlot, dockSlot, usePush);\n  orderDockChildren(shell, pushTrack, contentSlot, dockSlot, dock.side, usePush);\n\n  shell.dataset.personaHostLayout = \"docked\";\n  shell.dataset.personaDockSide = dock.side;\n  shell.dataset.personaDockOpen = expanded ? \"true\" : \"false\";\n  shell.style.width = \"100%\";\n  shell.style.maxWidth = \"100%\";\n  shell.style.minWidth = \"0\";\n  shell.style.height = \"100%\";\n  shell.style.minHeight = \"0\";\n  shell.style.position = \"relative\";\n\n  contentSlot.style.display = \"flex\";\n  contentSlot.style.flexDirection = \"column\";\n  contentSlot.style.minHeight = \"0\";\n  contentSlot.style.position = \"relative\";\n\n  host.className = \"persona-host\";\n  host.style.height = \"100%\";\n  host.style.minHeight = \"0\";\n  host.style.display = \"flex\";\n  host.style.flexDirection = \"column\";\n  host.style.flex = \"1 1 auto\";\n\n  const ownerWindow = shell.ownerDocument.defaultView;\n  const mobileFullscreenEnabled = config?.launcher?.mobileFullscreen ?? true;\n  const mobileBreakpoint = config?.launcher?.mobileBreakpoint ?? 640;\n  const isMobileViewport =\n    ownerWindow != null ? ownerWindow.innerWidth <= mobileBreakpoint : false;\n  const useMobileFullscreen = mobileFullscreenEnabled && isMobileViewport && expanded;\n\n  if (useMobileFullscreen) {\n    shell.dataset.personaDockMobileFullscreen = \"true\";\n    shell.removeAttribute(\"data-persona-dock-reveal\");\n    clearPushTrackStyles(pushTrack);\n    clearResizeDockSlotTransition(dockSlot);\n    clearMobileFullscreenDockSlotStyles(dockSlot);\n    resetContentSlotFlexSizing(contentSlot);\n    clearEmergeDockStyles(host, dockSlot);\n\n    shell.style.display = \"flex\";\n    shell.style.flexDirection = \"column\";\n    shell.style.alignItems = \"stretch\";\n    shell.style.overflow = \"hidden\";\n\n    contentSlot.style.flex = \"1 1 auto\";\n    contentSlot.style.width = \"100%\";\n    contentSlot.style.minWidth = \"0\";\n\n    dockSlot.style.display = \"flex\";\n    dockSlot.style.flexDirection = \"column\";\n    dockSlot.style.position = \"fixed\";\n    dockSlot.style.inset = \"0\";\n    dockSlot.style.width = \"100%\";\n    dockSlot.style.height = \"100%\";\n    dockSlot.style.maxWidth = \"100%\";\n    dockSlot.style.minWidth = \"0\";\n    dockSlot.style.minHeight = \"0\";\n    dockSlot.style.overflow = \"hidden\";\n    dockSlot.style.zIndex = String(config?.launcher?.zIndex ?? DEFAULT_OVERLAY_Z_INDEX);\n    dockSlot.style.transform = \"none\";\n    dockSlot.style.transition = \"none\";\n    dockSlot.style.pointerEvents = \"auto\";\n    dockSlot.style.flex = \"none\";\n\n    if (usePush) {\n      pushTrack.style.display = \"flex\";\n      pushTrack.style.flexDirection = \"column\";\n      pushTrack.style.width = \"100%\";\n      pushTrack.style.height = \"100%\";\n      pushTrack.style.minHeight = \"0\";\n      pushTrack.style.minWidth = \"0\";\n      pushTrack.style.flex = \"1 1 auto\";\n      pushTrack.style.alignItems = \"stretch\";\n      pushTrack.style.transform = \"none\";\n      // Reset the desktop push offset: this fullscreen path applies styles\n      // inline without going through clearPushTrackStyles, so a stale negative\n      // marginLeft from a prior expanded desktop render would shift the now\n      // width:100% track off-screen on mobile.\n      pushTrack.style.marginLeft = \"0\";\n      pushTrack.style.transition = \"none\";\n      contentSlot.style.flex = \"1 1 auto\";\n      contentSlot.style.width = \"100%\";\n      contentSlot.style.maxWidth = \"100%\";\n      contentSlot.style.minWidth = \"0\";\n    }\n\n    return;\n  }\n\n  shell.removeAttribute(\"data-persona-dock-mobile-fullscreen\");\n  clearMobileFullscreenDockSlotStyles(dockSlot);\n  applyDockSlotMaxHeight(dockSlot, dock.maxHeight);\n\n  if (dock.reveal === \"overlay\") {\n    shell.style.display = \"flex\";\n    shell.style.flexDirection = \"row\";\n    shell.style.alignItems = \"stretch\";\n    shell.style.overflow = \"hidden\";\n    shell.dataset.personaDockReveal = \"overlay\";\n    clearPushTrackStyles(pushTrack);\n    clearResizeDockSlotTransition(dockSlot);\n    resetContentSlotFlexSizing(contentSlot);\n    clearEmergeDockStyles(host, dockSlot);\n\n    const dockTransition = dock.animate ? \"transform 180ms ease\" : \"none\";\n    const translateClosed = dock.side === \"right\" ? \"translateX(100%)\" : \"translateX(-100%)\";\n    const translate = expanded ? \"translateX(0)\" : translateClosed;\n\n    dockSlot.style.display = \"flex\";\n    dockSlot.style.flexDirection = \"column\";\n    dockSlot.style.flex = \"none\";\n    dockSlot.style.position = \"absolute\";\n    dockSlot.style.top = \"0\";\n    dockSlot.style.bottom = \"0\";\n    dockSlot.style.width = dock.width;\n    dockSlot.style.maxWidth = dock.width;\n    dockSlot.style.minWidth = dock.width;\n    dockSlot.style.minHeight = \"0\";\n    dockSlot.style.overflow = \"hidden\";\n    dockSlot.style.transition = dockTransition;\n    dockSlot.style.transform = translate;\n    dockSlot.style.pointerEvents = expanded ? \"auto\" : \"none\";\n    dockSlot.style.zIndex = \"2\";\n    if (dock.side === \"right\") {\n      dockSlot.style.right = \"0\";\n      dockSlot.style.left = \"\";\n    } else {\n      dockSlot.style.left = \"0\";\n      dockSlot.style.right = \"\";\n    }\n  } else if (dock.reveal === \"push\") {\n    // Row flex so the wide push track is laid out on the horizontal axis; column was stretching\n    // the track to the shell width and fighting explicit width, which could confuse overflow.\n    shell.style.display = \"flex\";\n    shell.style.flexDirection = \"row\";\n    shell.style.alignItems = \"stretch\";\n    shell.style.overflow = \"hidden\";\n    shell.dataset.personaDockReveal = \"push\";\n    clearResizeDockSlotTransition(dockSlot);\n    clearOverlayDockSlotStyles(dockSlot);\n    clearEmergeDockStyles(host, dockSlot);\n\n    const panelPx = parseDockWidthToPx(dock.width, shell.clientWidth);\n    const contentPx = Math.max(0, shell.clientWidth);\n    const dockTransition = dock.animate ? \"margin-left 180ms ease\" : \"none\";\n    // Slide the wide track with a negative left margin rather than a CSS\n    // transform. A `transform` (even `translateX(0)`) turns the push track into\n    // the containing block for any `position: fixed` descendant, so host pages\n    // that render viewport-fixed chrome inside the pushed content (e.g. the\n    // dashboard editor's `fixed top-0 right-0` toolbar) resolve `right: 0`\n    // against the track's right edge: `panelPx` past the viewport, off-screen.\n    // `margin-left` produces the identical visual offset (the track is clipped\n    // by the overflow:hidden shell) without establishing a containing block for\n    // fixed OR absolutely-positioned descendants.\n    const marginOffsetPx =\n      dock.side === \"right\"\n        ? expanded\n          ? -panelPx\n          : 0\n        : expanded\n          ? 0\n          : -panelPx;\n\n    pushTrack.style.display = \"flex\";\n    pushTrack.style.flexDirection = \"row\";\n    pushTrack.style.flex = \"0 0 auto\";\n    pushTrack.style.minHeight = \"0\";\n    pushTrack.style.minWidth = \"0\";\n    pushTrack.style.alignItems = \"stretch\";\n    pushTrack.style.height = \"100%\";\n    pushTrack.style.width = `${contentPx + panelPx}px`;\n    pushTrack.style.transition = dockTransition;\n    pushTrack.style.marginLeft = `${marginOffsetPx}px`;\n    // Defensively clear any transform a previous reveal mode may have set:    // leaving one would re-establish the fixed-position containing block.\n    pushTrack.style.transform = \"\";\n\n    contentSlot.style.flex = \"0 0 auto\";\n    contentSlot.style.flexGrow = \"0\";\n    contentSlot.style.flexShrink = \"0\";\n    contentSlot.style.width = `${contentPx}px`;\n    contentSlot.style.maxWidth = `${contentPx}px`;\n    contentSlot.style.minWidth = `${contentPx}px`;\n\n    dockSlot.style.display = \"flex\";\n    dockSlot.style.flexDirection = \"column\";\n    dockSlot.style.flex = \"0 0 auto\";\n    dockSlot.style.flexShrink = \"0\";\n    dockSlot.style.width = dock.width;\n    dockSlot.style.minWidth = dock.width;\n    dockSlot.style.maxWidth = dock.width;\n    dockSlot.style.position = \"relative\";\n    dockSlot.style.top = \"\";\n    dockSlot.style.overflow = \"hidden\";\n    dockSlot.style.transition = \"none\";\n    dockSlot.style.pointerEvents = expanded ? \"auto\" : \"none\";\n  } else {\n    shell.style.display = \"flex\";\n    shell.style.flexDirection = \"row\";\n    shell.style.alignItems = \"stretch\";\n    shell.style.overflow = \"\";\n    clearPushTrackStyles(pushTrack);\n    clearOverlayDockSlotStyles(dockSlot);\n    resetContentSlotFlexSizing(contentSlot);\n    clearEmergeDockStyles(host, dockSlot);\n\n    const isEmerge = dock.reveal === \"emerge\";\n    if (isEmerge) {\n      shell.dataset.personaDockReveal = \"emerge\";\n    } else {\n      shell.removeAttribute(\"data-persona-dock-reveal\");\n    }\n\n    const width = expanded ? dock.width : \"0px\";\n    const dockTransition = dock.animate\n      ? \"width 180ms ease, min-width 180ms ease, max-width 180ms ease, flex-basis 180ms ease\"\n      : \"none\";\n    const collapsedClosed = !expanded;\n\n    dockSlot.style.display = \"flex\";\n    dockSlot.style.flexDirection = \"column\";\n    dockSlot.style.flex = `0 0 ${width}`;\n    dockSlot.style.width = width;\n    dockSlot.style.maxWidth = width;\n    dockSlot.style.minWidth = width;\n    dockSlot.style.minHeight = \"0\";\n    applyInFlowDockSlotPosition(dockSlot, dock.maxHeight);\n    dockSlot.style.overflow =\n      isEmerge ? \"hidden\" : collapsedClosed ? \"hidden\" : \"visible\";\n    dockSlot.style.transition = dockTransition;\n\n    if (isEmerge) {\n      dockSlot.style.alignItems = dock.side === \"right\" ? \"flex-start\" : \"flex-end\";\n      host.style.width = dock.width;\n      host.style.minWidth = dock.width;\n      host.style.maxWidth = dock.width;\n      host.style.boxSizing = \"border-box\";\n    }\n  }\n};\n\nconst createDirectLayout = (target: HTMLElement, config?: AgentWidgetConfig): WidgetHostLayout => {\n  const host = target.ownerDocument.createElement(\"div\");\n  setDirectHostStyles(host, config);\n  target.appendChild(host);\n\n  return {\n    mode: \"direct\",\n    host,\n    shell: null,\n    syncWidgetState: () => {},\n    updateConfig(nextConfig?: AgentWidgetConfig) {\n      setDirectHostStyles(host, nextConfig);\n    },\n    destroy() {\n      host.remove();\n    },\n  };\n};\n\nconst createDockedLayout = (target: HTMLElement, config?: AgentWidgetConfig): WidgetHostLayout => {\n  const { ownerDocument } = target;\n  const originalParent = target.parentElement;\n\n  if (!originalParent) {\n    throw new Error(\"Docked widget target must be attached to the DOM\");\n  }\n\n  const tagName = target.tagName.toUpperCase();\n  if (tagName === \"BODY\" || tagName === \"HTML\") {\n    throw new Error('Docked widget target must be a concrete container element, not \"body\" or \"html\"');\n  }\n\n  const originalNextSibling = target.nextSibling;\n  const shell = ownerDocument.createElement(\"div\");\n  const pushTrack = ownerDocument.createElement(\"div\");\n  const contentSlot = ownerDocument.createElement(\"div\");\n  const dockSlot = ownerDocument.createElement(\"aside\");\n  const host = ownerDocument.createElement(\"div\");\n  let expanded = (config?.launcher?.enabled ?? true) ? (config?.launcher?.autoExpand ?? false) : true;\n\n  pushTrack.dataset.personaDockRole = \"push-track\";\n  contentSlot.dataset.personaDockRole = \"content\";\n  dockSlot.dataset.personaDockRole = \"panel\";\n  host.dataset.personaDockRole = \"host\";\n\n  dockSlot.appendChild(host);\n  originalParent.insertBefore(shell, target);\n  contentSlot.appendChild(target);\n\n  let resizeObserver: ResizeObserver | null = null;\n\n  const disconnectResizeObserver = (): void => {\n    resizeObserver?.disconnect();\n    resizeObserver = null;\n  };\n\n  const syncPushResizeObserver = (): void => {\n    disconnectResizeObserver();\n    if (resolveDockConfig(config).reveal !== \"push\") return;\n    if (typeof ResizeObserver === \"undefined\") return;\n    resizeObserver = new ResizeObserver(() => {\n      applyDockStyles(shell, pushTrack, contentSlot, dockSlot, host, config, expanded);\n    });\n    resizeObserver.observe(shell);\n  };\n\n  let heightChainChecked = false;\n\n  const layout = (): void => {\n    applyDockStyles(shell, pushTrack, contentSlot, dockSlot, host, config, expanded);\n    syncPushResizeObserver();\n    // Check the height chain once, the first time the panel is actually shown\n    // in a layout that depends on it (mobile fullscreen is fixed-position and\n    // doesn't: keep checking until a dependent layout comes around).\n    if (\n      expanded &&\n      !heightChainChecked &&\n      shell.dataset.personaDockMobileFullscreen !== \"true\"\n    ) {\n      heightChainChecked = true;\n      warnIfDockHeightChainUnresolved(shell, resolveDockConfig(config));\n    }\n  };\n\n  const ownerWindow = shell.ownerDocument.defaultView;\n  const onViewportResize = (): void => {\n    layout();\n  };\n  ownerWindow?.addEventListener(\"resize\", onViewportResize);\n\n  if (resolveDockConfig(config).reveal === \"push\") {\n    pushTrack.appendChild(contentSlot);\n    pushTrack.appendChild(dockSlot);\n    shell.appendChild(pushTrack);\n  } else {\n    shell.appendChild(contentSlot);\n    shell.appendChild(dockSlot);\n  }\n\n  layout();\n\n  return {\n    mode: \"docked\",\n    host,\n    shell,\n    syncWidgetState(state) {\n      const nextExpanded = state.launcherEnabled ? state.open : true;\n      if (expanded === nextExpanded) return;\n      expanded = nextExpanded;\n      layout();\n    },\n    updateConfig(nextConfig?: AgentWidgetConfig) {\n      config = nextConfig;\n      if ((config?.launcher?.enabled ?? true) === false) {\n        expanded = true;\n      }\n      layout();\n    },\n    destroy() {\n      ownerWindow?.removeEventListener(\"resize\", onViewportResize);\n      disconnectResizeObserver();\n      if (originalParent.isConnected) {\n        if (originalNextSibling && originalNextSibling.parentNode === originalParent) {\n          originalParent.insertBefore(target, originalNextSibling);\n        } else {\n          originalParent.appendChild(target);\n        }\n      }\n      shell.remove();\n    },\n  };\n};\n\nexport const createWidgetHostLayout = (\n  target: HTMLElement,\n  config?: AgentWidgetConfig\n): WidgetHostLayout => {\n  if (isDockedMountMode(config)) {\n    return createDockedLayout(target, config);\n  }\n  return createDirectLayout(target, config);\n};\n","import { createAgentExperience, AgentWidgetController } from \"../ui\";\nimport { AgentWidgetConfig as _AgentWidgetConfig, AgentWidgetInitOptions, AgentWidgetEvent as _AgentWidgetEvent } from \"../types\";\nimport { isComposerBarMountMode, isDockedMountMode } from \"../utils/dock\";\nimport { createWidgetHostLayout } from \"./host-layout\";\n\nconst ensureTarget = (target: string | HTMLElement): HTMLElement => {\n  if (typeof window === \"undefined\" || typeof document === \"undefined\") {\n    throw new Error(\"Chat widget can only be mounted in a browser environment\");\n  }\n\n  if (typeof target === \"string\") {\n    const element = document.querySelector<HTMLElement>(target);\n    if (!element) {\n      throw new Error(`Chat widget target \"${target}\" was not found`);\n    }\n    return element;\n  }\n\n  return target;\n};\n\nconst widgetCssHref = (): string | null => {\n  try {\n    // This works in ESM builds but not in IIFE builds\n    if (typeof import.meta !== \"undefined\" && import.meta.url) {\n      return new URL(\"../widget.css\", import.meta.url).href;\n    }\n  } catch {\n    // Fallback for IIFE builds where CSS should be loaded separately\n  }\n  return null;\n};\n\nconst mountStyles = (root: ShadowRoot | HTMLElement, ownerDocument: Document) => {\n  const href = widgetCssHref();\n\n  const adoptExistingStylesheet = () => {\n    if (!(root instanceof ShadowRoot)) {\n      return;\n    }\n\n    if (root.querySelector('link[data-persona]')) {\n      return;\n    }\n\n    const globalLink = ownerDocument.head.querySelector<HTMLLinkElement>(\n      'link[data-persona]'\n    );\n    if (!globalLink) {\n      return;\n    }\n\n    const clonedLink = globalLink.cloneNode(true) as HTMLLinkElement;\n    root.insertBefore(clonedLink, root.firstChild);\n  };\n\n  if (root instanceof ShadowRoot) {\n    // For shadow DOM, we need to load CSS into the shadow root\n    if (href) {\n      const link = ownerDocument.createElement(\"link\");\n      link.rel = \"stylesheet\";\n      link.href = href;\n      link.setAttribute(\"data-persona\", \"true\");\n      root.insertBefore(link, root.firstChild);\n    } else {\n      adoptExistingStylesheet();\n    }\n    // If href is null (IIFE build), CSS should already be loaded globally\n  } else {\n    // For non-shadow DOM, check if CSS is already loaded\n    const existing = ownerDocument.head.querySelector<HTMLLinkElement>(\n      \"link[data-persona]\"\n    );\n    if (!existing) {\n      if (href) {\n        // ESM build - load CSS dynamically\n        const link = ownerDocument.createElement(\"link\");\n        link.rel = \"stylesheet\";\n        link.href = href;\n        link.setAttribute(\"data-persona\", \"true\");\n        ownerDocument.head.appendChild(link);\n      }\n      // IIFE build - CSS should be loaded via <link> tag before script\n      // If not found, we'll assume it's loaded globally or warn in dev\n    }\n  }\n};\n\nexport type AgentWidgetInitHandle = AgentWidgetController & { host: HTMLElement };\n\nexport const initAgentWidget = (\n  options: AgentWidgetInitOptions\n): AgentWidgetInitHandle => {\n  const target = ensureTarget(options.target);\n  const useShadow = options.useShadowDom === true;\n  const ownerDocument = target.ownerDocument;\n  let config = options.config;\n  let hostLayout = createWidgetHostLayout(target, config);\n  let controller: AgentWidgetController;\n  let stateUnsubs: Array<() => void> = [];\n\n  const createMount = (host: HTMLElement, nextConfig?: _AgentWidgetConfig): HTMLElement => {\n    const launcherEnabled = nextConfig?.launcher?.enabled ?? true;\n    const shouldFillHost = !launcherEnabled || isDockedMountMode(nextConfig);\n    const mount = ownerDocument.createElement(\"div\");\n    mount.setAttribute(\"data-persona-root\", \"true\");\n\n    if (shouldFillHost) {\n      mount.style.height = \"100%\";\n      mount.style.display = \"flex\";\n      mount.style.flexDirection = \"column\";\n      mount.style.flex = \"1\";\n      mount.style.minHeight = \"0\";\n    }\n\n    if (useShadow) {\n      const shadowRoot = host.attachShadow({ mode: \"open\" });\n      shadowRoot.appendChild(mount);\n      mountStyles(shadowRoot, ownerDocument);\n    } else {\n      host.appendChild(mount);\n      mountStyles(host, ownerDocument);\n    }\n\n    if (target.id) {\n      mount.setAttribute(\"data-persona-instance\", target.id);\n    }\n\n    return mount;\n  };\n\n  const syncHostState = () => {\n    hostLayout.syncWidgetState(controller.getState());\n  };\n\n  const bindHostState = () => {\n    stateUnsubs.forEach((unsubscribe) => unsubscribe());\n    stateUnsubs = [\n      controller.on(\"widget:opened\", syncHostState),\n      controller.on(\"widget:closed\", syncHostState),\n    ];\n    syncHostState();\n  };\n\n  const mountController = () => {\n    const mount = createMount(hostLayout.host, config);\n    controller = createAgentExperience(mount, config, {\n      debugTools: options.debugTools\n    });\n    bindHostState();\n  };\n\n  const destroyCurrentController = () => {\n    stateUnsubs.forEach((unsubscribe) => unsubscribe());\n    stateUnsubs = [];\n    controller.destroy();\n  };\n\n  mountController();\n  // Fired when the controller is mounted and its API is callable.\n  options.onChatReady?.();\n\n  const rebuildLayout = (nextConfig?: _AgentWidgetConfig) => {\n    destroyCurrentController();\n    hostLayout.destroy();\n    hostLayout = createWidgetHostLayout(target, nextConfig);\n    config = nextConfig;\n    mountController();\n  };\n\n  const handleBase = {\n    update(nextConfig: _AgentWidgetConfig) {\n      const mergedConfig = {\n        ...config,\n        ...nextConfig,\n        launcher: {\n          ...(config?.launcher ?? {}),\n          ...(nextConfig?.launcher ?? {}),\n          dock: {\n            ...(config?.launcher?.dock ?? {}),\n            ...(nextConfig?.launcher?.dock ?? {}),\n          },\n        },\n      } as _AgentWidgetConfig;\n      const previousDocked = isDockedMountMode(config);\n      const nextDocked = isDockedMountMode(mergedConfig);\n      const previousComposerBar = isComposerBarMountMode(config);\n      const nextComposerBar = isComposerBarMountMode(mergedConfig);\n\n      if (previousDocked !== nextDocked || previousComposerBar !== nextComposerBar) {\n        rebuildLayout(mergedConfig);\n        return;\n      }\n\n      config = mergedConfig;\n      hostLayout.updateConfig(config);\n      controller.update(nextConfig);\n      syncHostState();\n    },\n    destroy() {\n      destroyCurrentController();\n      hostLayout.destroy();\n      if (options.windowKey && typeof window !== \"undefined\") {\n        delete (window as any)[options.windowKey];\n      }\n    }\n  };\n\n  const handle = new Proxy(handleBase as AgentWidgetInitHandle, {\n    get(targetObject, prop, receiver) {\n      if (prop === \"host\") {\n        return hostLayout.host;\n      }\n\n      if (prop in targetObject) {\n        return Reflect.get(targetObject, prop, receiver);\n      }\n\n      const value = (controller as Record<PropertyKey, unknown>)[prop];\n      return typeof value === \"function\" ? (value as Function).bind(controller) : value;\n    }\n  }) as AgentWidgetInitHandle;\n\n  if (options.windowKey && typeof window !== 'undefined') {\n    (window as any)[options.windowKey] = handle;\n  }\n\n  return handle;\n};\n","/**\n * Enriched DOM context collection for providing richer page information to AI.\n *\n * Captures interactive elements, stable CSS selectors, ARIA roles, data attributes,\n * and visibility state: giving the LLM much better context than basic className/innerText.\n *\n * ## Modes\n *\n * - **structured** (default): collects candidates, scores them with optional {@link ParseRule}\n *   hooks, then applies `maxElements`. Rich containers (e.g. product cards) can surface\n *   before unrelated static noise.\n * - **simple**: legacy behavior: cap during traversal, interactive-first ordering, no rule\n *   scoring or {@link EnrichedPageElement.formattedSummary}.\n */\n\nexport interface EnrichedPageElement {\n  /** Stable CSS selector the LLM can use directly */\n  selector: string;\n  /** Lowercase tag name */\n  tagName: string;\n  /** Visible text content, trimmed */\n  text: string;\n  /** ARIA role or null */\n  role: string | null;\n  /** Interactivity classification */\n  interactivity: \"clickable\" | \"input\" | \"navigable\" | \"static\";\n  /** Relevant attributes: id, data-*, href, aria-label, type, value, name */\n  attributes: Record<string, string>;\n  /**\n   * When set (structured mode + matching rule), {@link formatEnrichedContext} prefers this\n   * markdown-like line instead of raw `text`.\n   */\n  formattedSummary?: string;\n}\n\n/** How DOM context is collected and formatted. */\nexport type DomContextMode = \"simple\" | \"structured\";\n\n/**\n * Options that control collection limits, visibility, and mode.\n * Prefer nesting these under {@link DomContextOptions.options}; top-level fields remain\n * supported for backward compatibility.\n */\nexport interface ParseOptionsConfig {\n  /**\n   * `structured` (default): score candidates with rules, then apply `maxElements`.\n   * `simple`: legacy traversal cap and ordering only: rules are ignored (with a warning\n   * if `rules` was passed on {@link DomContextOptions}).\n   */\n  mode?: DomContextMode;\n  /** Maximum number of elements to return. Default: 80 */\n  maxElements?: number;\n  /** CSS selector for elements to exclude (e.g. the widget host). Default: '.persona-host' */\n  excludeSelector?: string;\n  /** Maximum text length per element. Default: 200 */\n  maxTextLength?: number;\n  /** Only include visible elements. Default: true */\n  visibleOnly?: boolean;\n  /** Root element to walk. Default: document.body */\n  root?: HTMLElement;\n  /**\n   * Maximum candidates gathered before scoring (structured mode only).\n   * Default: `max(500, maxElements * 10)`.\n   */\n  maxCandidates?: number;\n}\n\nexport interface RuleScoringContext {\n  doc: Document;\n  maxTextLength: number;\n}\n\n/**\n * Extensible rule for structured DOM context: scoring, descendant suppression, and\n * optional formatted output.\n */\nexport interface ParseRule {\n  /** Stable id for debugging and tests */\n  id: string;\n  /**\n   * Score bonus when this rule applies to the element (0 when it does not).\n   * Higher scores are kept first when applying `maxElements`.\n   */\n  scoreElement(\n    el: HTMLElement,\n    enriched: EnrichedPageElement,\n    ctx: RuleScoringContext\n  ): number;\n  /**\n   * When `owner` is kept in the final set and matched this rule for formatting,\n   * return true to drop `descendant` (redundant price text, CTAs summarized on the card, etc.).\n   */\n  shouldSuppressDescendant?(\n    owner: HTMLElement,\n    descendant: HTMLElement,\n    descendantEnriched: EnrichedPageElement\n  ): boolean;\n  /**\n   * Markdown-like summary for the LLM. Only used when `scoreElement` &gt; 0 for this rule.\n   */\n  formatSummary?(\n    el: HTMLElement,\n    enriched: EnrichedPageElement,\n    ctx: RuleScoringContext\n  ): string | null;\n}\n\nexport interface DomContextOptions {\n  /** Nested parse options (mode, limits, root). Merged with legacy top-level fields. */\n  options?: ParseOptionsConfig;\n  /** Custom rules for structured mode. Default: {@link defaultParseRules} */\n  rules?: ParseRule[];\n  /** @inheritdoc ParseOptionsConfig.maxElements */\n  maxElements?: number;\n  /** @inheritdoc ParseOptionsConfig.excludeSelector */\n  excludeSelector?: string;\n  /** @inheritdoc ParseOptionsConfig.maxTextLength */\n  maxTextLength?: number;\n  /** @inheritdoc ParseOptionsConfig.visibleOnly */\n  visibleOnly?: boolean;\n  /** @inheritdoc ParseOptionsConfig.root */\n  root?: HTMLElement;\n}\n\nexport interface FormatEnrichedContextOptions {\n  /** When `simple`, ignore {@link EnrichedPageElement.formattedSummary}. Default: structured */\n  mode?: DomContextMode;\n}\n\nconst SKIP_TAGS = new Set([\n  \"script\",\n  \"style\",\n  \"noscript\",\n  \"svg\",\n  \"path\",\n  \"meta\",\n  \"link\",\n  \"br\",\n  \"hr\",\n]);\n\nconst INTERACTIVE_TAGS = new Set([\n  \"button\",\n  \"a\",\n  \"input\",\n  \"select\",\n  \"textarea\",\n  \"details\",\n  \"summary\",\n]);\n\nconst INTERACTIVE_ROLES = new Set([\n  \"button\",\n  \"link\",\n  \"menuitem\",\n  \"tab\",\n  \"option\",\n  \"switch\",\n  \"checkbox\",\n  \"radio\",\n  \"combobox\",\n  \"listbox\",\n  \"slider\",\n  \"spinbutton\",\n  \"textbox\",\n]);\n\n/** Class / id / data-* value hints for card-like containers */\nconst CARD_HINT_RE = /\\b(product|card|item|listing|result)\\b/i;\n\n/** Currency-like text in subtree */\nconst CURRENCY_RE =\n  /\\$[\\d,]+(?:\\.\\d{2})?|€[\\d,]+(?:\\.\\d{2})?|£[\\d,]+(?:\\.\\d{2})?|USD\\s*[\\d,]+(?:\\.\\d{2})?/i;\n\nconst BASE_SCORE_INTERACTIVE = 3000;\nconst BASE_SCORE_STATIC = 100;\n\nfunction hasCardHint(el: HTMLElement): boolean {\n  const cls = typeof el.className === \"string\" ? el.className : \"\";\n  if (CARD_HINT_RE.test(cls)) return true;\n  if (el.id && CARD_HINT_RE.test(el.id)) return true;\n  for (let i = 0; i < el.attributes.length; i++) {\n    const a = el.attributes[i];\n    if (a.name.startsWith(\"data-\") && CARD_HINT_RE.test(a.value)) return true;\n  }\n  return false;\n}\n\nfunction subtreeHasCurrency(el: HTMLElement): boolean {\n  return CURRENCY_RE.test((el.textContent ?? \"\").trim());\n}\n\nfunction subtreeHasNonTrivialLink(el: HTMLElement): boolean {\n  const anchors = el.querySelectorAll(\"a[href]\");\n  for (let i = 0; i < anchors.length; i++) {\n    const href = (anchors[i] as HTMLAnchorElement).getAttribute(\"href\") ?? \"\";\n    if (href && href !== \"#\" && !href.toLowerCase().startsWith(\"javascript:\"))\n      return true;\n  }\n  return false;\n}\n\nfunction subtreeHasButtonLike(el: HTMLElement): boolean {\n  return !!el.querySelector(\n    'button, [role=\"button\"], input[type=\"submit\"], input[type=\"button\"]'\n  );\n}\n\nfunction extractFirstPrice(text: string): string | null {\n  const m = text.match(CURRENCY_RE);\n  return m ? m[0] : null;\n}\n\nfunction extractTitleAndHref(el: HTMLElement): { title: string; href: string | null } {\n  const link =\n    el.querySelector(\n      \".product-title a, h1 a, h2 a, h3 a, h4 a, .title a, a[href]\"\n    ) ?? el.querySelector(\"a[href]\");\n  if (link && link.textContent?.trim()) {\n    const href = (link as HTMLAnchorElement).getAttribute(\"href\");\n    return {\n      title: link.textContent.trim(),\n      href: href && href !== \"#\" ? href : null,\n    };\n  }\n  const heading = el.querySelector(\"h1, h2, h3, h4, h5, h6\");\n  if (heading?.textContent?.trim()) {\n    return { title: heading.textContent.trim(), href: null };\n  }\n  return { title: \"\", href: null };\n}\n\nfunction extractCtaLabels(el: HTMLElement): string[] {\n  const labels: string[] = [];\n  const push = (s: string) => {\n    const t = s.trim();\n    if (t && !labels.includes(t)) labels.push(t);\n  };\n  el.querySelectorAll(\"button\").forEach((b) => push(b.textContent ?? \"\"));\n  el.querySelectorAll('[role=\"button\"]').forEach((b) => push(b.textContent ?? \"\"));\n  el.querySelectorAll('input[type=\"submit\"], input[type=\"button\"]').forEach((inp) => {\n    push((inp as HTMLInputElement).value ?? \"\");\n  });\n  return labels.slice(0, 6);\n}\n\nexport const COMMERCE_CARD_RULE_ID = \"commerce-card\";\nexport const RESULT_CARD_RULE_ID = \"result-card\";\n\nfunction commerceCardScore(el: HTMLElement): number {\n  if (!hasCardHint(el)) return 0;\n  if (!subtreeHasCurrency(el)) return 0;\n  if (!subtreeHasNonTrivialLink(el) && !subtreeHasButtonLike(el)) return 0;\n  return 5200;\n}\n\nfunction resultCardScore(el: HTMLElement): number {\n  if (!hasCardHint(el)) return 0;\n  if (subtreeHasCurrency(el)) return 0;\n  if (!subtreeHasNonTrivialLink(el)) return 0;\n  const text = (el.textContent ?? \"\").trim();\n  if (text.length < 20) return 0;\n  const hasTitle =\n    !!el.querySelector(\"h1, h2, h3, h4, h5, h6, .title\") ||\n    !!el.querySelector(\".snippet, .description, p\");\n  if (!hasTitle) return 0;\n  return 2800;\n}\n\n/** Default structured rules: commerce-style cards and generic search/result rows. */\nexport const defaultParseRules: ParseRule[] = [\n  {\n    id: COMMERCE_CARD_RULE_ID,\n    scoreElement(el) {\n      return commerceCardScore(el);\n    },\n    shouldSuppressDescendant(owner, descendant, enriched) {\n      if (descendant === owner || !owner.contains(descendant)) return false;\n      if (enriched.interactivity === \"static\") {\n        const t = enriched.text.trim();\n        if (t.length === 0) return true;\n        if (CURRENCY_RE.test(t) && t.length < 32) return true;\n        return false;\n      }\n      return true;\n    },\n    formatSummary(el, enriched) {\n      if (commerceCardScore(el) === 0) return null;\n      const { title, href } = extractTitleAndHref(el);\n      const price =\n        extractFirstPrice((el.textContent ?? \"\").trim()) ??\n        extractFirstPrice(enriched.text) ??\n        \"\";\n      const ctas = extractCtaLabels(el);\n      const head =\n        href && title\n          ? `[${title}](${href})${price ? `: ${price}` : \"\"}`\n          : title\n            ? `${title}${price ? `: ${price}` : \"\"}`\n            : price || enriched.text.trim().slice(0, 120);\n      const lines = [\n        head,\n        `selector: ${enriched.selector}`,\n        ctas.length ? `actions: ${ctas.join(\", \")}` : \"\",\n      ].filter(Boolean);\n      return lines.join(\"\\n\");\n    },\n  },\n  {\n    id: RESULT_CARD_RULE_ID,\n    scoreElement(el) {\n      return resultCardScore(el);\n    },\n    formatSummary(el, enriched) {\n      if (resultCardScore(el) === 0) return null;\n      const { title, href } = extractTitleAndHref(el);\n      const head =\n        href && title\n          ? `[${title}](${href})`\n          : title || enriched.text.trim().slice(0, 120);\n      const lines = [head, `selector: ${enriched.selector}`].filter(Boolean);\n      return lines.join(\"\\n\");\n    },\n  },\n];\n\ninterface ResolvedDomContextConfig {\n  mode: DomContextMode;\n  maxElements: number;\n  maxCandidates: number;\n  excludeSelector: string;\n  maxTextLength: number;\n  visibleOnly: boolean;\n  root: HTMLElement | undefined;\n  rules: ParseRule[];\n}\n\nfunction warnSimpleWithRules(): void {\n  if (typeof console !== \"undefined\" && typeof console.warn === \"function\") {\n    console.warn(\n      \"[persona] collectEnrichedPageContext: options.mode is \\\"simple\\\" but `rules` were provided; rules are ignored.\"\n    );\n  }\n}\n\nfunction resolveDomContextConfig(options: DomContextOptions): ResolvedDomContextConfig {\n  const nested = options.options ?? {};\n  const maxElements =\n    nested.maxElements ?? options.maxElements ?? 80;\n  const excludeSelector =\n    nested.excludeSelector ?? options.excludeSelector ?? \".persona-host\";\n  const maxTextLength =\n    nested.maxTextLength ?? options.maxTextLength ?? 200;\n  const visibleOnly =\n    nested.visibleOnly ?? options.visibleOnly ?? true;\n  const root = nested.root ?? options.root;\n  const mode: DomContextMode = nested.mode ?? \"structured\";\n  const maxCandidates =\n    nested.maxCandidates ?? Math.max(500, maxElements * 10);\n\n  let rules = options.rules ?? defaultParseRules;\n  if (mode === \"simple\" && options.rules && options.rules.length > 0) {\n    warnSimpleWithRules();\n    rules = [];\n  } else if (mode === \"simple\") {\n    rules = [];\n  }\n\n  return {\n    mode,\n    maxElements,\n    maxCandidates,\n    excludeSelector,\n    maxTextLength,\n    visibleOnly,\n    root,\n    rules,\n  };\n}\n\n/**\n * Escape a string for use in CSS selectors. Falls back to simple escaping\n * when CSS.escape is not available (e.g. in jsdom).\n */\nfunction cssEscape(str: string): string {\n  if (typeof CSS !== \"undefined\" && typeof CSS.escape === \"function\") {\n    return CSS.escape(str);\n  }\n  return str.replace(/([^\\w-])/g, \"\\\\$1\");\n}\n\nconst DATA_ATTR_PRIORITY = [\n  \"data-testid\",\n  \"data-product\",\n  \"data-action\",\n  \"data-id\",\n  \"data-name\",\n  \"data-type\",\n];\n\n/**\n * Classify an element's interactivity type.\n */\nfunction classifyInteractivity(\n  el: HTMLElement\n): EnrichedPageElement[\"interactivity\"] {\n  const tag = el.tagName.toLowerCase();\n  const role = el.getAttribute(\"role\");\n\n  if (tag === \"a\" && el.hasAttribute(\"href\")) return \"navigable\";\n  if (tag === \"input\" || tag === \"select\" || tag === \"textarea\") return \"input\";\n  if (\n    role === \"textbox\" ||\n    role === \"combobox\" ||\n    role === \"listbox\" ||\n    role === \"spinbutton\"\n  )\n    return \"input\";\n  if (tag === \"button\" || role === \"button\") return \"clickable\";\n  if (\n    INTERACTIVE_TAGS.has(tag) ||\n    (role && INTERACTIVE_ROLES.has(role)) ||\n    el.hasAttribute(\"tabindex\") ||\n    el.hasAttribute(\"onclick\") ||\n    el.getAttribute(\"contenteditable\") === \"true\"\n  )\n    return \"clickable\";\n\n  return \"static\";\n}\n\n/**\n * Check if an element is visible.\n * Uses a defensive approach: only marks as invisible when we have positive evidence\n * of hidden state (display:none, visibility:hidden, hidden attribute).\n * offsetParent is unreliable in non-layout environments (e.g. jsdom).\n */\nfunction isElementVisible(el: HTMLElement): boolean {\n  if (el.hidden) return false;\n\n  try {\n    const style = getComputedStyle(el);\n    if (style.display === \"none\") return false;\n    if (style.visibility === \"hidden\") return false;\n  } catch {\n    // getComputedStyle can fail in some environments: assume visible\n  }\n\n  if (el.style.display === \"none\") return false;\n  if (el.style.visibility === \"hidden\") return false;\n\n  return true;\n}\n\n/**\n * Collect relevant attributes from an element.\n */\nfunction collectAttributes(el: HTMLElement): Record<string, string> {\n  const attrs: Record<string, string> = {};\n\n  const id = el.id;\n  if (id) attrs[\"id\"] = id;\n\n  const href = el.getAttribute(\"href\");\n  if (href) attrs[\"href\"] = href;\n\n  const ariaLabel = el.getAttribute(\"aria-label\");\n  if (ariaLabel) attrs[\"aria-label\"] = ariaLabel;\n\n  const type = el.getAttribute(\"type\");\n  if (type) attrs[\"type\"] = type;\n\n  const value = el.getAttribute(\"value\");\n  if (value) attrs[\"value\"] = value;\n\n  const name = el.getAttribute(\"name\");\n  if (name) attrs[\"name\"] = name;\n\n  const role = el.getAttribute(\"role\");\n  if (role) attrs[\"role\"] = role;\n\n  for (let i = 0; i < el.attributes.length; i++) {\n    const attr = el.attributes[i];\n    if (attr.name.startsWith(\"data-\")) {\n      attrs[attr.name] = attr.value;\n    }\n  }\n\n  return attrs;\n}\n\n/**\n * Generate a stable, unique CSS selector for an element.\n * Priority: #id → [data-testid]/[data-product] → tag.classes with :nth-of-type()\n */\nexport function generateStableSelector(el: HTMLElement): string {\n  const tag = el.tagName.toLowerCase();\n\n  if (el.id) {\n    const sel = `#${cssEscape(el.id)}`;\n    try {\n      if (el.ownerDocument.querySelectorAll(sel).length === 1) return sel;\n    } catch {\n      // invalid selector, fall through\n    }\n  }\n\n  for (const attr of DATA_ATTR_PRIORITY) {\n    const val = el.getAttribute(attr);\n    if (val) {\n      const sel = `${tag}[${attr}=\"${cssEscape(val)}\"]`;\n      try {\n        if (el.ownerDocument.querySelectorAll(sel).length === 1) return sel;\n      } catch {\n        // invalid selector, fall through\n      }\n    }\n  }\n\n  const classes = Array.from(el.classList)\n    .filter((c) => c && !c.startsWith(\"persona-\"))\n    .slice(0, 3);\n\n  if (classes.length > 0) {\n    const classSel = `${tag}.${classes.map((c) => cssEscape(c)).join(\".\")}`;\n    try {\n      if (el.ownerDocument.querySelectorAll(classSel).length === 1) return classSel;\n    } catch {\n      // fall through\n    }\n\n    const parent = el.parentElement;\n    if (parent) {\n      const siblings = Array.from(parent.querySelectorAll(`:scope > ${tag}`));\n      const index = siblings.indexOf(el);\n      if (index >= 0) {\n        const nthSel = `${classSel}:nth-of-type(${index + 1})`;\n        try {\n          if (el.ownerDocument.querySelectorAll(nthSel).length === 1) return nthSel;\n        } catch {\n          // fall through\n        }\n      }\n    }\n  }\n\n  const parent = el.parentElement;\n  if (parent) {\n    const siblings = Array.from(parent.querySelectorAll(`:scope > ${tag}`));\n    const index = siblings.indexOf(el);\n    if (index >= 0) {\n      return `${tag}:nth-of-type(${index + 1})`;\n    }\n  }\n\n  return tag;\n}\n\nfunction baseInteractivityScore(\n  interactivity: EnrichedPageElement[\"interactivity\"]\n): number {\n  return interactivity === \"static\" ? BASE_SCORE_STATIC : BASE_SCORE_INTERACTIVE;\n}\n\ninterface ScoredCandidate {\n  el: HTMLElement;\n  domIndex: number;\n  enriched: EnrichedPageElement;\n  score: number;\n  formattingRule: ParseRule | null;\n}\n\nfunction buildEnriched(\n  el: HTMLElement,\n  maxTextLength: number\n): EnrichedPageElement {\n  const tag = el.tagName.toLowerCase();\n  const text = (el.textContent ?? \"\").trim().substring(0, maxTextLength);\n  return {\n    selector: generateStableSelector(el),\n    tagName: tag,\n    text,\n    role: el.getAttribute(\"role\"),\n    interactivity: classifyInteractivity(el),\n    attributes: collectAttributes(el),\n  };\n}\n\nfunction scoreCandidate(\n  el: HTMLElement,\n  enriched: EnrichedPageElement,\n  rules: ParseRule[],\n  ctx: RuleScoringContext\n): { score: number; formattingRule: ParseRule | null } {\n  let score = baseInteractivityScore(enriched.interactivity);\n  let formattingRule: ParseRule | null = null;\n  for (const rule of rules) {\n    const bonus = rule.scoreElement(el, enriched, ctx);\n    if (bonus > 0) {\n      score += bonus;\n      if (rule.formatSummary && !formattingRule) formattingRule = rule;\n    }\n  }\n  return { score, formattingRule };\n}\n\nfunction shouldSuppress(\n  kept: ScoredCandidate[],\n  cand: ScoredCandidate\n): boolean {\n  for (const k of kept) {\n    if (cand.el === k.el) continue;\n    if (!k.formattingRule?.shouldSuppressDescendant) continue;\n    if (!k.el.contains(cand.el)) continue;\n    if (\n      k.formattingRule.shouldSuppressDescendant(\n        k.el,\n        cand.el,\n        cand.enriched\n      )\n    )\n      return true;\n  }\n  return false;\n}\n\nfunction collectStructured(\n  cfg: ResolvedDomContextConfig,\n  rootEl: HTMLElement\n): EnrichedPageElement[] {\n  const ctx: RuleScoringContext = {\n    doc: rootEl.ownerDocument,\n    maxTextLength: cfg.maxTextLength,\n  };\n\n  const seenSelectors = new Set<string>();\n  const raw: ScoredCandidate[] = [];\n  let domIndex = 0;\n\n  const walker = document.createTreeWalker(rootEl, NodeFilter.SHOW_ELEMENT, null);\n  let node: Node | null = walker.currentNode;\n\n  while (node && raw.length < cfg.maxCandidates) {\n    if (node.nodeType === Node.ELEMENT_NODE) {\n      const el = node as HTMLElement;\n      const tag = el.tagName.toLowerCase();\n\n      if (SKIP_TAGS.has(tag)) {\n        node = walker.nextNode();\n        continue;\n      }\n\n      if (cfg.excludeSelector) {\n        try {\n          if (el.closest(cfg.excludeSelector)) {\n            node = walker.nextNode();\n            continue;\n          }\n        } catch {\n          // invalid selector\n        }\n      }\n\n      if (cfg.visibleOnly && !isElementVisible(el)) {\n        node = walker.nextNode();\n        continue;\n      }\n\n      const enriched = buildEnriched(el, cfg.maxTextLength);\n      const hasText = enriched.text.length > 0;\n      const hasMeaningfulAttrs =\n        Object.keys(enriched.attributes).length > 0 &&\n        !Object.keys(enriched.attributes).every((k) => k === \"role\");\n\n      if (!hasText && !hasMeaningfulAttrs) {\n        node = walker.nextNode();\n        continue;\n      }\n\n      if (seenSelectors.has(enriched.selector)) {\n        node = walker.nextNode();\n        continue;\n      }\n      seenSelectors.add(enriched.selector);\n\n      const { score, formattingRule } = scoreCandidate(\n        el,\n        enriched,\n        cfg.rules,\n        ctx\n      );\n      raw.push({ el, domIndex, enriched, score, formattingRule });\n      domIndex += 1;\n    }\n    node = walker.nextNode();\n  }\n\n  raw.sort((a, b) => {\n    const sa = a.enriched.interactivity === \"static\" ? 1 : 0;\n    const sb = b.enriched.interactivity === \"static\" ? 1 : 0;\n    if (sa !== sb) return sa - sb;\n    if (b.score !== a.score) return b.score - a.score;\n    return a.domIndex - b.domIndex;\n  });\n\n  const kept: ScoredCandidate[] = [];\n  for (const cand of raw) {\n    if (kept.length >= cfg.maxElements) break;\n    if (shouldSuppress(kept, cand)) continue;\n    kept.push(cand);\n  }\n\n  kept.sort((a, b) => {\n    const sa = a.enriched.interactivity === \"static\" ? 1 : 0;\n    const sb = b.enriched.interactivity === \"static\" ? 1 : 0;\n    if (sa !== sb) return sa - sb;\n    if (sa === 1 && b.score !== a.score) return b.score - a.score;\n    return a.domIndex - b.domIndex;\n  });\n\n  return kept.map((c) => {\n    let formattedSummary: string | undefined;\n    if (c.formattingRule?.formatSummary) {\n      const line = c.formattingRule.formatSummary(c.el, c.enriched, ctx);\n      if (line) formattedSummary = line;\n    }\n    const out: EnrichedPageElement = { ...c.enriched };\n    if (formattedSummary) out.formattedSummary = formattedSummary;\n    return out;\n  });\n}\n\nfunction collectSimple(\n  cfg: ResolvedDomContextConfig,\n  rootEl: HTMLElement\n): EnrichedPageElement[] {\n  const elements: EnrichedPageElement[] = [];\n  const seenSelectors = new Set<string>();\n\n  const walker = document.createTreeWalker(rootEl, NodeFilter.SHOW_ELEMENT, null);\n  let node: Node | null = walker.currentNode;\n\n  while (node && elements.length < cfg.maxElements) {\n    if (node.nodeType === Node.ELEMENT_NODE) {\n      const el = node as HTMLElement;\n      const tag = el.tagName.toLowerCase();\n\n      if (SKIP_TAGS.has(tag)) {\n        node = walker.nextNode();\n        continue;\n      }\n\n      if (cfg.excludeSelector) {\n        try {\n          if (el.closest(cfg.excludeSelector)) {\n            node = walker.nextNode();\n            continue;\n          }\n        } catch {\n          // invalid selector\n        }\n      }\n\n      if (cfg.visibleOnly && !isElementVisible(el)) {\n        node = walker.nextNode();\n        continue;\n      }\n\n      const enriched = buildEnriched(el, cfg.maxTextLength);\n      const hasText = enriched.text.length > 0;\n      const hasMeaningfulAttrs =\n        Object.keys(enriched.attributes).length > 0 &&\n        !Object.keys(enriched.attributes).every((k) => k === \"role\");\n\n      if (!hasText && !hasMeaningfulAttrs) {\n        node = walker.nextNode();\n        continue;\n      }\n\n      if (!seenSelectors.has(enriched.selector)) {\n        seenSelectors.add(enriched.selector);\n        elements.push(enriched);\n      }\n    }\n    node = walker.nextNode();\n  }\n\n  const interactive: EnrichedPageElement[] = [];\n  const staticEls: EnrichedPageElement[] = [];\n  for (const el of elements) {\n    if (el.interactivity !== \"static\") interactive.push(el);\n    else staticEls.push(el);\n  }\n\n  return [...interactive, ...staticEls].slice(0, cfg.maxElements);\n}\n\n/**\n * Collect enriched page context from the DOM.\n *\n * - **Default (structured):** walks up to `maxCandidates` nodes, scores with\n *   {@link defaultParseRules} (or `rules`), suppresses redundant descendants when a\n *   formatting rule matches, then keeps the top `maxElements` by score (DOM order tie-break).\n * - **simple:** legacy path: stops once `maxElements` nodes are collected during traversal\n *   and sorts interactive before static.\n *\n * Pass `options: { mode: \"simple\" }` to disable rules. If `mode` is `simple` and `rules` is\n * non-empty, rules are ignored and a console warning is emitted.\n */\nexport function collectEnrichedPageContext(\n  options: DomContextOptions = {}\n): EnrichedPageElement[] {\n  const cfg = resolveDomContextConfig(options);\n  const rootEl = cfg.root ?? document.body;\n  if (!rootEl) return [];\n\n  if (cfg.mode === \"simple\") {\n    return collectSimple(cfg, rootEl);\n  }\n  return collectStructured(cfg, rootEl);\n}\n\nconst TEXT_PREVIEW_LEN = 100;\n\n/**\n * Format enriched page elements as a structured string for LLM consumption.\n * When `mode` is structured (default) and elements include {@link EnrichedPageElement.formattedSummary},\n * those render under **Structured summaries** before the usual interactivity groups.\n */\nexport function formatEnrichedContext(\n  elements: EnrichedPageElement[],\n  options: FormatEnrichedContextOptions = {}\n): string {\n  if (elements.length === 0) {\n    return \"No page elements found.\";\n  }\n\n  const mode: DomContextMode = options.mode ?? \"structured\";\n  const sections: string[] = [];\n\n  if (mode === \"structured\") {\n    const summaries = elements\n      .map((el) => el.formattedSummary)\n      .filter((s): s is string => !!s && s.length > 0);\n    if (summaries.length > 0) {\n      sections.push(\n        `Structured summaries:\\n${summaries.map((s) => `- ${s.split(\"\\n\").join(\"\\n  \")}`).join(\"\\n\")}`\n      );\n    }\n  }\n\n  const groups: Record<string, EnrichedPageElement[]> = {\n    clickable: [],\n    navigable: [],\n    input: [],\n    static: [],\n  };\n\n  for (const el of elements) {\n    if (mode === \"structured\" && el.formattedSummary) continue;\n    groups[el.interactivity].push(el);\n  }\n\n  if (groups.clickable.length > 0) {\n    const lines = groups.clickable.map(\n      (el) =>\n        `- ${el.selector}: \"${el.text.substring(0, TEXT_PREVIEW_LEN)}\" (clickable)`\n    );\n    sections.push(`Interactive elements:\\n${lines.join(\"\\n\")}`);\n  }\n\n  if (groups.navigable.length > 0) {\n    const lines = groups.navigable.map(\n      (el) =>\n        `- ${el.selector}${el.attributes.href ? `[href=\"${el.attributes.href}\"]` : \"\"}: \"${el.text.substring(0, TEXT_PREVIEW_LEN)}\" (navigable)`\n    );\n    sections.push(`Navigation links:\\n${lines.join(\"\\n\")}`);\n  }\n\n  if (groups.input.length > 0) {\n    const lines = groups.input.map(\n      (el) =>\n        `- ${el.selector}${el.attributes.type ? `[type=\"${el.attributes.type}\"]` : \"\"}: \"${el.text.substring(0, TEXT_PREVIEW_LEN)}\" (input)`\n    );\n    sections.push(`Form inputs:\\n${lines.join(\"\\n\")}`);\n  }\n\n  if (groups.static.length > 0) {\n    const lines = groups.static.map(\n      (el) => `- ${el.selector}: \"${el.text.substring(0, TEXT_PREVIEW_LEN)}\"`\n    );\n    sections.push(`Content:\\n${lines.join(\"\\n\")}`);\n  }\n\n  return sections.join(\"\\n\\n\");\n}\n","import type { PersonaTheme, PersonaThemePlugin } from '../types/theme';\n\nexport function accessibilityPlugin(): PersonaThemePlugin {\n  return {\n    name: '@persona/accessibility',\n    version: '1.0.0',\n    transform(theme: PersonaTheme): PersonaTheme {\n      return {\n        ...theme,\n        semantic: {\n          ...theme.semantic,\n          colors: {\n            ...theme.semantic.colors,\n            interactive: {\n              ...theme.semantic.colors.interactive,\n              focus: 'palette.colors.primary.700',\n              disabled: 'palette.colors.gray.300',\n            },\n          },\n        },\n      };\n    },\n    cssVariables: {\n      '--persona-accessibility-focus-ring':\n        '0 0 0 2px var(--persona-semantic-colors-surface, #fff), 0 0 0 4px var(--persona-semantic-colors-interactive-focus, #0f0f0f)',\n    },\n  };\n}\n\nexport function animationsPlugin(): PersonaThemePlugin {\n  return {\n    name: '@persona/animations',\n    version: '1.0.0',\n    transform(theme: PersonaTheme): PersonaTheme {\n      return {\n        ...theme,\n        palette: {\n          ...theme.palette,\n          transitions: {\n            fast: '150ms',\n            normal: '200ms',\n            slow: '300ms',\n            bounce: '500ms cubic-bezier(0.68, -0.55, 0.265, 1.55)',\n          },\n          easings: {\n            easeIn: 'cubic-bezier(0.4, 0, 1, 1)',\n            easeOut: 'cubic-bezier(0, 0, 0.2, 1)',\n            easeInOut: 'cubic-bezier(0.4, 0, 0.2, 1)',\n          },\n        },\n      };\n    },\n    cssVariables: {\n      '--persona-transition-fast': '150ms ease',\n      '--persona-transition-normal': '200ms ease',\n      '--persona-transition-slow': '300ms ease',\n    },\n  };\n}\n\nexport function brandPlugin(brandConfig: {\n  colors?: { primary?: string; secondary?: string; accent?: string };\n  logo?: string;\n}): PersonaThemePlugin {\n  return {\n    name: '@persona/brand',\n    version: '1.0.0',\n    transform(theme: PersonaTheme): PersonaTheme {\n      const newPalette = { ...theme.palette };\n\n      if (brandConfig.colors?.primary) {\n        newPalette.colors = {\n          ...newPalette.colors,\n          primary: {\n            50: adjustColor(brandConfig.colors.primary, 0.95),\n            100: adjustColor(brandConfig.colors.primary, 0.9),\n            200: adjustColor(brandConfig.colors.primary, 0.8),\n            300: adjustColor(brandConfig.colors.primary, 0.7),\n            400: adjustColor(brandConfig.colors.primary, 0.6),\n            500: brandConfig.colors.primary,\n            600: adjustColor(brandConfig.colors.primary, 0.8),\n            700: adjustColor(brandConfig.colors.primary, 0.7),\n            800: adjustColor(brandConfig.colors.primary, 0.6),\n            900: adjustColor(brandConfig.colors.primary, 0.5),\n            950: adjustColor(brandConfig.colors.primary, 0.45),\n          },\n        };\n      }\n\n      return {\n        ...theme,\n        palette: newPalette,\n      };\n    },\n  };\n}\n\nexport function reducedMotionPlugin(): PersonaThemePlugin {\n  return {\n    name: '@persona/reduced-motion',\n    version: '1.0.0',\n    transform(theme: PersonaTheme): PersonaTheme {\n      return {\n        ...theme,\n        palette: {\n          ...theme.palette,\n          transitions: {\n            fast: '0ms',\n            normal: '0ms',\n            slow: '0ms',\n            bounce: '0ms',\n          },\n        },\n      };\n    },\n    afterResolve(resolved: Record<string, string>): Record<string, string> {\n      return {\n        ...resolved,\n        '--persona-transition-fast': '0ms',\n        '--persona-transition-normal': '0ms',\n        '--persona-transition-slow': '0ms',\n      };\n    },\n  };\n}\n\nexport function highContrastPlugin(): PersonaThemePlugin {\n  return {\n    name: '@persona/high-contrast',\n    version: '1.0.0',\n    transform(theme: PersonaTheme): PersonaTheme {\n      return {\n        ...theme,\n        semantic: {\n          ...theme.semantic,\n          colors: {\n            ...theme.semantic.colors,\n            text: 'palette.colors.gray.950',\n            textMuted: 'palette.colors.gray.700',\n            border: 'palette.colors.gray.900',\n            divider: 'palette.colors.gray.900',\n          },\n        },\n      };\n    },\n  };\n}\n\nfunction adjustColor(hex: string, factor: number): string {\n  const r = parseInt(hex.slice(1, 3), 16);\n  const g = parseInt(hex.slice(3, 5), 16);\n  const b = parseInt(hex.slice(5, 7), 16);\n\n  const nr = Math.round(r + (255 - r) * (1 - factor));\n  const ng = Math.round(g + (255 - g) * (1 - factor));\n  const nb = Math.round(b + (255 - b) * (1 - factor));\n\n  return `#${nr.toString(16).padStart(2, '0')}${ng.toString(16).padStart(2, '0')}${nb.toString(16).padStart(2, '0')}`;\n}\n\nexport function createPlugin(config: {\n  name: string;\n  version: string;\n  transform?: (theme: PersonaTheme) => PersonaTheme;\n  cssVariables?: Record<string, string>;\n  afterResolve?: (resolved: Record<string, string>) => Record<string, string>;\n}): PersonaThemePlugin {\n  return {\n    name: config.name,\n    version: config.version,\n    transform: config.transform || ((theme) => theme),\n    cssVariables: config.cssVariables,\n    afterResolve: config.afterResolve,\n  };\n}\n","import type { AgentWidgetConfig } from \"./types\";\nimport type { DeepPartial, PersonaTheme } from \"./types/theme\";\nimport { DEFAULT_FLOATING_LAUNCHER_WIDTH } from \"./defaults\";\n\n/**\n * A named preset containing partial widget configuration.\n * Apply with: `createAgentExperience(el, { ...PRESET_SHOP.config, apiUrl: '...' })`\n * or via IIFE: `{ ...AgentWidget.PRESETS.shop.config, apiUrl: '...' }`\n */\nexport interface WidgetPreset {\n  id: string;\n  label: string;\n  config: Partial<AgentWidgetConfig>;\n}\n\n/** Shopping palette + semantic roles (matches prior shop preset visuals). */\nconst SHOP_THEME: DeepPartial<PersonaTheme> = {\n  palette: {\n    colors: {\n      primary: { 500: \"#111827\" },\n      accent: { 600: \"#1d4ed8\" },\n      gray: {\n        50: \"#ffffff\",\n        100: \"#f8fafc\",\n        200: \"#f1f5f9\",\n        500: \"#6b7280\",\n        900: \"#000000\",\n      },\n    },\n    radius: {\n      sm: \"0.75rem\",\n      md: \"1rem\",\n      lg: \"1.5rem\",\n      launcher: \"9999px\",\n      button: \"9999px\",\n    },\n  },\n  semantic: {\n    colors: {\n      primary: \"palette.colors.primary.500\",\n      textInverse: \"palette.colors.gray.50\",\n    },\n  },\n};\n\nconst PANEL_EDGELESS_THEME: DeepPartial<PersonaTheme> = {\n  components: {\n    panel: {\n      borderRadius: \"0\",\n      shadow: \"none\",\n    },\n  },\n};\n\n/**\n * Shopping / e-commerce preset.\n * Dark header, rounded launchers, shopping-oriented copy.\n */\nexport const PRESET_SHOP: WidgetPreset = {\n  id: \"shop\",\n  label: \"Shopping Assistant\",\n  config: {\n    theme: SHOP_THEME,\n    launcher: {\n      title: \"Shopping Assistant\",\n      subtitle: \"Here to help you find what you need\",\n      agentIconText: \"🛍️\",\n      position: \"bottom-right\",\n      width: DEFAULT_FLOATING_LAUNCHER_WIDTH,\n    },\n    copy: {\n      welcomeTitle: \"Welcome to our shop!\",\n      welcomeSubtitle: \"I can help you find products and answer questions\",\n      inputPlaceholder: \"Ask me anything...\",\n      sendButtonLabel: \"Send\",\n    },\n    suggestionChips: [\n      \"What can you help me with?\",\n      \"Tell me about your features\",\n      \"How does this work?\",\n    ],\n  },\n};\n\n/**\n * Minimal preset.\n * Stripped-down header, no launcher button, suitable for inline embeds.\n */\nexport const PRESET_MINIMAL: WidgetPreset = {\n  id: \"minimal\",\n  label: \"Minimal\",\n  config: {\n    launcher: {\n      enabled: false,\n      fullHeight: true,\n    },\n    layout: {\n      header: {\n        layout: \"minimal\",\n        showCloseButton: false,\n      },\n      messages: {\n        layout: \"minimal\",\n      },\n    },\n    theme: PANEL_EDGELESS_THEME,\n  },\n};\n\n/**\n * Fullscreen assistant preset.\n * No launcher, content-max-width constrained, minimal header.\n */\nexport const PRESET_FULLSCREEN: WidgetPreset = {\n  id: \"fullscreen\",\n  label: \"Fullscreen Assistant\",\n  config: {\n    launcher: {\n      enabled: false,\n      fullHeight: true,\n    },\n    layout: {\n      header: {\n        layout: \"minimal\",\n        showCloseButton: false,\n      },\n      contentMaxWidth: \"72ch\",\n    },\n    theme: PANEL_EDGELESS_THEME,\n  },\n};\n\n/** All named presets keyed by ID. */\nexport const PRESETS: Record<string, WidgetPreset> = {\n  shop: PRESET_SHOP,\n  minimal: PRESET_MINIMAL,\n  fullscreen: PRESET_FULLSCREEN,\n};\n\n/** Look up a preset by ID. */\nexport function getPreset(id: string): WidgetPreset | undefined {\n  return PRESETS[id];\n}\n","import {\n  initAgentWidget as initAgentWidgetFn,\n  type AgentWidgetInitHandle\n} from \"./runtime/init\";\n\nexport type {\n  AgentWidgetConfig,\n  TargetResolver,\n  ResolvedTarget,\n  AgentWidgetFeatureFlags,\n  AgentWidgetArtifactsFeature,\n  AgentWidgetArtifactsLayoutConfig,\n  PersonaArtifactKind,\n  PersonaArtifactRecord,\n  PersonaArtifactManualUpsert,\n  ArtifactConfigPayload,\n  AgentWidgetInitOptions,\n  AgentWidgetMessage,\n  AgentWidgetLauncherConfig,\n  AgentWidgetDockConfig,\n  AgentWidgetEvent,\n  AgentWidgetStreamParser,\n  AgentWidgetStreamParserResult,\n  AgentWidgetRequestPayload,\n  // Context provider types (e.g. for config.contextProviders)\n  AgentWidgetContextProvider,\n  AgentWidgetContextProviderContext,\n  AgentWidgetCustomFetch,\n  AgentWidgetSSEEventParser,\n  AgentWidgetSSEEventResult,\n  AgentWidgetHeadersFunction,\n  // Multi-modal content types\n  TextContentPart,\n  ImageContentPart,\n  ContentPart,\n  MessageContent,\n  // Attachment config type\n  AgentWidgetAttachmentsConfig,\n  AgentWidgetComposerConfig,\n  // Layout types\n  AgentWidgetLayoutConfig,\n  AgentWidgetHeaderLayoutConfig,\n  AgentWidgetMessageLayoutConfig,\n  AgentWidgetAvatarConfig,\n  AgentWidgetTimestampConfig,\n  WidgetLayoutSlot,\n  SlotRenderer,\n  SlotRenderContext,\n  HeaderRenderContext,\n  MessageRenderContext,\n  // Markdown types\n  AgentWidgetMarkdownConfig,\n  AgentWidgetMarkdownOptions,\n  AgentWidgetMarkdownRendererOverrides,\n  // Message actions types\n  AgentWidgetMessageActionsConfig,\n  AgentWidgetMessageFeedback,\n  // Client token types\n  ClientSession,\n  ClientInitResponse,\n  ClientChatRequest,\n  ClientFeedbackRequest,\n  ClientFeedbackType,\n  // Message injection types\n  InjectMessageOptions,\n  InjectAssistantMessageOptions,\n  InjectUserMessageOptions,\n  InjectSystemMessageOptions,\n  InjectComponentDirectiveOptions,\n  // Loading indicator types\n  LoadingIndicatorRenderContext,\n  AgentWidgetLoadingIndicatorConfig,\n  // Idle indicator types\n  IdleIndicatorRenderContext,\n  // Agent execution types\n  AgentConfig,\n  AgentLoopConfig,\n  AgentToolsConfig,\n  AgentRequestOptions,\n  AgentExecutionState,\n  AgentMessageMetadata,\n  AgentWidgetAgentRequestPayload,\n  // Approval types\n  AgentWidgetApproval,\n  AgentWidgetApprovalConfig,\n  AgentWidgetApprovalDecisionOptions,\n  // WebMCP: page-discovered tool consumption\n  AgentWidgetWebMcpConfig,\n  ClientToolDefinition,\n  WebMcpConfirmHandler,\n  WebMcpConfirmInfo,\n  WebMcpToolResult,\n  // Event stream types\n  SSEEventRecord,\n  EventStreamConfig,\n  EventStreamBadgeColor,\n  EventStreamViewRenderContext,\n  EventStreamRowRenderContext,\n  EventStreamToolbarRenderContext,\n  EventStreamPayloadRenderContext,\n  // Controller event map\n  AgentWidgetControllerEventMap,\n  AgentWidgetReadAloudEvent,\n  // Ask-user-question (built-in answer-pill sheet) types\n  AskUserQuestionPayload,\n  AskUserQuestionPrompt,\n  AskUserQuestionOption,\n  AgentWidgetAskUserQuestionFeature,\n  AgentWidgetAskUserQuestionStyles\n} from \"./types\";\n\nexport type {\n  RuntypeExecutionStreamEvent,\n  RuntypeFlowSSEEvent,\n  RuntypeStreamEventOf,\n  RuntypeTurnCompleteEvent,\n  RuntypeStepCompleteEvent,\n  RuntypeStopReasonKind,\n  RuntypeClientInitRequest,\n  RuntypeClientInitResponse,\n  RuntypeClientChatRequest,\n  RuntypeClientChatStreamEvent,\n  RuntypeClientResumeRequest,\n  RuntypeClientResumeStreamEvent,\n  RuntypeClientFeedbackRequest,\n  RuntypeClientFeedbackResponse,\n  RuntypeClientFeedbackType,\n} from \"./generated/runtype-openapi-contract\";\n\nexport {\n  ASK_USER_QUESTION_TOOL_NAME,\n  createAskUserQuestionBubble,\n  ensureAskUserQuestionSheet,\n  removeAskUserQuestionSheet,\n  isAskUserQuestionMessage,\n  parseAskUserQuestionPayload\n} from \"./components/ask-user-question-bubble\";\n\nexport {\n  ASK_USER_QUESTION_CLIENT_TOOL,\n  ASK_USER_QUESTION_PARAMETERS_SCHEMA,\n  builtInClientToolsForDispatch\n} from \"./ask-user-question-tool\";\n\nexport {\n  SUGGEST_REPLIES_TOOL_NAME,\n  SUGGEST_REPLIES_CLIENT_TOOL,\n  SUGGEST_REPLIES_PARAMETERS_SCHEMA,\n  isSuggestRepliesMessage,\n  parseSuggestRepliesPayload,\n  latestAgentSuggestions\n} from \"./suggest-replies-tool\";\n\nexport { initAgentWidgetFn as initAgentWidget };\nexport {\n  createWidgetHostLayout,\n  type WidgetHostLayout,\n  type WidgetHostLayoutMode\n} from \"./runtime/host-layout\";\nexport {\n  createAgentExperience,\n  type AgentWidgetController\n} from \"./ui\";\nexport {\n  AgentWidgetSession,\n  type AgentWidgetSessionStatus\n} from \"./session\";\nexport { AgentWidgetClient } from \"./client\";\nexport type { SSEEventCallback } from \"./client\";\nexport {\n  WebMcpBridge,\n  WEBMCP_TOOL_PREFIX,\n  isWebMcpToolName,\n  stripWebMcpPrefix\n} from \"./webmcp-bridge\";\nexport { createLocalStorageAdapter } from \"./utils/storage\";\nexport {\n  createActionManager,\n  defaultActionHandlers,\n  defaultJsonActionParser\n} from \"./utils/actions\";\nexport {\n  markdownPostprocessor,\n  escapeHtml,\n  directivePostprocessor,\n  createMarkdownProcessor,\n  createMarkdownProcessorFromConfig,\n  createDirectivePostprocessor\n} from \"./postprocessors\";\nexport type { MarkdownProcessorOptions } from \"./postprocessors\";\nexport {\n  createDefaultSanitizer,\n  resolveSanitizer\n} from \"./utils/sanitize\";\nexport type { SanitizeFunction } from \"./utils/sanitize\";\nexport {\n  createPlainTextParser,\n  createJsonStreamParser,\n  createFlexibleJsonStreamParser,\n  createRegexJsonParser,\n  createXmlParser\n} from \"./utils/formatting\";\nexport {\n  // Multi-modal content utilities\n  normalizeContent,\n  getDisplayText,\n  hasImages,\n  getImageParts,\n  createTextPart,\n  createImagePart,\n  fileToImagePart,\n  validateImageFile\n} from \"./utils/content\";\nexport {\n  collectEnrichedPageContext,\n  formatEnrichedContext,\n  generateStableSelector,\n  defaultParseRules\n} from \"./utils/dom-context\";\nexport type {\n  EnrichedPageElement,\n  DomContextOptions,\n  DomContextMode,\n  ParseOptionsConfig,\n  ParseRule,\n  RuleScoringContext,\n  FormatEnrichedContextOptions\n} from \"./utils/dom-context\";\nexport {\n  AttachmentManager,\n  type PendingAttachment,\n  type AttachmentManagerConfig\n} from \"./utils/attachment-manager\";\nexport {\n  generateMessageId,\n  generateUserMessageId,\n  generateAssistantMessageId\n} from \"./utils/message-id\";\nexport { isDockedMountMode, resolveDockConfig } from \"./utils/dock\";\n// NOTE: `generateCodeSnippet` (dev/config-tool helper) is intentionally NOT\n// re-exported here so it stays out of the IIFE/CDN bundle (index-global.ts\n// re-exports from this module). It is re-added to the npm barrel in `index.ts`.\nexport { VERSION } from \"./version\";\nexport type { AgentWidgetInitHandle };\n\n// Plugin system exports\nexport type { AgentWidgetPlugin } from \"./plugins/types\";\nexport { pluginRegistry } from \"./plugins/registry\";\n\n// Stream animation plugin API: lets consumers register custom animations\n// that match the built-in surface (typewriter, pop-bubble) and subpath\n// modules (letter-rise, word-fade, wipe, glyph-cycle).\nexport {\n  registerStreamAnimationPlugin,\n  unregisterStreamAnimationPlugin,\n  listRegisteredStreamAnimations,\n} from \"./utils/stream-animation\";\nexport type {\n  StreamAnimationPlugin,\n  StreamAnimationContext,\n  AgentWidgetStreamAnimationBuffer,\n  AgentWidgetStreamAnimationBuiltinType,\n  AgentWidgetStreamAnimationType,\n  AgentWidgetStreamAnimationFeature,\n  AgentWidgetStreamAnimationPlaceholder,\n  AgentWidgetScrollMode,\n  AgentWidgetScrollRestorePosition,\n  AgentWidgetScrollBehaviorFeature,\n  AgentWidgetScrollToBottomFeature,\n  AgentWidgetComponentRenderer,\n} from \"./types\";\n\n// Action system types: needed to type the `actionHandlers` / `actionParsers`\n// config options and to author custom handlers/parsers.\nexport type {\n  AgentWidgetActionHandler,\n  AgentWidgetActionHandlerResult,\n  AgentWidgetActionParser,\n  AgentWidgetParsedAction,\n  AgentWidgetActionContext,\n  AgentWidgetActionEventPayload,\n} from \"./types\";\n\n// Dropdown utility exports\nexport { createDropdownMenu } from \"./utils/dropdown\";\nexport type { DropdownMenuItem, CreateDropdownOptions, DropdownMenuHandle } from \"./utils/dropdown\";\n\n// Icon utility exports\nexport { renderLucideIcon } from \"./utils/icons\";\nexport type { IconName } from \"./utils/icons\";\n\n// Button utility exports\nexport { createIconButton, createLabelButton, createToggleGroup, createComboButton } from \"./utils/buttons\";\nexport type {\n  CreateIconButtonOptions,\n  CreateLabelButtonOptions,\n  CreateToggleGroupOptions,\n  ToggleGroupItem,\n  ToggleGroupHandle,\n  CreateComboButtonOptions,\n  ComboButtonHandle\n} from \"./utils/buttons\";\n\n// NOTE: `createDemoCarousel` (demo-only component) is intentionally NOT\n// re-exported here so it stays out of the IIFE/CDN bundle. It is re-added to\n// the npm barrel in `index.ts`.\n\n// Theme system exports\nexport {\n  createTheme,\n  resolveTokens,\n  themeToCssVariables,\n  applyThemeVariables,\n  getActiveTheme,\n  getColorScheme,\n  detectColorScheme,\n  createThemeObserver\n} from \"./utils/theme\";\nexport {\n  DEFAULT_PALETTE,\n  DEFAULT_SEMANTIC,\n  DEFAULT_COMPONENTS,\n  validateTheme,\n  THEME_ZONES\n} from \"./utils/tokens\";\nexport type { ThemeZone } from \"./utils/tokens\";\nexport {\n  accessibilityPlugin,\n  animationsPlugin,\n  brandPlugin,\n  reducedMotionPlugin,\n  highContrastPlugin,\n  createPlugin\n} from \"./utils/plugins\";\nexport type {\n  DeepPartial,\n  PersonaTheme,\n  PersonaThemePlugin,\n  CreateThemeOptions,\n  TokenReference,\n  ColorShade,\n  ColorPalette,\n  SpacingScale,\n  TypographyScale,\n  ShadowScale,\n  BorderScale,\n  RadiusScale,\n  SemanticColors,\n  SemanticSpacing,\n  SemanticTypography,\n  ComponentTokens,\n  ArtifactToolbarTokens,\n  ArtifactTabTokens,\n  ArtifactPaneTokens,\n  IconButtonTokens,\n  LabelButtonTokens,\n  ToggleGroupTokens,\n  ThemeValidationResult,\n  ThemeValidationError\n} from \"./types/theme\";\n\n// Component system exports\nexport { componentRegistry } from \"./components/registry\";\nexport type { ComponentRenderer, ComponentContext } from \"./components/registry\";\nexport {\n  createComponentStreamParser,\n  isComponentDirectiveType\n} from \"./utils/component-parser\";\nexport type { ComponentDirective } from \"./utils/component-parser\";\nexport {\n  renderComponentDirective,\n  createComponentMiddleware,\n  hasComponentDirective,\n  extractComponentDirectiveFromMessage\n} from \"./utils/component-middleware\";\n\n// Default configuration exports\nexport {\n  DEFAULT_WIDGET_CONFIG,\n  DEFAULT_FLOATING_LAUNCHER_MAX_WIDTH,\n  DEFAULT_FLOATING_LAUNCHER_WIDTH,\n  mergeWithDefaults\n} from \"./defaults\";\nexport {\n  PRESETS,\n  getPreset,\n  PRESET_SHOP,\n  PRESET_MINIMAL,\n  PRESET_FULLSCREEN\n} from \"./presets\";\nexport type { WidgetPreset } from \"./presets\";\n\n// Layout system exports\nexport {\n  buildHeader,\n  buildComposer,\n  attachHeaderToContainer\n} from \"./components/panel\";\nexport type {\n  HeaderElements,\n  HeaderBuildContext,\n  ComposerElements,\n  ComposerBuildContext\n} from \"./components/panel\";\nexport {\n  headerLayouts,\n  getHeaderLayout,\n  buildHeaderWithLayout,\n  buildDefaultHeader,\n  buildMinimalHeader\n} from \"./components/header-layouts\";\nexport type {\n  HeaderLayoutContext,\n  HeaderLayoutRenderer\n} from \"./components/header-layouts\";\nexport {\n  createStandardBubble,\n  createBubbleWithLayout,\n  createTypingIndicator,\n  createMessageActions,\n  renderLoadingIndicatorWithFallback\n} from \"./components/message-bubble\";\nexport type {\n  MessageTransform,\n  MessageActionCallbacks,\n  LoadingIndicatorRenderer,\n  CreateStandardBubbleOptions\n} from \"./components/message-bubble\";\nexport {\n  createCSATFeedback,\n  createNPSFeedback\n} from \"./components/feedback\";\nexport type { CSATFeedbackOptions, NPSFeedbackOptions } from \"./components/feedback\";\n\n// Voice module exports\nexport {\n  createVoiceProvider,\n  createBestAvailableVoiceProvider,\n  isVoiceSupported\n} from \"./voice\";\nexport type {\n  VoiceProvider,\n  VoiceResult,\n  VoiceStatus,\n  VoiceConfig,\n  VoiceMetrics,\n  VoicePlaybackEngine,\n  PcmStreamPlayer,\n  AgentWidgetVoiceStatusEvent,\n  ReadAloudState,\n  SpeechEngine,\n  SpeechRequest,\n  SpeechCallbacks\n} from \"./types\";\n\nexport {\n  BrowserSpeechEngine,\n  pickBestVoice,\n  ReadAloudController\n} from \"./voice\";\nexport type {\n  BrowserSpeechEngineOptions,\n  ReadAloudListener,\n  FallbackSpeechEngineOptions\n} from \"./voice\";\n// RuntypeSpeechEngine + FallbackSpeechEngine ship from the\n// `@runtypelabs/persona/voice-worklet-player` subpath, not here: keeping the\n// hosted read-aloud engine out of the main bundle lets the IIFE/CDN build defer\n// it to the `runtype-tts.js` chunk (loaded only for `provider: 'runtype'`). The\n// blessed way to use it is `textToSpeech: { provider: 'runtype' }` — no import\n// needed.\n\nexport default initAgentWidgetFn;\n","import type { AgentWidgetConfig } from \"../types\";\nimport { VERSION } from \"../version\";\n\ntype ParserType = \"plain\" | \"json\" | \"regex-json\" | \"xml\";\nexport type CodeFormat = \"esm\" | \"script-installer\" | \"script-manual\" | \"script-advanced\" | \"react-component\" | \"react-advanced\";\n\n/**\n * Hook code templates for code generation.\n * Each hook can be provided as a string (code template) OR as an actual function.\n * Functions are automatically serialized via `.toString()`.\n *\n * IMPORTANT: When providing functions:\n * - Functions must be self-contained (no external variables/closures)\n * - External variables will be undefined when the generated code runs\n * - Use arrow functions or regular function expressions\n *\n * @example\n * ```typescript\n * // Both of these work:\n *\n * // As string:\n * { getHeaders: \"async () => ({ 'Authorization': 'Bearer token' })\" }\n *\n * // As function (recommended - better IDE support):\n * { getHeaders: async () => ({ 'Authorization': 'Bearer token' }) }\n * ```\n */\nexport type CodeGeneratorHooks = {\n  /**\n   * Custom getHeaders function.\n   * Should return an object with header key-value pairs.\n   *\n   * @example\n   * ```typescript\n   * async () => ({ 'Authorization': `Bearer ${await getAuthToken()}` })\n   * ```\n   */\n  getHeaders?: string | (() => Record<string, string> | Promise<Record<string, string>>);\n\n  /**\n   * Custom onFeedback callback for message actions.\n   * Receives a feedback object with type, messageId, and message.\n   *\n   * @example\n   * ```typescript\n   * (feedback) => { console.log('Feedback:', feedback.type); }\n   * ```\n   */\n  onFeedback?: string | ((feedback: { type: string; messageId: string; message: unknown }) => void);\n\n  /**\n   * Custom onCopy callback for message actions.\n   * Receives the message that was copied.\n   *\n   * @example\n   * ```typescript\n   * (message) => { analytics.track('message_copied', { id: message.id }); }\n   * ```\n   */\n  onCopy?: string | ((message: unknown) => void);\n\n  /**\n   * Custom requestMiddleware function.\n   * Receives { payload, config } context.\n   *\n   * @example\n   * ```typescript\n   * ({ payload }) => ({ ...payload, metadata: { pageUrl: window.location.href } })\n   * ```\n   */\n  requestMiddleware?: string | ((context: { payload: unknown; config: unknown }) => unknown);\n\n  /**\n   * Custom action handlers array.\n   * Array of handler functions.\n   *\n   * @example\n   * ```typescript\n   * [\n   *   (action, context) => {\n   *     if (action.type === 'custom') {\n   *       return { handled: true };\n   *     }\n   *   }\n   * ]\n   * ```\n   */\n  actionHandlers?: string | Array<(action: unknown, context: unknown) => unknown>;\n\n  /**\n   * Custom action parsers array.\n   * Array of parser functions.\n   */\n  actionParsers?: string | Array<(context: unknown) => unknown>;\n\n  /**\n   * Custom postprocessMessage function.\n   * Receives { text, message, streaming, raw } context.\n   * Will override the default markdownPostprocessor.\n   *\n   * @example\n   * ```typescript\n   * ({ text }) => customMarkdownProcessor(text)\n   * ```\n   */\n  postprocessMessage?: string | ((context: { text: string; message?: unknown; streaming?: boolean; raw?: string }) => string);\n\n  /**\n   * Custom context providers array.\n   * Array of provider functions.\n   */\n  contextProviders?: string | Array<() => unknown>;\n\n  /**\n   * Custom stream parser factory.\n   * Should be a function that returns a StreamParser.\n   */\n  streamParser?: string | (() => unknown);\n};\n\n/**\n * Options for code generation beyond format selection.\n */\nexport type CodeGeneratorOptions = {\n  /**\n   * Custom hook code to inject into the generated snippet.\n   * Hooks are JavaScript/TypeScript code strings that will be\n   * inserted at appropriate locations in the output.\n   */\n  hooks?: CodeGeneratorHooks;\n\n  /**\n   * Whether to include comments explaining each hook.\n   * @default true\n   */\n  includeHookComments?: boolean;\n\n  /**\n   * If provided, emits `windowKey` in the generated `initAgentWidget()` call\n   * so the widget handle is stored on `window[windowKey]`.\n   * Only affects script formats (script-installer, script-manual, script-advanced).\n   */\n  windowKey?: string;\n\n  /**\n   * CSS selector for the mount target. When omitted, the widget mounts on\n   * `body` (the existing default). When provided, the generated snippet mounts\n   * into that element: ESM / React / manual / advanced formats emit it as the\n   * `target` argument to `initAgentWidget()`, and `script-installer` serializes\n   * it into `data-config` (the installer reads `config.target`).\n   * @default \"body\"\n   */\n  target?: string;\n};\n\n// Internal type for normalized hooks (always strings)\ntype NormalizedHooks = {\n  [K in keyof CodeGeneratorHooks]: string | undefined;\n};\n\n/**\n * Serialize a hook value (string, function, or array of functions) to a string.\n */\nfunction serializeHook(hook: string | Function | Function[] | undefined): string | undefined {\n  if (hook === undefined) return undefined;\n  if (typeof hook === 'string') return hook;\n  if (Array.isArray(hook)) {\n    return `[${hook.map(fn => fn.toString()).join(', ')}]`;\n  }\n  return hook.toString();\n}\n\n/**\n * Normalize hooks by converting any functions to their string representations.\n */\nfunction normalizeHooks(hooks: CodeGeneratorHooks | undefined): NormalizedHooks | undefined {\n  if (!hooks) return undefined;\n\n  return {\n    getHeaders: serializeHook(hooks.getHeaders),\n    onFeedback: serializeHook(hooks.onFeedback),\n    onCopy: serializeHook(hooks.onCopy),\n    requestMiddleware: serializeHook(hooks.requestMiddleware),\n    actionHandlers: serializeHook(hooks.actionHandlers),\n    actionParsers: serializeHook(hooks.actionParsers),\n    postprocessMessage: serializeHook(hooks.postprocessMessage),\n    contextProviders: serializeHook(hooks.contextProviders),\n    streamParser: serializeHook(hooks.streamParser),\n  };\n}\n\n// =============================================================================\n// Template Literals for Code Generation\n// These are injected into generated code as-is.\n// =============================================================================\n\n/**\n * Template: Parser for JSON wrapped in markdown code fences (TypeScript).\n * @internal\n */\nconst TEMPLATE_MARKDOWN_JSON_PARSER_TS = `({ text, message }: any) => {\n  const jsonSource = (message as any).rawContent || text || message.content;\n  if (!jsonSource || typeof jsonSource !== 'string') return null;\n  let cleanJson = jsonSource\n    .replace(/^\\`\\`\\`(?:json)?\\\\s*\\\\n?/, '')\n    .replace(/\\\\n?\\`\\`\\`\\\\s*$/, '')\n    .trim();\n  if (!cleanJson.startsWith('{') || !cleanJson.endsWith('}')) return null;\n  try {\n    const parsed = JSON.parse(cleanJson);\n    if (parsed.action) return { type: parsed.action, payload: parsed };\n  } catch (e) { return null; }\n  return null;\n}`;\n\n/**\n * Template: Parser for JSON wrapped in markdown code fences (ES5).\n * @internal\n */\nconst TEMPLATE_MARKDOWN_JSON_PARSER_ES5 = `function(ctx) {\n  var jsonSource = ctx.message.rawContent || ctx.text || ctx.message.content;\n  if (!jsonSource || typeof jsonSource !== 'string') return null;\n  var cleanJson = jsonSource\n    .replace(/^\\`\\`\\`(?:json)?\\\\s*\\\\n?/, '')\n    .replace(/\\\\n?\\`\\`\\`\\\\s*$/, '')\n    .trim();\n  if (!cleanJson.startsWith('{') || !cleanJson.endsWith('}')) return null;\n  try {\n    var parsed = JSON.parse(cleanJson);\n    if (parsed.action) return { type: parsed.action, payload: parsed };\n  } catch (e) { return null; }\n  return null;\n}`;\n\n/**\n * Template: Handler for nav_then_click actions (TypeScript).\n * @internal\n */\nconst TEMPLATE_NAV_THEN_CLICK_HANDLER_TS = `(action: any, context: any) => {\n  if (action.type !== 'nav_then_click') return;\n  const payload = action.payload || action.raw || {};\n  const url = payload?.page;\n  const text = payload?.on_load_text || 'Navigating...';\n  if (!url) return { handled: true, displayText: text };\n  const messageId = context.message?.id;\n  const processedActions = JSON.parse(localStorage.getItem(PROCESSED_ACTIONS_KEY) || '[]');\n  const actionKey = \\`nav_\\${messageId}_\\${url}\\`;\n  if (processedActions.includes(actionKey)) {\n    return { handled: true, displayText: text };\n  }\n  processedActions.push(actionKey);\n  localStorage.setItem(PROCESSED_ACTIONS_KEY, JSON.stringify(processedActions));\n  const targetUrl = url.startsWith('http') ? url : new URL(url, window.location.origin).toString();\n  window.location.href = targetUrl;\n  return { handled: true, displayText: text };\n}`;\n\n/**\n * Template: Handler for nav_then_click actions (ES5).\n * @internal\n */\nconst TEMPLATE_NAV_THEN_CLICK_HANDLER_ES5 = `function(action, context) {\n  if (action.type !== 'nav_then_click') return;\n  var payload = action.payload || action.raw || {};\n  var url = payload.page;\n  var text = payload.on_load_text || 'Navigating...';\n  if (!url) return { handled: true, displayText: text };\n  var messageId = context.message ? context.message.id : null;\n  var processedActions = JSON.parse(localStorage.getItem(PROCESSED_ACTIONS_KEY) || '[]');\n  var actionKey = 'nav_' + messageId + '_' + url;\n  if (processedActions.includes(actionKey)) {\n    return { handled: true, displayText: text };\n  }\n  processedActions.push(actionKey);\n  localStorage.setItem(PROCESSED_ACTIONS_KEY, JSON.stringify(processedActions));\n  var targetUrl = url.startsWith('http') ? url : new URL(url, window.location.origin).toString();\n  window.location.href = targetUrl;\n  return { handled: true, displayText: text };\n}`;\n\n/**\n * Template: Stream parser callback (TypeScript).\n * @internal\n */\nconst TEMPLATE_STREAM_PARSER_CALLBACK_TS = `(parsed: any) => {\n  if (!parsed || typeof parsed !== 'object') return null;\n  if (parsed.action === 'nav_then_click') return 'Navigating...';\n  if (parsed.action === 'message') return parsed.text || '';\n  if (parsed.action === 'message_and_click') return parsed.text || 'Processing...';\n  return parsed.text || null;\n}`;\n\n/**\n * Template: Stream parser callback (ES5).\n * @internal\n */\nconst TEMPLATE_STREAM_PARSER_CALLBACK_ES5 = `function(parsed) {\n  if (!parsed || typeof parsed !== 'object') return null;\n  if (parsed.action === 'nav_then_click') return 'Navigating...';\n  if (parsed.action === 'message') return parsed.text || '';\n  if (parsed.action === 'message_and_click') return parsed.text || 'Processing...';\n  return parsed.text || null;\n}`;\n\nfunction detectParserTypeFromStreamParser(streamParser: any): ParserType | null {\n  if (!streamParser) return null;\n  const fnString = streamParser.toString();\n  if (fnString.includes(\"createJsonStreamParser\") || fnString.includes(\"partial-json\")) {\n    return \"json\";\n  }\n  if (fnString.includes(\"createRegexJsonParser\") || fnString.includes(\"regex\")) {\n    return \"regex-json\";\n  }\n  if (fnString.includes(\"createXmlParser\") || fnString.includes(\"<text>\")) {\n    return \"xml\";\n  }\n  return null;\n}\n\nfunction getParserTypeFromConfig(config: AgentWidgetConfig): ParserType {\n  return config.parserType ?? detectParserTypeFromStreamParser(config.streamParser) ?? \"plain\";\n}\n\n// Helper to generate toolCall config\nfunction generateToolCallConfig(config: any, indent: string): string[] {\n  const lines: string[] = [];\n  if (config.toolCall) {\n    lines.push(`${indent}toolCall: {`);\n    Object.entries(config.toolCall).forEach(([key, value]) => {\n      if (typeof value === \"string\") {\n        lines.push(`${indent}  ${key}: \"${value}\",`);\n      }\n    });\n    lines.push(`${indent}},`);\n  }\n  return lines;\n}\n\n// Helper to generate messageActions config (with optional hook callbacks)\nfunction generateMessageActionsConfig(config: any, indent: string, hooks?: CodeGeneratorHooks): string[] {\n  const lines: string[] = [];\n  const hasSerializableProps = config.messageActions && Object.entries(config.messageActions).some(\n    ([key, value]) => key !== \"onFeedback\" && key !== \"onCopy\" && value !== undefined\n  );\n  const hasHookCallbacks = hooks?.onFeedback || hooks?.onCopy;\n\n  if (hasSerializableProps || hasHookCallbacks) {\n    lines.push(`${indent}messageActions: {`);\n\n    // Add serializable properties from config\n    if (config.messageActions) {\n      Object.entries(config.messageActions).forEach(([key, value]) => {\n        // Skip function callbacks - we'll add from hooks if provided\n        if (key === \"onFeedback\" || key === \"onCopy\") return;\n        if (typeof value === \"string\") {\n          lines.push(`${indent}  ${key}: \"${value}\",`);\n        } else if (typeof value === \"boolean\") {\n          lines.push(`${indent}  ${key}: ${value},`);\n        }\n      });\n    }\n\n    // Add hook callbacks\n    if (hooks?.onFeedback) {\n      lines.push(`${indent}  onFeedback: ${hooks.onFeedback},`);\n    }\n    if (hooks?.onCopy) {\n      lines.push(`${indent}  onCopy: ${hooks.onCopy},`);\n    }\n\n    lines.push(`${indent}},`);\n  }\n  return lines;\n}\n\n// Helper to generate markdown config (excluding renderer functions)\nfunction generateMarkdownConfig(config: any, indent: string): string[] {\n  const lines: string[] = [];\n  if (config.markdown) {\n    const hasOptions = config.markdown.options && Object.keys(config.markdown.options).length > 0;\n    const hasDisableDefaultStyles = config.markdown.disableDefaultStyles !== undefined;\n    \n    if (hasOptions || hasDisableDefaultStyles) {\n      lines.push(`${indent}markdown: {`);\n      \n      if (hasOptions) {\n        lines.push(`${indent}  options: {`);\n        Object.entries(config.markdown.options).forEach(([key, value]) => {\n          if (typeof value === \"string\") {\n            lines.push(`${indent}    ${key}: \"${value}\",`);\n          } else if (typeof value === \"boolean\") {\n            lines.push(`${indent}    ${key}: ${value},`);\n          }\n        });\n        lines.push(`${indent}  },`);\n      }\n      \n      if (hasDisableDefaultStyles) {\n        lines.push(`${indent}  disableDefaultStyles: ${config.markdown.disableDefaultStyles},`);\n      }\n      \n      lines.push(`${indent}},`);\n    }\n  }\n  return lines;\n}\n\n// Helper to generate layout config (excluding render functions and slots)\nfunction generateLayoutConfig(config: any, indent: string): string[] {\n  const lines: string[] = [];\n  if (config.layout) {\n    const hasHeader = config.layout.header && Object.keys(config.layout.header).some(\n      (key: string) => key !== \"render\"\n    );\n    const hasMessages = config.layout.messages && Object.keys(config.layout.messages).some(\n      (key: string) => key !== \"renderUserMessage\" && key !== \"renderAssistantMessage\"\n    );\n    \n    if (hasHeader || hasMessages) {\n      lines.push(`${indent}layout: {`);\n      \n      // Header config (excluding render function)\n      if (hasHeader) {\n        lines.push(`${indent}  header: {`);\n        Object.entries(config.layout.header).forEach(([key, value]) => {\n          if (key === \"render\") return; // Skip render function\n          if (typeof value === \"string\") {\n            lines.push(`${indent}    ${key}: \"${value}\",`);\n          } else if (typeof value === \"boolean\") {\n            lines.push(`${indent}    ${key}: ${value},`);\n          }\n        });\n        lines.push(`${indent}  },`);\n      }\n      \n      // Messages config (excluding render functions)\n      if (hasMessages) {\n        lines.push(`${indent}  messages: {`);\n        Object.entries(config.layout.messages).forEach(([key, value]) => {\n          // Skip render functions\n          if (key === \"renderUserMessage\" || key === \"renderAssistantMessage\") return;\n          \n          if (key === \"avatar\" && typeof value === \"object\" && value !== null) {\n            lines.push(`${indent}    avatar: {`);\n            Object.entries(value as Record<string, unknown>).forEach(([avatarKey, avatarValue]) => {\n              if (typeof avatarValue === \"string\") {\n                lines.push(`${indent}      ${avatarKey}: \"${avatarValue}\",`);\n              } else if (typeof avatarValue === \"boolean\") {\n                lines.push(`${indent}      ${avatarKey}: ${avatarValue},`);\n              }\n            });\n            lines.push(`${indent}    },`);\n          } else if (key === \"timestamp\" && typeof value === \"object\" && value !== null) {\n            // Only emit serializable timestamp properties (skip format function)\n            const hasSerializableTimestamp = Object.entries(value as Record<string, unknown>).some(\n              ([k]) => k !== \"format\"\n            );\n            if (hasSerializableTimestamp) {\n              lines.push(`${indent}    timestamp: {`);\n              Object.entries(value as Record<string, unknown>).forEach(([tsKey, tsValue]) => {\n                if (tsKey === \"format\") return; // Skip format function\n                if (typeof tsValue === \"string\") {\n                  lines.push(`${indent}      ${tsKey}: \"${tsValue}\",`);\n                } else if (typeof tsValue === \"boolean\") {\n                  lines.push(`${indent}      ${tsKey}: ${tsValue},`);\n                }\n              });\n              lines.push(`${indent}    },`);\n            }\n          } else if (typeof value === \"string\") {\n            lines.push(`${indent}    ${key}: \"${value}\",`);\n          } else if (typeof value === \"boolean\") {\n            lines.push(`${indent}    ${key}: ${value},`);\n          }\n        });\n        lines.push(`${indent}  },`);\n      }\n      \n      lines.push(`${indent}},`);\n    }\n  }\n  return lines;\n}\n\n// Helper to generate hook-related config lines\nfunction generateHooksConfig(hooks: CodeGeneratorHooks | undefined, indent: string): string[] {\n  const lines: string[] = [];\n  if (!hooks) return lines;\n\n  if (hooks.getHeaders) {\n    lines.push(`${indent}getHeaders: ${hooks.getHeaders},`);\n  }\n\n  if (hooks.requestMiddleware) {\n    lines.push(`${indent}requestMiddleware: ${hooks.requestMiddleware},`);\n  }\n\n  if (hooks.actionParsers) {\n    lines.push(`${indent}actionParsers: ${hooks.actionParsers},`);\n  }\n\n  if (hooks.actionHandlers) {\n    lines.push(`${indent}actionHandlers: ${hooks.actionHandlers},`);\n  }\n\n  if (hooks.contextProviders) {\n    lines.push(`${indent}contextProviders: ${hooks.contextProviders},`);\n  }\n\n  if (hooks.streamParser) {\n    lines.push(`${indent}streamParser: ${hooks.streamParser},`);\n  }\n\n  return lines;\n}\n\nfunction appendSerializableObjectEntries(\n  lines: string[],\n  value: Record<string, unknown>,\n  indent: string\n): void {\n  Object.entries(value).forEach(([key, entryValue]) => {\n    if (entryValue === undefined || typeof entryValue === \"function\") return;\n\n    if (Array.isArray(entryValue)) {\n      lines.push(`${indent}${key}: ${JSON.stringify(entryValue)},`);\n      return;\n    }\n\n    if (entryValue && typeof entryValue === \"object\") {\n      lines.push(`${indent}${key}: {`);\n      appendSerializableObjectEntries(lines, entryValue as Record<string, unknown>, `${indent}  `);\n      lines.push(`${indent}},`);\n      return;\n    }\n\n    lines.push(`${indent}${key}: ${JSON.stringify(entryValue)},`);\n  });\n}\n\nfunction appendSerializableObjectBlock(\n  lines: string[],\n  key: string,\n  value: Record<string, unknown> | undefined,\n  indent: string\n): void {\n  if (!value) return;\n  lines.push(`${indent}${key}: {`);\n  appendSerializableObjectEntries(lines, value, `${indent}  `);\n  lines.push(`${indent}},`);\n}\n\n/**\n * Resolve the mount-target selector for a single-quoted JS string context.\n * Defaults to `body` and escapes backslashes / single quotes so an arbitrary\n * selector can't break out of the emitted `target: '...'` literal.\n */\nfunction emitTargetSelector(options?: CodeGeneratorOptions): string {\n  return (options?.target ?? \"body\").replace(/\\\\/g, \"\\\\\\\\\").replace(/'/g, \"\\\\'\");\n}\n\nexport function generateCodeSnippet(\n  config: any,\n  format: CodeFormat = \"esm\",\n  options?: CodeGeneratorOptions\n): string {\n  // Remove non-serializable properties\n  const cleanConfig = { ...config };\n  delete cleanConfig.postprocessMessage;\n  delete cleanConfig.initialMessages;\n\n  // Normalize hooks - convert functions to strings via .toString()\n  const normalizedOptions: CodeGeneratorOptions | undefined = options\n    ? { ...options, hooks: normalizeHooks(options.hooks) as CodeGeneratorHooks }\n    : undefined;\n\n  if (format === \"esm\") {\n    return generateESMCode(cleanConfig, normalizedOptions);\n  } else if (format === \"script-installer\") {\n    return generateScriptInstallerCode(cleanConfig, normalizedOptions);\n  } else if (format === \"script-advanced\") {\n    return generateScriptAdvancedCode(cleanConfig, normalizedOptions);\n  } else if (format === \"react-component\") {\n    return generateReactComponentCode(cleanConfig, normalizedOptions);\n  } else if (format === \"react-advanced\") {\n    return generateReactAdvancedCode(cleanConfig, normalizedOptions);\n  } else {\n    return generateScriptManualCode(cleanConfig, normalizedOptions);\n  }\n}\n\nfunction generateESMCode(config: any, options?: CodeGeneratorOptions): string {\n  const hooks = options?.hooks;\n  const parserType = getParserTypeFromConfig(config as AgentWidgetConfig);\n  const shouldEmitParserType = parserType !== \"plain\";\n\n  const lines: string[] = [\n    \"import '@runtypelabs/persona/widget.css';\",\n    \"import { initAgentWidget, markdownPostprocessor } from '@runtypelabs/persona';\",\n    \"\",\n    \"initAgentWidget({\",\n    `  target: '${emitTargetSelector(options)}',`,\n    \"  config: {\"\n  ];\n\n  if (config.apiUrl) lines.push(`    apiUrl: \"${config.apiUrl}\",`);\n  if (config.clientToken) lines.push(`    clientToken: \"${config.clientToken}\",`);\n  if (config.agentId) lines.push(`    agentId: \"${config.agentId}\",`);\n  if (config.target) lines.push(`    target: \"${config.target}\",`);\n  if (config.flowId) lines.push(`    flowId: \"${config.flowId}\",`);\n  if (shouldEmitParserType) lines.push(`    parserType: \"${parserType}\",`);\n\n  if (config.theme && typeof config.theme === \"object\" && Object.keys(config.theme).length > 0) {\n    appendSerializableObjectBlock(lines, \"theme\", config.theme as Record<string, unknown>, \"    \");\n  }\n\n  if (config.launcher) {\n    appendSerializableObjectBlock(lines, \"launcher\", config.launcher, \"    \");\n  }\n\n  if (config.copy) {\n    lines.push(\"    copy: {\");\n    Object.entries(config.copy).forEach(([key, value]) => {\n      lines.push(`      ${key}: \"${value}\",`);\n    });\n    lines.push(\"    },\");\n  }\n\n  if (config.sendButton) {\n    lines.push(\"    sendButton: {\");\n    Object.entries(config.sendButton).forEach(([key, value]) => {\n      if (typeof value === \"string\") {\n        lines.push(`      ${key}: \"${value}\",`);\n      } else if (typeof value === \"boolean\") {\n        lines.push(`      ${key}: ${value},`);\n      }\n    });\n    lines.push(\"    },\");\n  }\n\n  if (config.voiceRecognition) {\n    lines.push(\"    voiceRecognition: {\");\n    Object.entries(config.voiceRecognition).forEach(([key, value]) => {\n      if (typeof value === \"string\") {\n        lines.push(`      ${key}: \"${value}\",`);\n      } else if (typeof value === \"boolean\") {\n        lines.push(`      ${key}: ${value},`);\n      } else if (typeof value === \"number\") {\n        lines.push(`      ${key}: ${value},`);\n      }\n    });\n    lines.push(\"    },\");\n  }\n\n  if (config.statusIndicator) {\n    lines.push(\"    statusIndicator: {\");\n    Object.entries(config.statusIndicator).forEach(([key, value]) => {\n      if (typeof value === \"string\") {\n        lines.push(`      ${key}: \"${value}\",`);\n      } else if (typeof value === \"boolean\") {\n        lines.push(`      ${key}: ${value},`);\n      }\n    });\n    lines.push(\"    },\");\n  }\n\n  if (config.features) {\n    lines.push(\"    features: {\");\n    Object.entries(config.features).forEach(([key, value]) => {\n      lines.push(`      ${key}: ${value},`);\n    });\n    lines.push(\"    },\");\n  }\n\n  if (config.suggestionChips && config.suggestionChips.length > 0) {\n    lines.push(\"    suggestionChips: [\");\n    config.suggestionChips.forEach((chip: string) => {\n      lines.push(`      \"${chip}\",`);\n    });\n    lines.push(\"    ],\");\n  }\n\n  if (config.suggestionChipsConfig) {\n    lines.push(\"    suggestionChipsConfig: {\");\n    if (config.suggestionChipsConfig.fontFamily) {\n      lines.push(`      fontFamily: \"${config.suggestionChipsConfig.fontFamily}\",`);\n    }\n    if (config.suggestionChipsConfig.fontWeight) {\n      lines.push(`      fontWeight: \"${config.suggestionChipsConfig.fontWeight}\",`);\n    }\n    if (config.suggestionChipsConfig.paddingX) {\n      lines.push(`      paddingX: \"${config.suggestionChipsConfig.paddingX}\",`);\n    }\n    if (config.suggestionChipsConfig.paddingY) {\n      lines.push(`      paddingY: \"${config.suggestionChipsConfig.paddingY}\",`);\n    }\n    lines.push(\"    },\");\n  }\n\n  // Add toolCall config\n  lines.push(...generateToolCallConfig(config, \"    \"));\n\n  // Add messageActions config (with hook callbacks if provided)\n  lines.push(...generateMessageActionsConfig(config, \"    \", hooks));\n\n  // Add markdown config\n  lines.push(...generateMarkdownConfig(config, \"    \"));\n\n  // Add layout config\n  lines.push(...generateLayoutConfig(config, \"    \"));\n\n  // Add hook-based config (getHeaders, requestMiddleware, actionParsers, actionHandlers, etc.)\n  lines.push(...generateHooksConfig(hooks, \"    \"));\n\n  if (config.debug) {\n    lines.push(`    debug: ${config.debug},`);\n  }\n\n  // Use custom postprocessMessage if provided, otherwise default\n  if (hooks?.postprocessMessage) {\n    lines.push(`    postprocessMessage: ${hooks.postprocessMessage}`);\n  } else {\n    lines.push(\"    postprocessMessage: ({ text }) => markdownPostprocessor(text)\");\n  }\n  lines.push(\"  }\");\n  lines.push(\"});\");\n\n  return lines.join(\"\\n\");\n}\n\nfunction generateReactComponentCode(config: any, options?: CodeGeneratorOptions): string {\n  const hooks = options?.hooks;\n  const parserType = getParserTypeFromConfig(config as AgentWidgetConfig);\n  const shouldEmitParserType = parserType !== \"plain\";\n\n  const lines: string[] = [\n    \"// ChatWidget.tsx\",\n    \"'use client'; // Required for Next.js - remove for Vite/CRA\",\n    \"\",\n    \"import { useEffect } from 'react';\",\n    \"import '@runtypelabs/persona/widget.css';\",\n    \"import { initAgentWidget, markdownPostprocessor } from '@runtypelabs/persona';\",\n    \"import type { AgentWidgetInitHandle } from '@runtypelabs/persona';\",\n    \"\",\n    \"export function ChatWidget() {\",\n    \"  useEffect(() => {\",\n    \"    let handle: AgentWidgetInitHandle | null = null;\",\n    \"\",\n    \"    handle = initAgentWidget({\",\n    `      target: '${emitTargetSelector(options)}',`,\n    \"      config: {\"\n  ];\n\n  if (config.apiUrl) lines.push(`        apiUrl: \"${config.apiUrl}\",`);\n  if (config.clientToken) lines.push(`        clientToken: \"${config.clientToken}\",`);\n  if (config.agentId) lines.push(`        agentId: \"${config.agentId}\",`);\n  if (config.target) lines.push(`        target: \"${config.target}\",`);\n  if (config.flowId) lines.push(`        flowId: \"${config.flowId}\",`);\n  if (shouldEmitParserType) lines.push(`        parserType: \"${parserType}\",`);\n\n  if (config.theme && typeof config.theme === \"object\" && Object.keys(config.theme).length > 0) {\n    appendSerializableObjectBlock(lines, \"theme\", config.theme as Record<string, unknown>, \"        \");\n  }\n\n  if (config.launcher) {\n    appendSerializableObjectBlock(lines, \"launcher\", config.launcher, \"        \");\n  }\n\n  if (config.copy) {\n    lines.push(\"        copy: {\");\n    Object.entries(config.copy).forEach(([key, value]) => {\n      lines.push(`          ${key}: \"${value}\",`);\n    });\n    lines.push(\"        },\");\n  }\n\n  if (config.sendButton) {\n    lines.push(\"        sendButton: {\");\n    Object.entries(config.sendButton).forEach(([key, value]) => {\n      if (typeof value === \"string\") {\n        lines.push(`          ${key}: \"${value}\",`);\n      } else if (typeof value === \"boolean\") {\n        lines.push(`          ${key}: ${value},`);\n      }\n    });\n    lines.push(\"        },\");\n  }\n\n  if (config.voiceRecognition) {\n    lines.push(\"        voiceRecognition: {\");\n    Object.entries(config.voiceRecognition).forEach(([key, value]) => {\n      if (typeof value === \"string\") {\n        lines.push(`          ${key}: \"${value}\",`);\n      } else if (typeof value === \"boolean\") {\n        lines.push(`          ${key}: ${value},`);\n      } else if (typeof value === \"number\") {\n        lines.push(`          ${key}: ${value},`);\n      }\n    });\n    lines.push(\"        },\");\n  }\n\n  if (config.statusIndicator) {\n    lines.push(\"        statusIndicator: {\");\n    Object.entries(config.statusIndicator).forEach(([key, value]) => {\n      if (typeof value === \"string\") {\n        lines.push(`          ${key}: \"${value}\",`);\n      } else if (typeof value === \"boolean\") {\n        lines.push(`          ${key}: ${value},`);\n      }\n    });\n    lines.push(\"        },\");\n  }\n\n  if (config.features) {\n    lines.push(\"        features: {\");\n    Object.entries(config.features).forEach(([key, value]) => {\n      lines.push(`          ${key}: ${value},`);\n    });\n    lines.push(\"        },\");\n  }\n\n  if (config.suggestionChips && config.suggestionChips.length > 0) {\n    lines.push(\"        suggestionChips: [\");\n    config.suggestionChips.forEach((chip: string) => {\n      lines.push(`          \"${chip}\",`);\n    });\n    lines.push(\"        ],\");\n  }\n\n  if (config.suggestionChipsConfig) {\n    lines.push(\"        suggestionChipsConfig: {\");\n    if (config.suggestionChipsConfig.fontFamily) {\n      lines.push(`          fontFamily: \"${config.suggestionChipsConfig.fontFamily}\",`);\n    }\n    if (config.suggestionChipsConfig.fontWeight) {\n      lines.push(`          fontWeight: \"${config.suggestionChipsConfig.fontWeight}\",`);\n    }\n    if (config.suggestionChipsConfig.paddingX) {\n      lines.push(`          paddingX: \"${config.suggestionChipsConfig.paddingX}\",`);\n    }\n    if (config.suggestionChipsConfig.paddingY) {\n      lines.push(`          paddingY: \"${config.suggestionChipsConfig.paddingY}\",`);\n    }\n    lines.push(\"        },\");\n  }\n\n  // Add toolCall config\n  lines.push(...generateToolCallConfig(config, \"        \"));\n\n  // Add messageActions config (with hook callbacks if provided)\n  lines.push(...generateMessageActionsConfig(config, \"        \", hooks));\n\n  // Add markdown config\n  lines.push(...generateMarkdownConfig(config, \"        \"));\n\n  // Add layout config\n  lines.push(...generateLayoutConfig(config, \"        \"));\n\n  // Add hook-based config (getHeaders, requestMiddleware, actionParsers, actionHandlers, etc.)\n  lines.push(...generateHooksConfig(hooks, \"        \"));\n\n  if (config.debug) {\n    lines.push(`        debug: ${config.debug},`);\n  }\n\n  // Use custom postprocessMessage if provided, otherwise default\n  if (hooks?.postprocessMessage) {\n    lines.push(`        postprocessMessage: ${hooks.postprocessMessage}`);\n  } else {\n    lines.push(\"        postprocessMessage: ({ text }) => markdownPostprocessor(text)\");\n  }\n  lines.push(\"      }\");\n  lines.push(\"    });\");\n  lines.push(\"\");\n  lines.push(\"    // Cleanup on unmount\");\n  lines.push(\"    return () => {\");\n  lines.push(\"      if (handle) {\");\n  lines.push(\"        handle.destroy();\");\n  lines.push(\"      }\");\n  lines.push(\"    };\");\n  lines.push(\"  }, []);\");\n  lines.push(\"\");\n  lines.push(\"  return null; // Widget injects itself into the DOM\");\n  lines.push(\"}\");\n  lines.push(\"\");\n  lines.push(\"// Usage in your app:\");\n  lines.push(\"// import { ChatWidget } from './components/ChatWidget';\");\n  lines.push(\"//\");\n  lines.push(\"// export default function App() {\");\n  lines.push(\"//   return (\");\n  lines.push(\"//     <div>\");\n  lines.push(\"//       {/* Your app content */}\");\n  lines.push(\"//       <ChatWidget />\");\n  lines.push(\"//     </div>\");\n  lines.push(\"//   );\");\n  lines.push(\"// }\");\n\n  return lines.join(\"\\n\");\n}\n\nfunction generateReactAdvancedCode(config: any, options?: CodeGeneratorOptions): string {\n  const hooks = options?.hooks;\n  const lines: string[] = [\n    \"// ChatWidgetAdvanced.tsx\",\n    \"'use client'; // Required for Next.js - remove for Vite/CRA\",\n    \"\",\n    \"import { useEffect } from 'react';\",\n    \"import '@runtypelabs/persona/widget.css';\",\n    \"import {\",\n    \"  initAgentWidget,\",\n    \"  createFlexibleJsonStreamParser,\",\n    \"  defaultJsonActionParser,\",\n    \"  defaultActionHandlers,\",\n    \"  markdownPostprocessor\",\n    \"} from '@runtypelabs/persona';\",\n    \"import type { AgentWidgetInitHandle } from '@runtypelabs/persona';\",\n    \"\",\n    \"const STORAGE_KEY = 'chat-widget-state';\",\n    \"const PROCESSED_ACTIONS_KEY = 'chat-widget-processed-actions';\",\n    \"\",\n    \"// Types for DOM elements\",\n    \"interface PageElement {\",\n    \"  type: string;\",\n    \"  tagName: string;\",\n    \"  selector: string;\",\n    \"  innerText: string;\",\n    \"  href?: string;\",\n    \"}\",\n    \"\",\n    \"interface DOMContext {\",\n    \"  page_elements: PageElement[];\",\n    \"  page_element_count: number;\",\n    \"  element_types: Record<string, number>;\",\n    \"  page_url: string;\",\n    \"  page_title: string;\",\n    \"  timestamp: string;\",\n    \"}\",\n    \"\",\n    \"// DOM context provider - extracts page elements for AI context\",\n    \"const collectDOMContext = (): DOMContext => {\",\n    \"  const selectors = {\",\n    \"    products: '[data-product-id], .product-card, .product-item, [role=\\\"article\\\"]',\",\n    \"    buttons: 'button, [role=\\\"button\\\"], .btn',\",\n    \"    links: 'a[href]',\",\n    \"    inputs: 'input, textarea, select'\",\n    \"  };\",\n    \"\",\n    \"  const elements: PageElement[] = [];\",\n    \"  Object.entries(selectors).forEach(([type, selector]) => {\",\n    \"    document.querySelectorAll(selector).forEach((element) => {\",\n    \"      if (!(element instanceof HTMLElement)) return;\",\n    \"      \",\n    \"      // Exclude elements within the widget\",\n    \"      const widgetHost = element.closest('.persona-host');\",\n    \"      if (widgetHost) return;\",\n    \"      \",\n    \"      const text = element.innerText?.trim();\",\n    \"      if (!text) return;\",\n    \"\",\n    \"      const selectorString =\",\n    \"        element.id ? `#${element.id}` :\",\n    \"        element.getAttribute('data-testid') ? `[data-testid=\\\"${element.getAttribute('data-testid')}\\\"]` :\",\n    \"        element.getAttribute('data-product-id') ? `[data-product-id=\\\"${element.getAttribute('data-product-id')}\\\"]` :\",\n    \"        element.tagName.toLowerCase();\",\n    \"\",\n    \"      const elementData: PageElement = {\",\n    \"        type,\",\n    \"        tagName: element.tagName.toLowerCase(),\",\n    \"        selector: selectorString,\",\n    \"        innerText: text.substring(0, 200)\",\n    \"      };\",\n    \"\",\n    \"      if (type === 'links' && element instanceof HTMLAnchorElement && element.href) {\",\n    \"        elementData.href = element.href;\",\n    \"      }\",\n    \"\",\n    \"      elements.push(elementData);\",\n    \"    });\",\n    \"  });\",\n    \"\",\n    \"  const counts = elements.reduce((acc, el) => {\",\n    \"    acc[el.type] = (acc[el.type] || 0) + 1;\",\n    \"    return acc;\",\n    \"  }, {} as Record<string, number>);\",\n    \"\",\n    \"  return {\",\n    \"    page_elements: elements.slice(0, 50),\",\n    \"    page_element_count: elements.length,\",\n    \"    element_types: counts,\",\n    \"    page_url: window.location.href,\",\n    \"    page_title: document.title,\",\n    \"    timestamp: new Date().toISOString()\",\n    \"  };\",\n    \"};\",\n    \"\",\n    \"export function ChatWidgetAdvanced() {\",\n    \"  useEffect(() => {\",\n    \"    let handle: AgentWidgetInitHandle | null = null;\",\n    \"\",\n    \"    // Load saved state\",\n    \"    const loadSavedMessages = () => {\",\n    \"      const savedState = localStorage.getItem(STORAGE_KEY);\",\n    \"      if (savedState) {\",\n    \"        try {\",\n    \"          const { messages } = JSON.parse(savedState);\",\n    \"          return messages || [];\",\n    \"        } catch (e) {\",\n    \"          console.error('Failed to load saved state:', e);\",\n    \"        }\",\n    \"      }\",\n    \"      return [];\",\n    \"    };\",\n    \"\",\n    \"    handle = initAgentWidget({\",\n    `      target: '${emitTargetSelector(options)}',`,\n    \"      config: {\"\n  ];\n\n  if (config.apiUrl) lines.push(`        apiUrl: \"${config.apiUrl}\",`);\n  if (config.clientToken) lines.push(`        clientToken: \"${config.clientToken}\",`);\n  if (config.agentId) lines.push(`        agentId: \"${config.agentId}\",`);\n  if (config.target) lines.push(`        target: \"${config.target}\",`);\n  if (config.flowId) lines.push(`        flowId: \"${config.flowId}\",`);\n\n  if (config.theme && typeof config.theme === \"object\" && Object.keys(config.theme).length > 0) {\n    appendSerializableObjectBlock(lines, \"theme\", config.theme as Record<string, unknown>, \"        \");\n  }\n\n  if (config.launcher) {\n    appendSerializableObjectBlock(lines, \"launcher\", config.launcher, \"        \");\n  }\n\n  if (config.copy) {\n    lines.push(\"        copy: {\");\n    Object.entries(config.copy).forEach(([key, value]) => {\n      lines.push(`          ${key}: \"${value}\",`);\n    });\n    lines.push(\"        },\");\n  }\n\n  if (config.sendButton) {\n    lines.push(\"        sendButton: {\");\n    Object.entries(config.sendButton).forEach(([key, value]) => {\n      if (typeof value === \"string\") {\n        lines.push(`          ${key}: \"${value}\",`);\n      } else if (typeof value === \"boolean\") {\n        lines.push(`          ${key}: ${value},`);\n      }\n    });\n    lines.push(\"        },\");\n  }\n\n  if (config.voiceRecognition) {\n    lines.push(\"        voiceRecognition: {\");\n    Object.entries(config.voiceRecognition).forEach(([key, value]) => {\n      if (typeof value === \"string\") {\n        lines.push(`          ${key}: \"${value}\",`);\n      } else if (typeof value === \"boolean\") {\n        lines.push(`          ${key}: ${value},`);\n      } else if (typeof value === \"number\") {\n        lines.push(`          ${key}: ${value},`);\n      }\n    });\n    lines.push(\"        },\");\n  }\n\n  if (config.statusIndicator) {\n    lines.push(\"        statusIndicator: {\");\n    Object.entries(config.statusIndicator).forEach(([key, value]) => {\n      if (typeof value === \"string\") {\n        lines.push(`          ${key}: \"${value}\",`);\n      } else if (typeof value === \"boolean\") {\n        lines.push(`          ${key}: ${value},`);\n      }\n    });\n    lines.push(\"        },\");\n  }\n\n  if (config.features) {\n    lines.push(\"        features: {\");\n    Object.entries(config.features).forEach(([key, value]) => {\n      lines.push(`          ${key}: ${value},`);\n    });\n    lines.push(\"        },\");\n  }\n\n  if (config.suggestionChips && config.suggestionChips.length > 0) {\n    lines.push(\"        suggestionChips: [\");\n    config.suggestionChips.forEach((chip: string) => {\n      lines.push(`          \"${chip}\",`);\n    });\n    lines.push(\"        ],\");\n  }\n\n  if (config.suggestionChipsConfig) {\n    lines.push(\"        suggestionChipsConfig: {\");\n    if (config.suggestionChipsConfig.fontFamily) {\n      lines.push(`          fontFamily: \"${config.suggestionChipsConfig.fontFamily}\",`);\n    }\n    if (config.suggestionChipsConfig.fontWeight) {\n      lines.push(`          fontWeight: \"${config.suggestionChipsConfig.fontWeight}\",`);\n    }\n    if (config.suggestionChipsConfig.paddingX) {\n      lines.push(`          paddingX: \"${config.suggestionChipsConfig.paddingX}\",`);\n    }\n    if (config.suggestionChipsConfig.paddingY) {\n      lines.push(`          paddingY: \"${config.suggestionChipsConfig.paddingY}\",`);\n    }\n    lines.push(\"        },\");\n  }\n\n  // Add toolCall config\n  lines.push(...generateToolCallConfig(config, \"        \"));\n\n  // Add messageActions config (with hook callbacks if provided)\n  lines.push(...generateMessageActionsConfig(config, \"        \", hooks));\n\n  // Add markdown config\n  lines.push(...generateMarkdownConfig(config, \"        \"));\n\n  // Add layout config\n  lines.push(...generateLayoutConfig(config, \"        \"));\n\n  // Add getHeaders if provided\n  if (hooks?.getHeaders) {\n    lines.push(`        getHeaders: ${hooks.getHeaders},`);\n  }\n\n  // Add contextProviders if provided\n  if (hooks?.contextProviders) {\n    lines.push(`        contextProviders: ${hooks.contextProviders},`);\n  }\n\n  if (config.debug) {\n    lines.push(`        debug: ${config.debug},`);\n  }\n\n  lines.push(\"        initialMessages: loadSavedMessages(),\");\n\n  // Stream parser - use custom if provided, otherwise default\n  if (hooks?.streamParser) {\n    lines.push(`        streamParser: ${hooks.streamParser},`);\n  } else {\n    lines.push(\"        // Flexible JSON stream parser for handling structured actions\");\n    lines.push(`        streamParser: () => createFlexibleJsonStreamParser(${TEMPLATE_STREAM_PARSER_CALLBACK_TS}),`);\n  }\n\n  // Action parsers - merge custom with defaults if provided\n  if (hooks?.actionParsers) {\n    lines.push(\"        // Action parsers (custom merged with defaults)\");\n    lines.push(`        actionParsers: [...(${hooks.actionParsers}), defaultJsonActionParser,`);\n    lines.push(`          // Built-in parser for markdown-wrapped JSON`);\n    lines.push(`          ${TEMPLATE_MARKDOWN_JSON_PARSER_TS}`);\n    lines.push(\"        ],\");\n  } else {\n    lines.push(\"        // Action parsers to detect JSON actions in responses\");\n    lines.push(\"        actionParsers: [\");\n    lines.push(\"          defaultJsonActionParser,\");\n    lines.push(`          // Parser for markdown-wrapped JSON`);\n    lines.push(`          ${TEMPLATE_MARKDOWN_JSON_PARSER_TS}`);\n    lines.push(\"        ],\");\n  }\n\n  // Action handlers - merge custom with defaults if provided\n  if (hooks?.actionHandlers) {\n    lines.push(\"        // Action handlers (custom merged with defaults)\");\n    lines.push(`        actionHandlers: [...(${hooks.actionHandlers}),`);\n    lines.push(\"          defaultActionHandlers.message,\");\n    lines.push(\"          defaultActionHandlers.messageAndClick,\");\n    lines.push(`          // Built-in handler for nav_then_click action`);\n    lines.push(`          ${TEMPLATE_NAV_THEN_CLICK_HANDLER_TS}`);\n    lines.push(\"        ],\");\n  } else {\n    lines.push(\"        // Action handlers for navigation and other actions\");\n    lines.push(\"        actionHandlers: [\");\n    lines.push(\"          defaultActionHandlers.message,\");\n    lines.push(\"          defaultActionHandlers.messageAndClick,\");\n    lines.push(`          // Handler for nav_then_click action`);\n    lines.push(`          ${TEMPLATE_NAV_THEN_CLICK_HANDLER_TS}`);\n    lines.push(\"        ],\");\n  }\n\n  // postprocessMessage - use custom if provided, otherwise default\n  if (hooks?.postprocessMessage) {\n    lines.push(`        postprocessMessage: ${hooks.postprocessMessage},`);\n  } else {\n    lines.push(\"        postprocessMessage: ({ text }) => markdownPostprocessor(text),\");\n  }\n\n  // requestMiddleware - merge custom with DOM context if provided\n  if (hooks?.requestMiddleware) {\n    lines.push(\"        // Request middleware (custom merged with DOM context)\");\n    lines.push(\"        requestMiddleware: ({ payload, config }) => {\");\n    lines.push(`          const customResult = (${hooks.requestMiddleware})({ payload, config });`);\n    lines.push(\"          const merged = customResult || payload;\");\n    lines.push(\"          return {\");\n    lines.push(\"            ...merged,\");\n    lines.push(\"            metadata: { ...merged.metadata, ...collectDOMContext() }\");\n    lines.push(\"          };\");\n    lines.push(\"        }\");\n  } else {\n    lines.push(\"        requestMiddleware: ({ payload }) => {\");\n    lines.push(\"          return {\");\n    lines.push(\"            ...payload,\");\n    lines.push(\"            metadata: collectDOMContext()\");\n    lines.push(\"          };\");\n    lines.push(\"        }\");\n  }\n  lines.push(\"      }\");\n  lines.push(\"    });\");\n  lines.push(\"\");\n  lines.push(\"    // Save state on message events\");\n  lines.push(\"    const handleMessage = () => {\");\n  lines.push(\"      const session = handle?.getSession?.();\");\n  lines.push(\"      if (session) {\");\n  lines.push(\"        localStorage.setItem(STORAGE_KEY, JSON.stringify({\");\n  lines.push(\"          messages: session.messages,\");\n  lines.push(\"          timestamp: new Date().toISOString()\");\n  lines.push(\"        }));\");\n  lines.push(\"      }\");\n  lines.push(\"    };\");\n  lines.push(\"\");\n  lines.push(\"    // Clear state on clear chat\");\n  lines.push(\"    const handleClearChat = () => {\");\n  lines.push(\"      localStorage.removeItem(STORAGE_KEY);\");\n  lines.push(\"      localStorage.removeItem(PROCESSED_ACTIONS_KEY);\");\n  lines.push(\"    };\");\n  lines.push(\"\");\n  lines.push(\"    window.addEventListener('persona:message', handleMessage);\");\n  lines.push(\"    window.addEventListener('persona:clear-chat', handleClearChat);\");\n  lines.push(\"\");\n  lines.push(\"    // Cleanup on unmount\");\n  lines.push(\"    return () => {\");\n  lines.push(\"      window.removeEventListener('persona:message', handleMessage);\");\n  lines.push(\"      window.removeEventListener('persona:clear-chat', handleClearChat);\");\n  lines.push(\"      if (handle) {\");\n  lines.push(\"        handle.destroy();\");\n  lines.push(\"      }\");\n  lines.push(\"    };\");\n  lines.push(\"  }, []);\");\n  lines.push(\"\");\n  lines.push(\"  return null; // Widget injects itself into the DOM\");\n  lines.push(\"}\");\n  lines.push(\"\");\n  lines.push(\"// Usage: Collects DOM context for AI-powered navigation\");\n  lines.push(\"// Features:\");\n  lines.push(\"// - Extracts page elements (products, buttons, links)\");\n  lines.push(\"// - Persists chat history across page loads\");\n  lines.push(\"// - Handles navigation actions (nav_then_click)\");\n  lines.push(\"// - Processes structured JSON actions from AI\");\n  lines.push(\"//\");\n  lines.push(\"// Example usage in Next.js:\");\n  lines.push(\"// import { ChatWidgetAdvanced } from './components/ChatWidgetAdvanced';\");\n  lines.push(\"//\");\n  lines.push(\"// export default function RootLayout({ children }) {\");\n  lines.push(\"//   return (\");\n  lines.push(\"//     <html lang=\\\"en\\\">\");\n  lines.push(\"//       <body>\");\n  lines.push(\"//         {children}\");\n  lines.push(\"//         <ChatWidgetAdvanced />\");\n  lines.push(\"//       </body>\");\n  lines.push(\"//     </html>\");\n  lines.push(\"//   );\");\n  lines.push(\"// }\");\n\n  return lines.join(\"\\n\");\n}\n\n// Helper to build a serializable config object for JSON export\nfunction buildSerializableConfig(config: any): Record<string, any> {\n  const parserType = getParserTypeFromConfig(config as AgentWidgetConfig);\n  const shouldEmitParserType = parserType !== \"plain\";\n  \n  const serializableConfig: Record<string, any> = {};\n  \n  if (config.apiUrl) serializableConfig.apiUrl = config.apiUrl;\n  if (config.clientToken) serializableConfig.clientToken = config.clientToken;\n  if (config.agentId) serializableConfig.agentId = config.agentId;\n  if (config.target) serializableConfig.target = config.target;\n  if (config.flowId) serializableConfig.flowId = config.flowId;\n  if (shouldEmitParserType) serializableConfig.parserType = parserType;\n  if (config.theme) serializableConfig.theme = config.theme;\n  if (config.launcher) serializableConfig.launcher = config.launcher;\n  if (config.copy) serializableConfig.copy = config.copy;\n  if (config.sendButton) serializableConfig.sendButton = config.sendButton;\n  if (config.voiceRecognition) serializableConfig.voiceRecognition = config.voiceRecognition;\n  if (config.statusIndicator) serializableConfig.statusIndicator = config.statusIndicator;\n  if (config.features) serializableConfig.features = config.features;\n  if (config.suggestionChips?.length > 0) serializableConfig.suggestionChips = config.suggestionChips;\n  if (config.suggestionChipsConfig) serializableConfig.suggestionChipsConfig = config.suggestionChipsConfig;\n  if (config.debug) serializableConfig.debug = config.debug;\n  \n  // Add toolCall config (only serializable parts)\n  if (config.toolCall) {\n    const toolCallConfig: Record<string, any> = {};\n    Object.entries(config.toolCall).forEach(([key, value]) => {\n      if (typeof value === \"string\") toolCallConfig[key] = value;\n    });\n    if (Object.keys(toolCallConfig).length > 0) {\n      serializableConfig.toolCall = toolCallConfig;\n    }\n  }\n  \n  // Add messageActions config (excluding callbacks)\n  if (config.messageActions) {\n    const messageActionsConfig: Record<string, any> = {};\n    Object.entries(config.messageActions).forEach(([key, value]) => {\n      if (key !== \"onFeedback\" && key !== \"onCopy\" && value !== undefined) {\n        if (typeof value === \"string\" || typeof value === \"boolean\") {\n          messageActionsConfig[key] = value;\n        }\n      }\n    });\n    if (Object.keys(messageActionsConfig).length > 0) {\n      serializableConfig.messageActions = messageActionsConfig;\n    }\n  }\n  \n  // Add markdown config (excluding renderer functions)\n  if (config.markdown) {\n    const markdownConfig: Record<string, any> = {};\n    if (config.markdown.options) markdownConfig.options = config.markdown.options;\n    if (config.markdown.disableDefaultStyles !== undefined) {\n      markdownConfig.disableDefaultStyles = config.markdown.disableDefaultStyles;\n    }\n    if (Object.keys(markdownConfig).length > 0) {\n      serializableConfig.markdown = markdownConfig;\n    }\n  }\n  \n  // Add layout config (excluding render functions)\n  if (config.layout) {\n    const layoutConfig: Record<string, any> = {};\n    \n    if (config.layout.header) {\n      const headerConfig: Record<string, any> = {};\n      Object.entries(config.layout.header).forEach(([key, value]) => {\n        if (key !== \"render\" && (typeof value === \"string\" || typeof value === \"boolean\")) {\n          headerConfig[key] = value;\n        }\n      });\n      if (Object.keys(headerConfig).length > 0) {\n        layoutConfig.header = headerConfig;\n      }\n    }\n    \n    if (config.layout.messages) {\n      const messagesConfig: Record<string, any> = {};\n      Object.entries(config.layout.messages).forEach(([key, value]) => {\n        if (key !== \"renderUserMessage\" && key !== \"renderAssistantMessage\") {\n          if (key === \"avatar\" && typeof value === \"object\" && value !== null) {\n            messagesConfig.avatar = value;\n          } else if (key === \"timestamp\" && typeof value === \"object\" && value !== null) {\n            // Exclude format function\n            const tsConfig: Record<string, any> = {};\n            Object.entries(value as Record<string, unknown>).forEach(([tsKey, tsValue]) => {\n              if (tsKey !== \"format\" && (typeof tsValue === \"string\" || typeof tsValue === \"boolean\")) {\n                tsConfig[tsKey] = tsValue;\n              }\n            });\n            if (Object.keys(tsConfig).length > 0) {\n              messagesConfig.timestamp = tsConfig;\n            }\n          } else if (typeof value === \"string\" || typeof value === \"boolean\") {\n            messagesConfig[key] = value;\n          }\n        }\n      });\n      if (Object.keys(messagesConfig).length > 0) {\n        layoutConfig.messages = messagesConfig;\n      }\n    }\n    \n    if (Object.keys(layoutConfig).length > 0) {\n      serializableConfig.layout = layoutConfig;\n    }\n  }\n  \n  return serializableConfig;\n}\n\nfunction generateScriptInstallerCode(config: any, options?: CodeGeneratorOptions): string {\n  const serializableConfig = buildSerializableConfig(config);\n\n  // `windowKey` and `target` are INSTALLER options, not widget-config fields.\n  // install.ts reads them from the top level of data-config: the nested-`config`\n  // branch hoists every sibling key (Object.assign(scriptConfig, parsed)), while\n  // a flat payload becomes the widget config wholesale and its siblings are\n  // ignored. So whenever either is set we emit the nested `{ config, ...}` form\n  // with the option as a SIBLING of `config`: nesting `target` inside the\n  // widget config would leave the widget mounted on `body`.\n  const needsWrapper = Boolean(options?.windowKey || options?.target);\n  const payload = needsWrapper\n    ? {\n        config: serializableConfig,\n        ...(options?.windowKey ? { windowKey: options.windowKey } : {}),\n        ...(options?.target ? { target: options.target } : {}),\n      }\n    : serializableConfig;\n\n  // Escape single quotes in JSON for HTML attribute\n  const configJson = JSON.stringify(payload, null, 0).replace(/'/g, \"&#39;\");\n\n  return `<script src=\"https://cdn.jsdelivr.net/npm/@runtypelabs/persona@${VERSION}/dist/install.global.js\" data-config='${configJson}'></script>`;\n}\n\nfunction generateScriptManualCode(config: any, options?: CodeGeneratorOptions): string {\n  const hooks = options?.hooks;\n  const parserType = getParserTypeFromConfig(config as AgentWidgetConfig);\n  const shouldEmitParserType = parserType !== \"plain\";\n\n  const lines: string[] = [\n    \"<!-- Load CSS -->\",\n    `<link rel=\"stylesheet\" href=\"https://cdn.jsdelivr.net/npm/@runtypelabs/persona@${VERSION}/dist/widget.css\" />`,\n    \"\",\n    \"<!-- Load JavaScript -->\",\n    `<script src=\"https://cdn.jsdelivr.net/npm/@runtypelabs/persona@${VERSION}/dist/index.global.js\"></script>`,\n    \"\",\n    \"<!-- Initialize widget -->\",\n    \"<script>\",\n    \"  var handle = window.AgentWidget.initAgentWidget({\",\n    `    target: '${emitTargetSelector(options)}',`,\n    ...(options?.windowKey ? [`    windowKey: '${options.windowKey}',`] : []),\n    \"    config: {\"\n  ];\n\n  if (config.apiUrl) lines.push(`      apiUrl: \"${config.apiUrl}\",`);\n  if (config.clientToken) lines.push(`      clientToken: \"${config.clientToken}\",`);\n  if (config.agentId) lines.push(`      agentId: \"${config.agentId}\",`);\n  if (config.target) lines.push(`      target: \"${config.target}\",`);\n  if (config.flowId) lines.push(`      flowId: \"${config.flowId}\",`);\n  if (shouldEmitParserType) lines.push(`      parserType: \"${parserType}\",`);\n\n  if (config.theme && typeof config.theme === \"object\" && Object.keys(config.theme).length > 0) {\n    appendSerializableObjectBlock(lines, \"theme\", config.theme as Record<string, unknown>, \"      \");\n  }\n\n  if (config.launcher) {\n    appendSerializableObjectBlock(lines, \"launcher\", config.launcher, \"      \");\n  }\n\n  if (config.copy) {\n    lines.push(\"      copy: {\");\n    Object.entries(config.copy).forEach(([key, value]) => {\n      lines.push(`        ${key}: \"${value}\",`);\n    });\n    lines.push(\"      },\");\n  }\n\n  if (config.sendButton) {\n    lines.push(\"      sendButton: {\");\n    Object.entries(config.sendButton).forEach(([key, value]) => {\n      if (typeof value === \"string\") {\n        lines.push(`        ${key}: \"${value}\",`);\n      } else if (typeof value === \"boolean\") {\n        lines.push(`        ${key}: ${value},`);\n      }\n    });\n    lines.push(\"      },\");\n  }\n\n  if (config.voiceRecognition) {\n    lines.push(\"      voiceRecognition: {\");\n    Object.entries(config.voiceRecognition).forEach(([key, value]) => {\n      if (typeof value === \"string\") {\n        lines.push(`        ${key}: \"${value}\",`);\n      } else if (typeof value === \"boolean\") {\n        lines.push(`        ${key}: ${value},`);\n      } else if (typeof value === \"number\") {\n        lines.push(`        ${key}: ${value},`);\n      }\n    });\n    lines.push(\"      },\");\n  }\n\n  if (config.statusIndicator) {\n    lines.push(\"      statusIndicator: {\");\n    Object.entries(config.statusIndicator).forEach(([key, value]) => {\n      if (typeof value === \"string\") {\n        lines.push(`        ${key}: \"${value}\",`);\n      } else if (typeof value === \"boolean\") {\n        lines.push(`        ${key}: ${value},`);\n      }\n    });\n    lines.push(\"      },\");\n  }\n\n  if (config.features) {\n    lines.push(\"      features: {\");\n    Object.entries(config.features).forEach(([key, value]) => {\n      lines.push(`        ${key}: ${value},`);\n    });\n    lines.push(\"      },\");\n  }\n\n  if (config.suggestionChips && config.suggestionChips.length > 0) {\n    lines.push(\"      suggestionChips: [\");\n    config.suggestionChips.forEach((chip: string) => {\n      lines.push(`        \"${chip}\",`);\n    });\n    lines.push(\"      ],\");\n  }\n\n  if (config.suggestionChipsConfig) {\n    lines.push(\"      suggestionChipsConfig: {\");\n    if (config.suggestionChipsConfig.fontFamily) {\n      lines.push(`        fontFamily: \"${config.suggestionChipsConfig.fontFamily}\",`);\n    }\n    if (config.suggestionChipsConfig.fontWeight) {\n      lines.push(`        fontWeight: \"${config.suggestionChipsConfig.fontWeight}\",`);\n    }\n    if (config.suggestionChipsConfig.paddingX) {\n      lines.push(`        paddingX: \"${config.suggestionChipsConfig.paddingX}\",`);\n    }\n    if (config.suggestionChipsConfig.paddingY) {\n      lines.push(`        paddingY: \"${config.suggestionChipsConfig.paddingY}\",`);\n    }\n    lines.push(\"      },\");\n  }\n\n  // Add toolCall config\n  lines.push(...generateToolCallConfig(config, \"      \"));\n\n  // Add messageActions config (with hook callbacks if provided)\n  lines.push(...generateMessageActionsConfig(config, \"      \", hooks));\n\n  // Add markdown config\n  lines.push(...generateMarkdownConfig(config, \"      \"));\n\n  // Add layout config\n  lines.push(...generateLayoutConfig(config, \"      \"));\n\n  // Add hook-based config (getHeaders, requestMiddleware, actionParsers, actionHandlers, etc.)\n  lines.push(...generateHooksConfig(hooks, \"      \"));\n\n  if (config.debug) {\n    lines.push(`      debug: ${config.debug},`);\n  }\n\n  // Use custom postprocessMessage if provided, otherwise default\n  if (hooks?.postprocessMessage) {\n    lines.push(`      postprocessMessage: ${hooks.postprocessMessage}`);\n  } else {\n    lines.push(\"      postprocessMessage: ({ text }) => window.AgentWidget.markdownPostprocessor(text)\");\n  }\n  lines.push(\"    }\");\n  lines.push(\"  });\");\n  lines.push(\"</script>\");\n\n  return lines.join(\"\\n\");\n}\n\nfunction generateScriptAdvancedCode(config: any, options?: CodeGeneratorOptions): string {\n  const hooks = options?.hooks;\n  const serializableConfig = buildSerializableConfig(config);\n  const configJson = JSON.stringify(serializableConfig, null, 2);\n\n  const lines: string[] = [\n    \"<script>\",\n    \"(function() {\",\n    \"  'use strict';\",\n    \"\",\n    \"  // Configuration\",\n    `  var CONFIG = ${configJson.split('\\n').map((line, i) => i === 0 ? line : '  ' + line).join('\\n')};`,\n    \"\",\n    \"  // Constants\",\n    `  var CDN_BASE = 'https://cdn.jsdelivr.net/npm/@runtypelabs/persona@${VERSION}/dist';`,\n    \"  var STORAGE_KEY = 'chat-widget-state';\",\n    \"  var PROCESSED_ACTIONS_KEY = 'chat-widget-processed-actions';\",\n    \"\",\n    \"  // DOM context provider - extracts page elements for AI context\",\n    \"  var domContextProvider = function() {\",\n    \"    var selectors = {\",\n    \"      products: '[data-product-id], .product-card, .product-item, [role=\\\"article\\\"]',\",\n    \"      buttons: 'button, [role=\\\"button\\\"], .btn',\",\n    \"      links: 'a[href]',\",\n    \"      inputs: 'input, textarea, select'\",\n    \"    };\",\n    \"\",\n    \"    var elements = [];\",\n    \"    Object.entries(selectors).forEach(function(entry) {\",\n    \"      var type = entry[0], selector = entry[1];\",\n    \"      document.querySelectorAll(selector).forEach(function(element) {\",\n    \"        if (!(element instanceof HTMLElement)) return;\",\n    \"        var widgetHost = element.closest('.persona-host');\",\n    \"        if (widgetHost) return;\",\n    \"        var text = element.innerText ? element.innerText.trim() : '';\",\n    \"        if (!text) return;\",\n    \"\",\n    \"        var selectorString = element.id ? '#' + element.id :\",\n    \"          element.getAttribute('data-testid') ? '[data-testid=\\\"' + element.getAttribute('data-testid') + '\\\"]' :\",\n    \"          element.getAttribute('data-product-id') ? '[data-product-id=\\\"' + element.getAttribute('data-product-id') + '\\\"]' :\",\n    \"          element.tagName.toLowerCase();\",\n    \"\",\n    \"        var elementData = {\",\n    \"          type: type,\",\n    \"          tagName: element.tagName.toLowerCase(),\",\n    \"          selector: selectorString,\",\n    \"          innerText: text.substring(0, 200)\",\n    \"        };\",\n    \"\",\n    \"        if (type === 'links' && element instanceof HTMLAnchorElement && element.href) {\",\n    \"          elementData.href = element.href;\",\n    \"        }\",\n    \"        elements.push(elementData);\",\n    \"      });\",\n    \"    });\",\n    \"\",\n    \"    var counts = elements.reduce(function(acc, el) {\",\n    \"      acc[el.type] = (acc[el.type] || 0) + 1;\",\n    \"      return acc;\",\n    \"    }, {});\",\n    \"\",\n    \"    return {\",\n    \"      page_elements: elements.slice(0, 50),\",\n    \"      page_element_count: elements.length,\",\n    \"      element_types: counts,\",\n    \"      page_url: window.location.href,\",\n    \"      page_title: document.title,\",\n    \"      timestamp: new Date().toISOString()\",\n    \"    };\",\n    \"  };\",\n    \"\",\n    \"  // Load CSS dynamically\",\n    \"  var loadCSS = function() {\",\n    \"    if (document.querySelector('link[data-persona]')) return;\",\n    \"    var link = document.createElement('link');\",\n    \"    link.rel = 'stylesheet';\",\n    \"    link.href = CDN_BASE + '/widget.css';\",\n    \"    link.setAttribute('data-persona', 'true');\",\n    \"    document.head.appendChild(link);\",\n    \"  };\",\n    \"\",\n    \"  // Load JS dynamically\",\n    \"  var loadJS = function(callback) {\",\n    \"    if (window.AgentWidget) { callback(); return; }\",\n    \"    var script = document.createElement('script');\",\n    \"    script.src = CDN_BASE + '/index.global.js';\",\n    \"    script.onload = callback;\",\n    \"    script.onerror = function() { console.error('Failed to load AgentWidget'); };\",\n    \"    document.head.appendChild(script);\",\n    \"  };\",\n    \"\",\n    \"  // Create widget config with advanced features\",\n    \"  var createWidgetConfig = function(agentWidget) {\",\n    \"    var widgetConfig = Object.assign({}, CONFIG);\",\n    \"\"\n  ];\n\n  // Add getHeaders if provided\n  if (hooks?.getHeaders) {\n    lines.push(`    widgetConfig.getHeaders = ${hooks.getHeaders};`);\n    lines.push(\"\");\n  }\n\n  // Add contextProviders if provided\n  if (hooks?.contextProviders) {\n    lines.push(`    widgetConfig.contextProviders = ${hooks.contextProviders};`);\n    lines.push(\"\");\n  }\n\n  // Stream parser - use custom if provided, otherwise default\n  if (hooks?.streamParser) {\n    lines.push(`    widgetConfig.streamParser = ${hooks.streamParser};`);\n  } else {\n    lines.push(\"    // Flexible JSON stream parser for handling structured actions\");\n    lines.push(\"    widgetConfig.streamParser = function() {\");\n    lines.push(`      return agentWidget.createFlexibleJsonStreamParser(${TEMPLATE_STREAM_PARSER_CALLBACK_ES5});`);\n    lines.push(\"    };\");\n  }\n  lines.push(\"\");\n\n  // Action parsers - merge custom with defaults if provided\n  if (hooks?.actionParsers) {\n    lines.push(\"    // Action parsers (custom merged with defaults)\");\n    lines.push(`    var customParsers = ${hooks.actionParsers};`);\n    lines.push(\"    widgetConfig.actionParsers = customParsers.concat([\");\n    lines.push(\"      agentWidget.defaultJsonActionParser,\");\n    lines.push(`      ${TEMPLATE_MARKDOWN_JSON_PARSER_ES5}`);\n    lines.push(\"    ]);\");\n  } else {\n    lines.push(\"    // Action parsers to detect JSON actions in responses\");\n    lines.push(\"    widgetConfig.actionParsers = [\");\n    lines.push(\"      agentWidget.defaultJsonActionParser,\");\n    lines.push(`      ${TEMPLATE_MARKDOWN_JSON_PARSER_ES5}`);\n    lines.push(\"    ];\");\n  }\n  lines.push(\"\");\n\n  // Action handlers - merge custom with defaults if provided\n  if (hooks?.actionHandlers) {\n    lines.push(\"    // Action handlers (custom merged with defaults)\");\n    lines.push(`    var customHandlers = ${hooks.actionHandlers};`);\n    lines.push(\"    widgetConfig.actionHandlers = customHandlers.concat([\");\n    lines.push(\"      agentWidget.defaultActionHandlers.message,\");\n    lines.push(\"      agentWidget.defaultActionHandlers.messageAndClick,\");\n    lines.push(`      ${TEMPLATE_NAV_THEN_CLICK_HANDLER_ES5}`);\n    lines.push(\"    ]);\");\n  } else {\n    lines.push(\"    // Action handlers for navigation and other actions\");\n    lines.push(\"    widgetConfig.actionHandlers = [\");\n    lines.push(\"      agentWidget.defaultActionHandlers.message,\");\n    lines.push(\"      agentWidget.defaultActionHandlers.messageAndClick,\");\n    lines.push(`      ${TEMPLATE_NAV_THEN_CLICK_HANDLER_ES5}`);\n    lines.push(\"    ];\");\n  }\n  lines.push(\"\");\n\n  // requestMiddleware - merge custom with DOM context if provided\n  if (hooks?.requestMiddleware) {\n    lines.push(\"    // Request middleware (custom merged with DOM context)\");\n    lines.push(\"    widgetConfig.requestMiddleware = function(ctx) {\");\n    lines.push(`      var customResult = (${hooks.requestMiddleware})(ctx);`);\n    lines.push(\"      var merged = customResult || ctx.payload;\");\n    lines.push(\"      return Object.assign({}, merged, { metadata: Object.assign({}, merged.metadata, domContextProvider()) });\");\n    lines.push(\"    };\");\n  } else {\n    lines.push(\"    // Send DOM context with each request\");\n    lines.push(\"    widgetConfig.requestMiddleware = function(ctx) {\");\n    lines.push(\"      return Object.assign({}, ctx.payload, { metadata: domContextProvider() });\");\n    lines.push(\"    };\");\n  }\n  lines.push(\"\");\n\n  // postprocessMessage - use custom if provided, otherwise default\n  if (hooks?.postprocessMessage) {\n    lines.push(`    widgetConfig.postprocessMessage = ${hooks.postprocessMessage};`);\n  } else {\n    lines.push(\"    // Markdown postprocessor\");\n    lines.push(\"    widgetConfig.postprocessMessage = function(ctx) {\");\n    lines.push(\"      return agentWidget.markdownPostprocessor(ctx.text);\");\n    lines.push(\"    };\");\n  }\n  lines.push(\"\");\n\n  // Add messageActions callbacks if provided\n  if (hooks?.onFeedback || hooks?.onCopy) {\n    lines.push(\"    // Message action callbacks\");\n    lines.push(\"    widgetConfig.messageActions = widgetConfig.messageActions || {};\");\n    if (hooks?.onFeedback) {\n      lines.push(`    widgetConfig.messageActions.onFeedback = ${hooks.onFeedback};`);\n    }\n    if (hooks?.onCopy) {\n      lines.push(`    widgetConfig.messageActions.onCopy = ${hooks.onCopy};`);\n    }\n    lines.push(\"\");\n  }\n\n  lines.push(...[\n    \"    return widgetConfig;\",\n    \"  };\",\n    \"\",\n    \"  // Initialize widget\",\n    \"  var init = function() {\",\n    \"    var agentWidget = window.AgentWidget;\",\n    \"    if (!agentWidget) {\",\n    \"      console.error('AgentWidget not loaded');\",\n    \"      return;\",\n    \"    }\",\n    \"\",\n    \"    var widgetConfig = createWidgetConfig(agentWidget);\",\n    \"\",\n    \"    // Load saved state\",\n    \"    var savedState = localStorage.getItem(STORAGE_KEY);\",\n    \"    if (savedState) {\",\n    \"      try {\",\n    \"        var parsed = JSON.parse(savedState);\",\n    \"        widgetConfig.initialMessages = parsed.messages || [];\",\n    \"      } catch (e) {\",\n    \"        console.error('Failed to load saved state:', e);\",\n    \"      }\",\n    \"    }\",\n    \"\",\n    \"    // Initialize widget\",\n    \"    var handle = agentWidget.initAgentWidget({\",\n    `      target: '${emitTargetSelector(options)}',`,\n    \"      useShadowDom: false,\",\n    ...(options?.windowKey ? [`      windowKey: '${options.windowKey}',`] : []),\n    \"      config: widgetConfig\",\n    \"    });\",\n    \"\",\n    \"    // Save state on message events\",\n    \"    window.addEventListener('persona:message', function() {\",\n    \"      var session = handle.getSession ? handle.getSession() : null;\",\n    \"      if (session) {\",\n    \"        localStorage.setItem(STORAGE_KEY, JSON.stringify({\",\n    \"          messages: session.messages,\",\n    \"          timestamp: new Date().toISOString()\",\n    \"        }));\",\n    \"      }\",\n    \"    });\",\n    \"\",\n    \"    // Clear state on clear chat\",\n    \"    window.addEventListener('persona:clear-chat', function() {\",\n    \"      localStorage.removeItem(STORAGE_KEY);\",\n    \"      localStorage.removeItem(PROCESSED_ACTIONS_KEY);\",\n    \"    });\",\n    \"  };\",\n    \"\",\n    \"  // Wait for framework hydration to complete (Next.js, Nuxt, etc.)\",\n    \"  // This prevents the framework from removing dynamically added CSS during reconciliation\",\n    \"  var waitForHydration = function(callback) {\",\n    \"    var executed = false;\",\n    \"    \",\n    \"    var execute = function() {\",\n    \"      if (executed) return;\",\n    \"      executed = true;\",\n    \"      callback();\",\n    \"    };\",\n    \"\",\n    \"    var afterDom = function() {\",\n    \"      // Strategy 1: Use requestIdleCallback if available (best for detecting idle after hydration)\",\n    \"      if (typeof requestIdleCallback !== 'undefined') {\",\n    \"        requestIdleCallback(function() {\",\n    \"          // Double requestAnimationFrame ensures at least one full paint cycle completed\",\n    \"          requestAnimationFrame(function() {\",\n    \"            requestAnimationFrame(execute);\",\n    \"          });\",\n    \"        }, { timeout: 3000 }); // Max wait 3 seconds, then proceed anyway\",\n    \"      } else {\",\n    \"        // Strategy 2: Fallback for Safari (no requestIdleCallback)\",\n    \"        // 300ms is typically enough for hydration on most pages\",\n    \"        setTimeout(execute, 300);\",\n    \"      }\",\n    \"    };\",\n    \"\",\n    \"    if (document.readyState === 'loading') {\",\n    \"      document.addEventListener('DOMContentLoaded', afterDom);\",\n    \"    } else {\",\n    \"      // DOM already ready, but still wait for potential hydration\",\n    \"      afterDom();\",\n    \"    }\",\n    \"  };\",\n    \"\",\n    \"  // Boot sequence: wait for hydration, then load CSS and JS, then initialize\",\n    \"  // This prevents Next.js/Nuxt/etc. from removing dynamically added CSS during reconciliation\",\n    \"  waitForHydration(function() {\",\n    \"    loadCSS();\",\n    \"    loadJS(function() {\",\n    \"      init();\",\n    \"    });\",\n    \"  });\",\n    \"})();\",\n    \"</script>\"\n  ]);\n\n  return lines.join(\"\\n\");\n}\n","import { createElement } from \"../utils/dom\";\nimport { createIconButton } from \"../utils/buttons\";\nimport { createToggleGroup } from \"../utils/buttons\";\nimport { renderLucideIcon } from \"../utils/icons\";\n\n// ---------------------------------------------------------------------------\n// Types\n// ---------------------------------------------------------------------------\n\nexport interface DemoCarouselItem {\n  /** URL to load in the iframe (relative or absolute). */\n  url: string;\n  /** Display title shown in the toolbar. */\n  title: string;\n  /** Optional subtitle/description. */\n  description?: string;\n}\n\nexport interface DemoCarouselOptions {\n  /** Demo pages to cycle through. */\n  items: DemoCarouselItem[];\n  /** Initial item index. Default: 0. */\n  initialIndex?: number;\n  /** Initial device viewport. Default: 'desktop'. */\n  initialDevice?: \"desktop\" | \"mobile\";\n  /** Initial color scheme for the iframe wrapper. Default: 'light'. */\n  initialColorScheme?: \"light\" | \"dark\";\n  /** Show zoom +/- controls. Default: true. */\n  showZoomControls?: boolean;\n  /** Show desktop/mobile toggle. Default: true. */\n  showDeviceToggle?: boolean;\n  /** Show light/dark scheme toggle. Default: true. */\n  showColorSchemeToggle?: boolean;\n  /** Called when the active demo changes. */\n  onChange?: (index: number, item: DemoCarouselItem) => void;\n}\n\nexport interface DemoCarouselHandle {\n  /** Root element (already appended to the container). */\n  element: HTMLElement;\n  /** Navigate to a demo by index. */\n  goTo(index: number): void;\n  /** Go to the next demo. */\n  next(): void;\n  /** Go to the previous demo. */\n  prev(): void;\n  /** Current demo index. */\n  getIndex(): number;\n  /** Change the device viewport. */\n  setDevice(device: \"desktop\" | \"mobile\"): void;\n  /** Change the wrapper color scheme. */\n  setColorScheme(scheme: \"light\" | \"dark\"): void;\n  /** Override zoom level (null = auto-fit). */\n  setZoom(zoom: number | null): void;\n  /** Tear down listeners, observer, and DOM. */\n  destroy(): void;\n}\n\n// ---------------------------------------------------------------------------\n// Constants\n// ---------------------------------------------------------------------------\n\nconst DEVICE_DIMENSIONS: Record<string, { w: number; h: number }> = {\n  desktop: { w: 1280, h: 800 },\n  mobile: { w: 390, h: 844 },\n};\n\nconst ZOOM_STEP = 0.1;\nconst ZOOM_MIN = 0.15;\nconst ZOOM_MAX = 1.5;\nconst STAGE_PADDING = 24;\nconst SHADOW_MARGIN = 40;\n\n// ---------------------------------------------------------------------------\n// Injected CSS (self-contained, prefixed persona-dc-)\n// ---------------------------------------------------------------------------\n\nconst CAROUSEL_CSS = /* css */ `\n/* ── Root ── */\n.persona-dc-root {\n  display: flex;\n  flex-direction: column;\n  width: 100%;\n  font-family: -apple-system, BlinkMacSystemFont, \"Segoe UI\", \"Helvetica Neue\", Arial, sans-serif;\n  font-size: 14px;\n  color: #111827;\n  line-height: 1.4;\n}\n\n/* ── Toolbar ── */\n.persona-dc-toolbar {\n  display: flex;\n  align-items: center;\n  justify-content: space-between;\n  gap: 12px;\n  padding: 8px 12px;\n  background: #fff;\n  border: 1px solid #e5e7eb;\n  border-bottom: none;\n  border-radius: 10px 10px 0 0;\n  flex-wrap: wrap;\n}\n.persona-dc-toolbar-lead {\n  display: flex;\n  align-items: center;\n  gap: 6px;\n  min-width: 0;\n}\n.persona-dc-toolbar-trail {\n  display: flex;\n  align-items: center;\n  gap: 8px;\n  flex-wrap: wrap;\n}\n\n.persona-dc-title-btn {\n  display: inline-flex;\n  align-items: center;\n  gap: 4px;\n  font-weight: 600;\n  font-size: 13px;\n  white-space: nowrap;\n  max-width: 240px;\n  background: none;\n  border: 1px solid transparent;\n  border-radius: 0.375rem;\n  padding: 4px 6px;\n  cursor: pointer;\n  color: inherit;\n  font-family: inherit;\n  transition: background-color 0.15s ease, border-color 0.15s ease;\n}\n.persona-dc-title-btn:hover {\n  background: #f3f4f6;\n  border-color: #e5e7eb;\n}\n.persona-dc-title-btn .persona-dc-title-text {\n  overflow: hidden;\n  text-overflow: ellipsis;\n}\n.persona-dc-title-btn .persona-dc-title-chevron {\n  flex-shrink: 0;\n  transition: transform 0.15s ease;\n}\n.persona-dc-title-btn[aria-expanded=\"true\"] .persona-dc-title-chevron {\n  transform: rotate(180deg);\n}\n\n/* ── Title dropdown ── */\n.persona-dc-dropdown {\n  position: absolute;\n  top: 100%;\n  left: 0;\n  margin-top: 4px;\n  min-width: 220px;\n  max-width: 320px;\n  background: #fff;\n  border: 1px solid #e5e7eb;\n  border-radius: 8px;\n  box-shadow: 0 8px 24px rgba(0,0,0,0.12), 0 2px 6px rgba(0,0,0,0.06);\n  padding: 4px;\n  z-index: 100;\n  max-height: 300px;\n  overflow-y: auto;\n}\n.persona-dc-root .persona-dc-dropdown button.persona-dc-dropdown-item {\n  display: flex;\n  flex-direction: column;\n  align-items: flex-start;\n  justify-content: flex-start;\n  width: 100%;\n  padding: 8px 10px;\n  border: none;\n  background: none;\n  border-radius: 6px;\n  cursor: pointer;\n  text-align: left;\n  font-family: inherit;\n  font-size: 13px;\n  font-weight: 500;\n  color: #111827;\n  transition: background-color 0.1s ease;\n}\n.persona-dc-root .persona-dc-dropdown button.persona-dc-dropdown-item:hover {\n  background: #f3f4f6;\n}\n.persona-dc-root .persona-dc-dropdown button.persona-dc-dropdown-item[aria-current=\"true\"] {\n  background: #f5f5f5;\n  color: #0f0f0f;\n}\n.persona-dc-root .persona-dc-dropdown-desc {\n  font-weight: 400;\n  font-size: 12px;\n  color: #6b7280;\n  margin-top: 1px;\n  text-align: left;\n}\n.persona-dc-root .persona-dc-dropdown button.persona-dc-dropdown-item[aria-current=\"true\"] .persona-dc-dropdown-desc {\n  color: #737373;\n}\n.persona-dc-counter {\n  font-size: 12px;\n  color: #6b7280;\n  white-space: nowrap;\n}\n.persona-dc-zoom-controls {\n  display: flex;\n  align-items: center;\n  gap: 2px;\n}\n.persona-dc-zoom-level {\n  font-size: 12px;\n  color: #6b7280;\n  min-width: 36px;\n  text-align: center;\n  user-select: none;\n}\n.persona-dc-separator {\n  width: 1px;\n  height: 20px;\n  background: #e5e7eb;\n  flex-shrink: 0;\n}\n\n/* ── Stage ── */\n.persona-dc-stage {\n  height: 550px;\n  min-height: 400px;\n  padding: ${STAGE_PADDING}px;\n  overflow: auto;\n  background: #f0f1f3;\n  background-image: radial-gradient(circle, #e0e1e5 1px, transparent 1px);\n  background-size: 24px 24px;\n  border: 1px solid #e5e7eb;\n  border-radius: 0 0 10px 10px;\n  display: flex;\n}\n\n/* ── Iframe wrapper ── */\n.persona-dc-iframe-wrapper {\n  position: relative;\n  overflow: hidden;\n  background: #fff;\n  border-radius: 10px;\n  box-shadow: 0 16px 64px rgba(0, 0, 0, 0.12), 0 2px 8px rgba(0, 0, 0, 0.06);\n  margin: auto;\n  flex-shrink: 0;\n  transition: border-radius 0.2s ease;\n}\n.persona-dc-iframe-wrapper[data-color-scheme=\"dark\"] {\n  background: #0f172a;\n}\n\n/* ── Iframe ── */\n.persona-dc-iframe {\n  border: none;\n  display: block;\n  background: #fff;\n  transform-origin: top left;\n}\n.persona-dc-iframe-wrapper[data-color-scheme=\"dark\"] .persona-dc-iframe {\n  background: #0f172a;\n}\n\n/* ── Button/toggle base styles (standalone, no widget.css dependency) ── */\n.persona-dc-root .persona-icon-btn {\n  display: inline-flex;\n  align-items: center;\n  justify-content: center;\n  padding: 0.25rem;\n  border-radius: 0.375rem;\n  border: 1px solid #e5e7eb;\n  background: #ffffff;\n  color: #111827;\n  cursor: pointer;\n  line-height: 1;\n  transition: background-color 0.15s ease, color 0.15s ease, border-color 0.15s ease;\n}\n.persona-dc-root .persona-icon-btn:hover {\n  background: #f3f4f6;\n}\n.persona-dc-root .persona-icon-btn:focus-visible {\n  outline: 2px solid #171717;\n  outline-offset: 2px;\n}\n.persona-dc-root .persona-icon-btn[aria-pressed=\"true\"] {\n  background: #f3f4f6;\n  border-color: #d1d5db;\n}\n.persona-dc-root .persona-toggle-group {\n  display: inline-flex;\n  gap: 0;\n}\n.persona-dc-root .persona-toggle-group > .persona-icon-btn {\n  border-radius: 0;\n}\n.persona-dc-root .persona-toggle-group > .persona-icon-btn:first-child {\n  border-top-left-radius: 0.375rem;\n  border-bottom-left-radius: 0.375rem;\n}\n.persona-dc-root .persona-toggle-group > .persona-icon-btn:last-child {\n  border-top-right-radius: 0.375rem;\n  border-bottom-right-radius: 0.375rem;\n}\n\n/* ── Responsive ── */\n@media (max-width: 640px) {\n  .persona-dc-toolbar {\n    gap: 8px;\n  }\n  .persona-dc-zoom-controls {\n    display: none;\n  }\n  .persona-dc-stage {\n    height: 400px;\n    min-height: 300px;\n  }\n}\n`;\n\nfunction injectStyles(): void {\n  if (document.querySelector(\"style[data-persona-dc-styles]\")) return;\n  const style = document.createElement(\"style\");\n  style.setAttribute(\"data-persona-dc-styles\", \"\");\n  style.textContent = CAROUSEL_CSS;\n  document.head.appendChild(style);\n}\n\n// ---------------------------------------------------------------------------\n// Scale helpers (ported from theme editor)\n// ---------------------------------------------------------------------------\n\nfunction computeFitScale(\n  stage: HTMLElement,\n  dims: { w: number; h: number },\n): number {\n  const availW = stage.clientWidth - STAGE_PADDING * 2 - SHADOW_MARGIN;\n  const availH = stage.clientHeight - STAGE_PADDING * 2 - SHADOW_MARGIN;\n  if (availW <= 0 || availH <= 0) return 1;\n  return Math.min(availW / dims.w, availH / dims.h, 1);\n}\n\nfunction applyScale(\n  wrapper: HTMLElement,\n  iframe: HTMLIFrameElement,\n  dims: { w: number; h: number },\n  scale: number,\n  device: string,\n): void {\n  wrapper.style.width = `${dims.w * scale}px`;\n  wrapper.style.height = `${dims.h * scale}px`;\n  wrapper.style.borderRadius =\n    device === \"mobile\" ? `${32 * scale}px` : \"10px\";\n\n  iframe.style.width = `${dims.w}px`;\n  iframe.style.height = `${dims.h}px`;\n  iframe.style.transformOrigin = \"top left\";\n  iframe.style.transform = `scale(${scale})`;\n}\n\n// ---------------------------------------------------------------------------\n// Factory\n// ---------------------------------------------------------------------------\n\nexport function createDemoCarousel(\n  container: HTMLElement,\n  options: DemoCarouselOptions,\n): DemoCarouselHandle {\n  const {\n    items,\n    initialIndex = 0,\n    initialDevice = \"desktop\",\n    initialColorScheme = \"light\",\n    showZoomControls = true,\n    showDeviceToggle = true,\n    showColorSchemeToggle = true,\n    onChange,\n  } = options;\n\n  if (items.length === 0) {\n    throw new Error(\"createDemoCarousel: items array must not be empty\");\n  }\n\n  injectStyles();\n\n  // ── State ──\n  let currentIndex = Math.max(0, Math.min(initialIndex, items.length - 1));\n  let currentDevice = initialDevice;\n  let currentScheme = initialColorScheme;\n  let zoomOverride: number | null = null;\n  let lastAutoScale = 1;\n  let destroyed = false;\n\n  // ── DOM ──\n  const root = createElement(\"div\", \"persona-dc-root\");\n\n  // Toolbar\n  const toolbar = createElement(\"div\", \"persona-dc-toolbar\");\n  const toolbarLead = createElement(\"div\", \"persona-dc-toolbar-lead\");\n  const toolbarTrail = createElement(\"div\", \"persona-dc-toolbar-trail\");\n\n  // Prev / title / next / counter\n  const prevBtn = createIconButton({\n    icon: \"chevron-left\",\n    label: \"Previous demo\",\n    size: 14,\n    onClick: () => navigate(-1),\n  });\n\n  // Title button with dropdown\n  const titleWrap = createElement(\"div\");\n  titleWrap.style.position = \"relative\";\n\n  const titleBtn = createElement(\"button\", \"persona-dc-title-btn\");\n  titleBtn.type = \"button\";\n  titleBtn.setAttribute(\"aria-expanded\", \"false\");\n  titleBtn.setAttribute(\"aria-haspopup\", \"listbox\");\n  const titleText = createElement(\"span\", \"persona-dc-title-text\");\n  const titleChevron = createElement(\"span\", \"persona-dc-title-chevron\");\n  const chevronSvg = renderLucideIcon(\"chevron-down\", 12, \"currentColor\", 2);\n  if (chevronSvg) titleChevron.appendChild(chevronSvg);\n  titleBtn.append(titleText, titleChevron);\n\n  // Dropdown list\n  const dropdown = createElement(\"div\", \"persona-dc-dropdown\");\n  dropdown.setAttribute(\"role\", \"listbox\");\n  dropdown.style.display = \"none\";\n  let dropdownOpen = false;\n\n  function buildDropdownItems(): void {\n    dropdown.innerHTML = \"\";\n    for (let i = 0; i < items.length; i++) {\n      const item = items[i];\n      const btn = createElement(\"button\", \"persona-dc-dropdown-item\");\n      btn.type = \"button\";\n      btn.setAttribute(\"role\", \"option\");\n      btn.setAttribute(\"aria-current\", i === currentIndex ? \"true\" : \"false\");\n      const titleSpan = createElement(\"span\");\n      titleSpan.textContent = item.title;\n      btn.appendChild(titleSpan);\n      if (item.description) {\n        const desc = createElement(\"span\", \"persona-dc-dropdown-desc\");\n        desc.textContent = item.description;\n        btn.appendChild(desc);\n      }\n      btn.addEventListener(\"click\", () => {\n        closeDropdown();\n        goTo(i);\n      });\n      dropdown.appendChild(btn);\n    }\n  }\n\n  function toggleDropdown(): void {\n    dropdownOpen = !dropdownOpen;\n    dropdown.style.display = dropdownOpen ? \"\" : \"none\";\n    titleBtn.setAttribute(\"aria-expanded\", dropdownOpen ? \"true\" : \"false\");\n    if (dropdownOpen) buildDropdownItems();\n  }\n\n  function closeDropdown(): void {\n    if (!dropdownOpen) return;\n    dropdownOpen = false;\n    dropdown.style.display = \"none\";\n    titleBtn.setAttribute(\"aria-expanded\", \"false\");\n  }\n\n  titleBtn.addEventListener(\"click\", (e) => {\n    e.stopPropagation();\n    toggleDropdown();\n  });\n\n  // Close on outside click\n  const onDocClick = (): void => closeDropdown();\n  document.addEventListener(\"click\", onDocClick);\n\n  titleWrap.append(titleBtn, dropdown);\n\n  const nextBtn = createIconButton({\n    icon: \"chevron-right\",\n    label: \"Next demo\",\n    size: 14,\n    onClick: () => navigate(1),\n  });\n\n  const counterEl = createElement(\"span\", \"persona-dc-counter\");\n\n  toolbarLead.append(prevBtn, titleWrap, nextBtn, counterEl);\n\n  // Device toggle\n  let deviceToggle: ReturnType<typeof createToggleGroup> | null = null;\n  if (showDeviceToggle) {\n    deviceToggle = createToggleGroup({\n      items: [\n        { id: \"desktop\", icon: \"monitor\", label: \"Desktop\" },\n        { id: \"mobile\", icon: \"smartphone\", label: \"Mobile\" },\n      ],\n      selectedId: currentDevice,\n      onSelect: (id) => {\n        currentDevice = id as \"desktop\" | \"mobile\";\n        wrapper.dataset.device = currentDevice;\n        zoomOverride = null;\n        rescale();\n      },\n    });\n    toolbarTrail.appendChild(deviceToggle.element);\n  }\n\n  // Zoom controls\n  let zoomLevelEl: HTMLSpanElement | null = null;\n  if (showZoomControls) {\n    const zoomWrap = createElement(\"div\", \"persona-dc-zoom-controls\");\n    const zoomOut = createIconButton({\n      icon: \"minus\",\n      label: \"Zoom out\",\n      size: 14,\n      onClick: () => {\n        const current = zoomOverride ?? lastAutoScale;\n        zoomOverride = Math.max(ZOOM_MIN, current - ZOOM_STEP);\n        rescale();\n      },\n    });\n    zoomLevelEl = createElement(\"span\", \"persona-dc-zoom-level\");\n    zoomLevelEl.title = \"Reset to 100%\";\n    zoomLevelEl.style.cursor = \"pointer\";\n    zoomLevelEl.addEventListener(\"click\", () => {\n      zoomOverride = 1;\n      rescale();\n    });\n    const zoomIn = createIconButton({\n      icon: \"plus\",\n      label: \"Zoom in\",\n      size: 14,\n      onClick: () => {\n        const current = zoomOverride ?? lastAutoScale;\n        zoomOverride = Math.min(ZOOM_MAX, current + ZOOM_STEP);\n        rescale();\n      },\n    });\n    const zoomFit = createIconButton({\n      icon: \"maximize\",\n      label: \"Fit to view\",\n      size: 14,\n      onClick: () => {\n        zoomOverride = null;\n        rescale();\n      },\n    });\n    zoomWrap.append(zoomOut, zoomLevelEl, zoomIn, zoomFit);\n    toolbarTrail.appendChild(zoomWrap);\n  }\n\n  // Color scheme toggle\n  if (showColorSchemeToggle) {\n    const sep = createElement(\"div\", \"persona-dc-separator\");\n    toolbarTrail.appendChild(sep);\n    const schemeToggle = createToggleGroup({\n      items: [\n        { id: \"light\", icon: \"sun\", label: \"Light\" },\n        { id: \"dark\", icon: \"moon\", label: \"Dark\" },\n      ],\n      selectedId: currentScheme,\n      onSelect: (id) => {\n        currentScheme = id as \"light\" | \"dark\";\n        wrapper.dataset.colorScheme = currentScheme;\n        applySchemeToIframe();\n      },\n    });\n    toolbarTrail.appendChild(schemeToggle.element);\n  }\n\n  // Open in new tab\n  const sep2 = createElement(\"div\", \"persona-dc-separator\");\n  toolbarTrail.appendChild(sep2);\n  const openBtn = createIconButton({\n    icon: \"external-link\",\n    label: \"Open in new tab\",\n    size: 14,\n    onClick: () => {\n      window.open(items[currentIndex].url, \"_blank\");\n    },\n  });\n  toolbarTrail.appendChild(openBtn);\n\n  toolbar.append(toolbarLead, toolbarTrail);\n\n  // Stage + iframe\n  const stage = createElement(\"div\", \"persona-dc-stage\");\n  const wrapper = createElement(\"div\", \"persona-dc-iframe-wrapper\");\n  wrapper.dataset.device = currentDevice;\n  wrapper.dataset.colorScheme = currentScheme;\n\n  const iframe = createElement(\"iframe\", \"persona-dc-iframe\");\n  iframe.setAttribute(\"sandbox\", \"allow-scripts allow-same-origin allow-forms\");\n  iframe.setAttribute(\"loading\", \"lazy\");\n  iframe.title = items[currentIndex].title;\n\n  wrapper.appendChild(iframe);\n  stage.appendChild(wrapper);\n  root.append(toolbar, stage);\n  container.appendChild(root);\n\n  // ── Logic ──\n\n  function applySchemeToIframe(): void {\n    try {\n      const body = iframe.contentDocument?.body;\n      if (!body) return;\n      if (currentScheme === \"dark\") {\n        body.classList.add(\"theme-dark\");\n      } else {\n        body.classList.remove(\"theme-dark\");\n      }\n    } catch {\n      // Cross-origin iframe: silently ignore\n    }\n  }\n\n  // Re-apply scheme after iframe loads new content\n  iframe.addEventListener(\"load\", () => applySchemeToIframe());\n\n  function updateDisplay(): void {\n    const item = items[currentIndex];\n    titleText.textContent = item.title;\n    counterEl.textContent = `${currentIndex + 1} / ${items.length}`;\n    iframe.title = item.title;\n  }\n\n  function navigate(delta: number): void {\n    const next = ((currentIndex + delta) % items.length + items.length) % items.length;\n    goTo(next);\n  }\n\n  function goTo(index: number): void {\n    if (index < 0 || index >= items.length) return;\n    currentIndex = index;\n    iframe.src = items[currentIndex].url;\n    updateDisplay();\n    onChange?.(currentIndex, items[currentIndex]);\n  }\n\n  function rescale(): void {\n    if (destroyed) return;\n    const dims = DEVICE_DIMENSIONS[currentDevice] ?? DEVICE_DIMENSIONS.desktop;\n    lastAutoScale = computeFitScale(stage, dims);\n    const scale = Math.max(\n      ZOOM_MIN,\n      Math.min(ZOOM_MAX, zoomOverride ?? lastAutoScale),\n    );\n    applyScale(wrapper, iframe, dims, scale, currentDevice);\n    if (zoomLevelEl) {\n      zoomLevelEl.textContent = `${Math.round(scale * 100)}%`;\n    }\n  }\n\n  // ResizeObserver\n  const resizeObserver = new ResizeObserver(() => rescale());\n  resizeObserver.observe(stage);\n\n  // Initial render\n  updateDisplay();\n  iframe.src = items[currentIndex].url;\n  // Defer initial scale to next frame so stage has layout dimensions\n  requestAnimationFrame(() => rescale());\n\n  // ── Handle ──\n\n  function destroy(): void {\n    if (destroyed) return;\n    destroyed = true;\n    resizeObserver.disconnect();\n    document.removeEventListener(\"click\", onDocClick);\n    root.remove();\n  }\n\n  return {\n    element: root,\n    goTo,\n    next: () => navigate(1),\n    prev: () => navigate(-1),\n    getIndex: () => currentIndex,\n    setDevice(device: \"desktop\" | \"mobile\") {\n      currentDevice = device;\n      wrapper.dataset.device = device;\n      deviceToggle?.setSelected(device);\n      zoomOverride = null;\n      rescale();\n    },\n    setColorScheme(scheme: \"light\" | \"dark\") {\n      currentScheme = scheme;\n      wrapper.dataset.colorScheme = scheme;\n    },\n    setZoom(zoom: number | null) {\n      zoomOverride = zoom;\n      rescale();\n    },\n    destroy,\n  };\n}\n"],"mappings":"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AAc,EACzBA,EAAM,KAAK,mCAAmC,EAC9CA,EAAM,KAAK,yBAAyB,EACpCA,EAAM,KAAK,eAAe,EAC1BA,EAAM,KAAK,SAAS,EACpBA,EAAM,KAAK,MAAM,EAEVA,EAAM,KAAK;AAAA,CAAI,CACxB,CAEA,SAAS+B,GAA0BpC,EAAa2B,EAAwC,CACtF,IAAMtC,EAAQsC,GAAA,YAAAA,EAAS,MACjBtB,EAAkB,CACtB,4BACA,8DACA,GACA,qCACA,4CACA,WACA,qBACA,oCACA,6BACA,2BACA,0BACA,iCACA,qEACA,GACA,2CACA,iEACA,GACA,4BACA,0BACA,kBACA,qBACA,sBACA,uBACA,mBACA,IACA,GACA,yBACA,kCACA,gCACA,2CACA,sBACA,wBACA,uBACA,IACA,GACA,kEACA,gDACA,wBACA,qFACA,gDACA,wBACA,wCACA,OACA,GACA,wCACA,8DACA,iEACA,uDACA,SACA,8CACA,6DACA,gCACA,SACA,gDACA,2BACA,GACA,+BACA,0CACA,6GACA,yHACA,yCACA,GACA,2CACA,gBACA,kDACA,oCACA,4CACA,WACA,GACA,wFACA,2CACA,UACA,GACA,oCACA,UACA,QACA,GACA,kDACA,8CACA,kBACA,sCACA,GACA,aACA,4CACA,2CACA,6BACA,sCACA,kCACA,0CACA,OACA,KACA,GACA,yCACA,sBACA,uDACA,GACA,0BACA,wCACA,8DACA,0BACA,gBACA,yDACA,mCACA,wBACA,6DACA,YACA,UACA,mBACA,SACA,GACA,iCACA,kBAAkBqB,GAAmBC,CAAO,CAAC,KAC7C,iBACF,EAEA,OAAI3B,EAAO,QAAQK,EAAM,KAAK,oBAAoBL,EAAO,MAAM,IAAI,EAC/DA,EAAO,aAAaK,EAAM,KAAK,yBAAyBL,EAAO,WAAW,IAAI,EAC9EA,EAAO,SAASK,EAAM,KAAK,qBAAqBL,EAAO,OAAO,IAAI,EAClEA,EAAO,QAAQK,EAAM,KAAK,oBAAoBL,EAAO,MAAM,IAAI,EAC/DA,EAAO,QAAQK,EAAM,KAAK,oBAAoBL,EAAO,MAAM,IAAI,EAE/DA,EAAO,OAAS,OAAOA,EAAO,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","names":["markdown_parsers_entry_exports","__export","DOMPurify","import_marked","import_dompurify","init_markdown_parsers_entry","__esmMin","AudioPlaybackManager","init_audio_playback_manager","__esmMin","sampleRate","options","_a","prebufferMs","w","AudioCtx","ctx","pcmData","data","merged","float32","source","callback","held","samples","buffer","now","cbs","cb","idx","numSamples","view","i","int16","normalizeHost","host","describeError","res","_a","_b","data","RuntypeSpeechEngine","init_runtype_speech_engine","__esmMin","init_audio_playback_manager","opts","AudioPlaybackManager","player","request","callbacks","gen","_c","_d","url","reader","done","value","err","error","FallbackSpeechEngine","init_fallback_speech_engine","__esmMin","primary","fallback","options","request","callbacks","started","_a","error","_b","_c","_d","runtype_tts_entry_exports","__export","FallbackSpeechEngine","RuntypeSpeechEngine","init_runtype_tts_entry","__esmMin","init_runtype_speech_engine","init_fallback_speech_engine","index_exports","__export","ASK_USER_QUESTION_CLIENT_TOOL","ASK_USER_QUESTION_PARAMETERS_SCHEMA","ASK_USER_QUESTION_TOOL_NAME","AgentWidgetClient","AgentWidgetSession","AttachmentManager","BrowserSpeechEngine","DEFAULT_COMPONENTS","DEFAULT_FLOATING_LAUNCHER_MAX_WIDTH","DEFAULT_FLOATING_LAUNCHER_WIDTH","DEFAULT_PALETTE","DEFAULT_SEMANTIC","DEFAULT_WIDGET_CONFIG","PRESETS","PRESET_FULLSCREEN","PRESET_MINIMAL","PRESET_SHOP","ReadAloudController","SUGGEST_REPLIES_CLIENT_TOOL","SUGGEST_REPLIES_PARAMETERS_SCHEMA","SUGGEST_REPLIES_TOOL_NAME","THEME_ZONES","VERSION","WEBMCP_TOOL_PREFIX","WebMcpBridge","accessibilityPlugin","animationsPlugin","applyThemeVariables","attachHeaderToContainer","brandPlugin","buildComposer","buildDefaultHeader","buildHeader","buildHeaderWithLayout","buildMinimalHeader","builtInClientToolsForDispatch","collectEnrichedPageContext","componentRegistry","createActionManager","createAgentExperience","createAskUserQuestionBubble","createBestAvailableVoiceProvider","createBubbleWithLayout","createCSATFeedback","createComboButton","createComponentMiddleware","createComponentStreamParser","createDefaultSanitizer","createDemoCarousel","createDirectivePostprocessor","createDropdownMenu","createFlexibleJsonStreamParser","createIconButton","createImagePart","createJsonStreamParser","createLabelButton","createLocalStorageAdapter","createMarkdownProcessor","createMarkdownProcessorFromConfig","createMessageActions","createNPSFeedback","createPlainTextParser","createPlugin","createRegexJsonParser","createStandardBubble","createTextPart","createTheme","createThemeObserver","createToggleGroup","createTypingIndicator","createVoiceProvider","createWidgetHostLayout","createXmlParser","index_core_default","defaultActionHandlers","defaultJsonActionParser","defaultParseRules","detectColorScheme","directivePostprocessor","ensureAskUserQuestionSheet","escapeHtml","extractComponentDirectiveFromMessage","fileToImagePart","formatEnrichedContext","generateAssistantMessageId","generateCodeSnippet","generateMessageId","generateStableSelector","generateUserMessageId","getActiveTheme","getColorScheme","getDisplayText","getHeaderLayout","getImageParts","getPreset","hasComponentDirective","hasImages","headerLayouts","highContrastPlugin","initAgentWidget","isAskUserQuestionMessage","isComponentDirectiveType","isDockedMountMode","isSuggestRepliesMessage","isVoiceSupported","isWebMcpToolName","latestAgentSuggestions","listRegisteredStreamAnimations","markdownPostprocessor","mergeWithDefaults","normalizeContent","parseAskUserQuestionPayload","parseSuggestRepliesPayload","pickBestVoice","pluginRegistry","reducedMotionPlugin","registerStreamAnimationPlugin","removeAskUserQuestionSheet","renderComponentDirective","renderLoadingIndicatorWithFallback","renderLucideIcon","resolveDockConfig","resolveSanitizer","resolveTokens","stripWebMcpPrefix","themeToCssVariables","unregisterStreamAnimationPlugin","validateImageFile","validateTheme","__toCommonJS","import_marked","import_dompurify","loader","moduleCache","loadPromise","provideMarkdownParsers","mod","moduleCache","loadMarkdownParsers","loadPromise","loader","getMarkdownParsersSync","provideMarkdownParsers","DOMPurify","convertRendererOverrides","overrides","createMarkdownProcessor","options","markedInstance","text","_a","_b","parsers","getMarkdownParsersSync","escapeHtml","Marked","opts","rendererOverrides","createMarkdownProcessorFromConfig","config","defaultMarkdownProcessor","markdownPostprocessor","escapeAttribute","value","makeToken","idx","directiveReplacer","source","placeholders","working","match","jsonText","parsed","token","_","type","createDirectivePostprocessor","markdownConfig","processor","withTokens","html","tokenRegex","replacement","directivePostprocessor","DEFAULT_PURIFY_CONFIG","SAFE_DATA_URI","createDefaultSanitizer","purifyInstance","html","parsers","getMarkdownParsersSync","escapeHtml","DOMPurify","_node","data","val","resolveSanitizer","option","DELIMITER_RE","hasPipe","line","splitCells","s","cell","buildRow","cells","buildDelimiter","cols","fitCells","stabilizeStreamingTables","markdown","lines","changed","i","header","delimiter","fullDelimiter","j","row","normalized","WEBMCP_TOOL_PREFIX","webMcpToolDisplayTitles","recordWebMcpToolDisplayTitles","infos","_a","info","title","getWebMcpToolDisplayTitle","toolName","stripWebMcpPrefix","log","message","rest","polyfillLoader","computeClientToolsFingerprint","tools","parts","t","_a","_b","hashFingerprintContent","cyrb53","str","seed","h1","h2","i","ch","content","a","b","WebMcpBridge","config","handler","mc","infos","err","log","recordWebMcpToolDisplayTitles","pageOrigin","info","def","schema","parseSchema","wireToolName","args","signal","errorResult","present","bareName","stripWebMcpPrefix","message","candidate","displayTitle","getWebMcpToolDisplayTitle","gateInfo","controller","timedOut","timer","onAbort","raw","safeStringifyArgs","normalizeSerializedResult","polyfillLoader","core","defaultBrowserConfirmHandler","toolName","list","pattern","matchesGlob","name","WEBMCP_TOOL_PREFIX","isWebMcpToolName","escaped","parsed","safeStringify","argsPreview","previewArgs","prompt","json","value","RUNTYPE_AGENT_PREFIX","RUNTYPE_FLOW_PREFIX","detectRuntypeTypeId","id","resolveTarget","target","targetProviders","trimmed","colon","prefix","rest","detected","resolver","fallback","import_partial_json","createElement","tag","className","element","createElementInDocument","documentRef","createNode","tag","options","children","element","name","value","style","source","property","appendable","child","cx","parts","ASK_USER_QUESTION_TOOL_NAME","ASK_USER_QUESTION_MAX","SHEET_SENTINEL","DEFAULT_FREE_TEXT_LABEL_ROWS","DEFAULT_FREE_TEXT_LABEL_PILLS","DEFAULT_FREE_TEXT_PLACEHOLDER","DEFAULT_SUBMIT_LABEL","DEFAULT_NEXT_LABEL","DEFAULT_BACK_LABEL","DEFAULT_SUBMIT_ALL_LABEL","DEFAULT_SKIP_LABEL","DEFAULT_SKELETON_PILLS","ATTR_CURRENT_INDEX","ATTR_QUESTION_COUNT","ATTR_ANSWERS","ATTR_GROUPED","ATTR_LAYOUT","resolveLayout","feature","getLayout","sheet","truncateWarned","escapeAttrValue","value","isAskUserQuestionMessage","message","resolveFeature","config","_a","_b","parseAskUserQuestionPayload","toolCall","complete","chunks","text","parsed","parsePartialJson","promptsFromPayload","payload","all","firstPrompt","promptAt","index","applyStyleVars","root","s","buildAffordance","layout","multiSelect","wrap","createElement","check","badge","buildPill","option","btn","content","label","desc","aff","buildSkeletonPill","el","buildPillList","prompt","_c","list","cleanOptions","o","i","defaultLabel","otherRow","input","freeBtn","buildFreeTextRow","row","submit","buildMultiSelectActions","buildNavRow","count","_d","back","rightGroup","skip","next","isFinal","readAnswersFromSheet","raw","writeAnswersToSheet","answers","getCurrentIndex","setCurrentIndex","getQuestionCount","isGroupedSheet","restoreAnswersFromMessage","prompts","stored","result","p","q","v","restoreIndexFromMessage","buildStructuredAnswers","indexed","seen","applySelectionState","currentIndex","selected","pills","pill","on","realPillLabels","freeInput","freeRow","syncNavState","hasAnswer","multi","labels","renderCurrentPage","grouped","stepInline","oldStepper","qText","pillList","fresh","oldFree","oldMulti","oldNav","buildSheet","toolCallId","initialAnswers","initialIndex","header","syncSheetFromMessage","newCount","createAskUserQuestionBubble","bubble","ensureAskUserQuestionSheet","overlay","existing","removeAskUserQuestionSheet","selector","duration","getSelectedLabels","setCurrentAnswer","answer","idx","navigateToPage","clamped","SUGGEST_REPLIES_TOOL_NAME","SUGGEST_REPLIES_PARAMETERS_SCHEMA","SUGGEST_REPLIES_CLIENT_TOOL","SUGGEST_REPLIES_TOOL_NAME","suggestRepliesToolResult","isSuggestRepliesMessage","message","_a","parseSuggestRepliesPayload","args","parsed","raw","chips","item","latestAgentSuggestions","messages","i","shouldExposeSuggestReplies","config","feature","ASK_USER_QUESTION_PARAMETERS_SCHEMA","ASK_USER_QUESTION_MAX","ASK_USER_QUESTION_CLIENT_TOOL","ASK_USER_QUESTION_TOOL_NAME","builtInClientToolsForDispatch","config","_a","tools","ask","shouldExposeSuggestReplies","SUGGEST_REPLIES_CLIENT_TOOL","import_partial_json","unescapeJsonString","str","formatUnknownValue","value","formatReasoningDuration","reasoning","_a","_b","end","start","seconds","describeReasonStatus","formatToolDuration","tool","_c","describeToolTitle","tool","formatToolDuration","formatElapsedMs","ms","seconds","computeToolElapsed","_a","_b","_c","durationMs","computeReasoningElapsed","reasoning","resolveToolHeaderText","template","fallback","toolName","duration","parseFormattedTemplate","resolved","segments","regex","lastIndex","match","pushSegments","text","styles","parts","i","createRegexJsonParserInternal","extractedText","processedLength","extractTextFromIncompleteJson","jsonString","textFieldRegex","incompleteTextFieldRegex","incompleteMatch","accumulatedContent","trimmed","extracted","extractTextFromJson","parsed","createPlainTextParser","parser","_accumulatedContent","createRegexJsonParser","regexParser","createJsonStreamParser","parsePartialJson","unescapeJsonString","createFlexibleJsonStreamParser","textExtractor","extractText","getText","value","newText","createXmlParser","version","VERSION","version","DEFAULT_ENDPOINT","DEFAULT_CLIENT_API_BASE","filenameFromMediaType","mediaType","_a","_b","lower","ext","slash","subtype","hasValidContent","message","getParserFromType","parserType","createJsonStreamParser","createRegexJsonParser","createXmlParser","createPlainTextParser","looksStructured","value","preferFinalStructuredContent","rawBuffer","finalString","rawTrimmed","finalTrimmed","rawLooksStructured","rawJsonText","extractTextFromJson","AgentWidgetClient","config","_c","_d","VERSION","WebMcpBridge","next","callback","handler","wireToolName","args","signal","agentId","flowId","target","targetProviders","resolved","resolveTarget","endpoint","session","storedSessionId","routed","sessionTargetId","requestBody","response","error","data","feedback","errorData","messageId","type","rating","comment","options","onEvent","controller","basePayload","sanitizedMetadata","key","baseChatRequest","m","fullClientTools","hasClientTools","clientToolsFingerprint","computeClientToolsFingerprint","sameSession","unchanged","forceFull","attempt","chatRequest","body","err","payload","headers","dynamicHeaders","assistantMessageId","approval","decision","url","executionId","toolOutputs","isClientToken","resumeSessionId","messages","routedAgentId","normalizedMessages","a","b","timeA","timeB","clientTools","builtInClientToolsForDispatch","contextAggregate","provider","result","assistantMessageRef","emitMessage","nextSequence","partIdState","createNewAssistant","partId","msg","ensureAssistant","assistant","reader","decoder","buffer","baseSequence","sequenceCounter","cloneMessage","reasoning","toolCall","tools","tool","shouldEmitMessage","hasContentParts","hasRawContent","assistantMessage","lastAssistantInTurn","customParsePartId","currentTextBlockId","pendingFlowRaw","nestedBlockParent","nestedBlockMessages","nestedBlockRaw","reasoningMessages","toolMessages","reasoningContext","toolContext","normalizeKey","getStepKey","_e","getToolCallKey","_f","_g","baseAssistantId","assistantIdConsumed","ensureAssistantMessage","id","segment","trackReasoningId","stepKey","resolveReasoningId","allowCreate","rawId","existing","generated","ensureReasoningMessage","reasoningId","trackToolId","callKey","artifactToolCallIds","artifactCardMessages","artifactIdsWithCards","artifactContent","isArtifactEmitToolName","name","normalized","resolveToolId","ensureToolMessage","toolId","resolveTimestamp","parsed","dateParsed","ensureStringContent","streamParsers","rawContentBuffers","orderedChunkBuffers","insertOrderedChunk","seq","text","chunks","lo","hi","mid","accumulated","index","reconcileSealedAssistantWithFinalResponse","finalContent","contentToProcess","parser","mergeIfBetter","mergedDisplay","cur","finalizeCleanup","closeResult","extractedFromJson","bestDisplayText","extracted","parsedResult","lastSealedFlowBubble","applyFlowTextChunk","accumulatedRaw","chunk","chunkSeq","looksLikeJson","finalizeFlowTextBlock","effectiveFinal","extractedText","asyncPending","parserToClose","ensureNestedBlockMessage","blockId","parentToolCallId","variant","seqReadyQueue","drainReadyQueue","mediaBuffers","lastIterationSeen","executionKind","executionKindResolved","openTurnId","agentExecution","agentIterationMessages","iterationDisplay","_h","_i","_j","_k","_l","_m","_n","_o","_p","_q","_r","_s","_t","_u","_v","_w","_x","_y","_z","_A","_B","_C","_D","_E","_F","_G","_H","_I","_J","_K","_L","_M","_N","_O","_P","_Q","_R","_S","_T","_U","_V","_W","_X","_Y","_Z","__","_$","_aa","_ba","_ca","_da","_ea","_fa","_ga","_ha","_ia","_ja","_ka","_la","_ma","_na","_oa","_pa","_qa","_ra","_sa","_ta","_ua","_va","_wa","_xa","_ya","_za","_Aa","_Ba","_Ca","_Da","_Ea","_Fa","_Ga","_Ha","_Ia","_Ja","_Ka","_La","_Ma","_Na","_Oa","_Pa","_Qa","_Ra","_Sa","_Ta","_Ua","_Va","_Wa","_Xa","_Ya","i","payloadType","rStartBlockId","rStartParent","reasoningMessage","rDeltaBlockId","nested","nestedChunk","reasonSeq","ordered","start","rCompleteBlockId","nestedReflection","reflectionText","toolName","toolMessage","chunkText","agentCtxChunk","durationValue","agentCtxComplete","toolCallId","rawToolName","isWebMcpToolName","webMcpTool","startBlockId","startParent","prev","deltaBlockId","nestedParent","nestedDelta","nestedRaw","delta","completeBlockId","stepType","executionType","e","finalMsg","sealed","flowStopReason","finalResponse","hasResponse","iteration","prevMsg","turnStopReason","stopReasonTarget","turnId","buf","mediaBlockId","completeMediaType","completeData","completeUrl","reconstructed","mediaToolCallId","rawMedia","mediaContentParts","part","rec","partType","rawMediaType","src","resolvedMediaType","toolCallIdRaw","mediaMessage","prevAssistant","kind","errorMessage","approvalId","approvalMessage","existingMessage","at","artId","artTitle","cardMsg","deltaId","deltaText","acc","props","artCompleteId","refMsg","roleRaw","refArtId","resolvedError","done","events","event","lines","eventType","line","handled","generateMessageId","timestamp","random","generateUserMessageId","generateAssistantMessageId","IMAGE_ONLY_MESSAGE_FALLBACK_TEXT","normalizeContent","content","getDisplayText","part","hasImages","getImageParts","createTextPart","text","createImagePart","image","options","fileToImagePart","file","resolve","reject","reader","dataUri","validateImageFile","acceptedTypes","maxSizeBytes","IMAGE_MIME_TYPES","DOCUMENT_MIME_TYPES","ALL_SUPPORTED_MIME_TYPES","isImageMimeType","mimeType","isImageFile","fileToContentPart","file","resolve","reject","reader","dataUri","isImageFile","validateFile","acceptedTypes","ALL_SUPPORTED_MIME_TYPES","maxSizeBytes","getFileExtension","filename","parts","getFileTypeName","mimeType","ext","init_audio_playback_manager","CAPTURE_SAMPLE_RATE","PLAYBACK_SAMPLE_RATE","CAPTURE_BUFFER_SIZE","RIFF_MAGIC","stripWavHeader","buf","toWsBase","host","_a","trimmed","RuntypeVoiceProvider","config","_b","_c","_d","agentId","token","generation","stream","t","AudioCtx","captureContext","engine","AudioPlaybackManager","wsUrl","ws","event","evt","codeMsg","error","context","source","processor","e","input","pcm16","i","s","msg","role","pcm","callback","status","cb","text","isFinal","metrics","BrowserVoiceProvider","config","cb","_a","_b","SpeechRecognition","event","transcript","result","isFinal","error","callback","createVoiceProvider","config","RuntypeVoiceProvider","BrowserVoiceProvider","custom","provider","createBestAvailableVoiceProvider","isVoiceSupported","pickBestVoice","voices","_a","preferred","name","match","v","BrowserSpeechEngine","_BrowserSpeechEngine","options","request","callbacks","synth","utterance","event","_b","reason","ReadAloudController","resolveEngine","_a","_b","id","listener","request","generation","resolved","state","resolveSpeakableText","raw","actionText","extractActionMessageText","stripMarkdownForSpeech","body","fence","parsed","markdown","text","loader","loadRuntypeTts","loader","CONNECTION_CONFIG_KEYS","connectionConfigChanged","prev","next","key","buildDispatchErrorContent","error","override","err","base","buildWebMcpErrorResult","message","getWebMcpErrorMessage","fallback","isAutoResolvedLocalToolName","name","isWebMcpToolName","SUGGEST_REPLIES_TOOL_NAME","AgentWidgetSession","config","callbacks","ReadAloudController","event","_a","_b","_c","_d","_e","_f","_g","_h","_i","_j","tc","autoResolvable","webMcpPending","AgentWidgetClient","rec","tts","host","agentId","loadRuntypeTts","callback","isVoiceSupported","browser","BrowserSpeechEngine","clientToken","wantFallback","RuntypeSpeechEngine","FallbackSpeechEngine","runtype","voiceConfig","createVoiceProvider","processingErrorText","result","role","text","isFinal","msg","assistantMsg","metrics","status","_k","_l","providerConfig","session","welcomeMessage","messageId","type","rating","comment","merged","prevSSECallback","options","content","llmContent","contentParts","id","createdAt","sequence","streaming","voiceProcessing","rawContent","generateUserMessageId","generateAssistantMessageId","optionsList","results","component","props","directive","rawInput","input","userMessageId","assistantMessageId","userMessage","IMAGE_ONLY_MESSAGE_FALLBACK_TEXT","controller","snapshot","isAbortError","stream","hasStale","webmcp","info","approval","approvalMessageId","resolve","decision","existing","m","updatedApproval","updatedMessage","approvalConfig","onDecision","response","errorData","toolMessage","progress","current","answers","answer","live","executionId","toolName","structuredAnswers","args","questions","qText","toolCallId","q","v","stored","p","i","ans","answerStr","callId","queuedEpoch","batch","snapshots","candidates","candidate","startedAt","completedAt","extraMetadata","claimedKeys","controllers","resumeController","executed","wireToolName","dedupeKey","resumeKey","suggestRepliesToolResult","execPromise","output","ready","r","toolOutputs","malformedKey","resolveController","signal","isSuggestReplies","phase","resumeOutput","manual","ev","row","messages","artifacts","selectedId","wasStreaming","ttsConfig","lastAssistant","resolveSpeakableText","voices","pickBestVoice","listener","withSequence","index","idx","_m","_n","_o","prior","incoming","reTcName","reExecId","reTcId","reKey","isInflight","isResolved","existingToolName","hasCompletedTool","a","b","timeA","timeB","seqA","seqB","import_lucide","LUCIDE_ICONS","FileIcon","ImageIcon","renderLucideIcon","iconName","size","color","strokeWidth","iconData","createSvgFromIconData","svg","elementData","tagName","attrs","element","key","value","DEFAULTS","ALL_SUPPORTED_MIME_TYPES","generateAttachmentId","getFileIconName","mimeType","AttachmentManager","_AttachmentManager","config","_a","_b","_c","container","a","files","_d","_e","_f","_g","file","validation","validateFile","reason","contentPart","fileToContentPart","previewUrl","isImageFile","attachment","error","id","index","previewEl","isImage","previewWrapper","createElement","img","filePreview","iconName","fileIcon","renderLucideIcon","typeLabel","getFileTypeName","removeBtn","xIcon","e","onAttachmentsChange","isObject","value","deepMerge","base","override","merged","key","existing","DEFAULT_FLOATING_LAUNCHER_WIDTH","DEFAULT_FLOATING_LAUNCHER_MAX_WIDTH","DEFAULT_LAUNCHER_CONFIG","DEFAULT_WIDGET_CONFIG","mergeThemePartials","base","override","deepMerge","mergeWithDefaults","config","_a","_b","_c","_d","_e","_f","_g","_h","_i","_j","_k","_l","_m","_n","_o","_p","_q","_r","_s","_t","_u","da","ca","dsb","csb","dsc","csc","dsa","csa","dau","cau","mergedArtifacts","mergedScrollToBottom","mergedScrollBehavior","mergedStreamAnimation","mergedAskUserQuestion","DEFAULT_PALETTE","DEFAULT_SEMANTIC","DEFAULT_COMPONENTS","DEFAULT_FLOATING_LAUNCHER_WIDTH","DEFAULT_FLOATING_LAUNCHER_MAX_WIDTH","resolveTokenValue","theme","path","parts","current","part","resolveTokens","resolved","resolveObject","obj","prefix","key","value","resolvedValue","validateTheme","errors","warnings","mergeRecords","base","override","result","existing","deepMergeComponents","createTheme","userConfig","options","_a","_b","_c","_d","_e","_f","_g","_h","_i","_j","_k","_l","_m","baseTheme","validation","e","plugin","themeToCssVariables","_n","_o","_p","_q","_r","_s","_t","_u","_v","_w","_x","_y","_z","_A","_B","_C","_D","_E","_F","_G","_H","_I","_J","_K","_L","_M","_N","_O","_P","_Q","_R","_S","_T","_U","_V","_W","_X","_Y","_Z","__","_$","_aa","_ba","_ca","_da","_ea","_fa","_ga","_ha","_ia","_ja","_ka","_la","_ma","_na","_oa","_pa","_qa","_ra","_sa","_ta","_ua","_va","_wa","_xa","_ya","_za","_Aa","_Ba","_Ca","_Da","_Ea","_Fa","_Ga","_Ha","_Ia","_Ja","_Ka","_La","_Ma","_Na","_Oa","_Pa","_Qa","_Ra","_Sa","_Ta","_Ua","_Va","_Wa","_Xa","_Ya","_Za","__a","_$a","_ab","_bb","_cb","_db","_eb","_fb","_gb","_hb","_ib","_jb","cssVars","token","varName","headerTokens","introCardTokens","mdH1Size","mdH1Weight","mdH2Size","mdH2Weight","mdProseFont","components","iconBtn","labelBtn","toggleGrp","artifact","t","toolbarBg","THEME_ZONES","DARK_PALETTE","normalizeThemeConfig","theme","detectColorScheme","_a","getColorSchemeFromConfig","config","colorScheme","getColorScheme","createLightTheme","userConfig","createTheme","createDarkTheme","baseTheme","getActiveTheme","scheme","lightThemeConfig","darkThemeConfig","deepMerge","getCssVariables","themeToCssVariables","applyThemeVariables","element","cssVars","name","value","createThemeObserver","callback","cleanupFns","observer","mediaQuery","handleChange","fn","import_idiomorph","morphMessages","container","newContent","options","preserveTypingAnimation","oldNode","newNode","_a","_b","oldText","newText","normalizeCopiedSelectionText","text","INITIAL_HISTORY_STATE","navigateComposerHistory","input","direction","history","currentValue","atStart","state","inHistory","index","computeMessageFingerprint","message","configVersion","_a","_b","_c","_d","_e","_f","_g","_h","_i","_j","_k","_l","_m","_n","_o","_p","_q","_r","_s","_t","_u","_v","_w","_x","_y","_z","_A","_B","_C","_D","_E","_F","_G","_H","_I","_J","_K","_L","createMessageCache","getCachedWrapper","cache","messageId","fingerprint","entry","setCachedWrapper","wrapper","pruneCache","activeMessageIds","key","createFollowStateController","initiallyFollowing","following","getScrollBottomOffset","element","isElementNearBottom","threshold","resolveFollowStateFromScroll","input","currentScrollTop","lastScrollTop","nearBottom","userScrollThreshold","isAutoScrolling","pauseOnUpwardScroll","pauseWhenAwayFromBottom","resumeRequiresDownwardScroll","delta","resolveFollowStateFromWheel","deltaY","resumeWhenNearBottom","hasSelectionWithin","selection","container","computeAnchorScrollState","targetScrollTop","spacerHeight","computeShrunkSpacerHeight","growth","statusCopy","DEFAULT_OVERLAY_Z_INDEX","PORTALED_OVERLAY_Z_INDEX","DEFAULT_STREAM_ANIMATION","DEFAULT_SKIP_TAGS","resolveStreamAnimation","feature","_a","_b","_c","_d","_e","BUILTIN_PLUGINS","globalRegistry","plugin","registerStreamAnimationPlugin","unregisterStreamAnimationPlugin","name","p","listRegisteredStreamAnimations","resolveStreamAnimationPlugin","type","overrides","applyStreamBuffer","content","buffer","message","streaming","lastSpace","lastNewline","makeCharSpan","doc","ch","messageId","index","span","makeWordSpan","word","WHITESPACE_RE","shouldSkipSubtree","node","skipTags","current","el","wrapTextNodeChars","textNode","counterRef","parent","text","fragment","j","group","wrapTextNodeWords","tokens","token","wrapStreamAnimation","html","mode","options","scratch","t","walker","textNodes","wrap","createStreamCaret","caret","createSkeletonPlaceholder","wrapper","line","injectedStyleRoots","injectPluginStyles","root","names","escaped","style","attachedCleanups","attachPlugin","cleanups","cleanup","detachAllPlugins","ensurePluginActive","syncOverlayHostStacking","host","zIndex","DEFAULT_OVERLAY_Z_INDEX","originalPosition","originalZIndex","originalIsolation","computed","positionWasSet","lockCount","savedState","acquireScrollLock","doc","_a","body","scrollY","released","win","DEFAULT_DOCK_CONFIG","isDockedMountMode","config","_a","_b","isComposerBarMountMode","resolveDockConfig","_c","_d","_e","_f","dock","positionMap","DEFAULT_WRAPPER_CLASS","createCloseButton","config","options","_a","_b","_c","_d","_e","_f","showClose","wrapperClassName","buttonSize","iconSize","launcher","closeButtonSize","wrapper","createElement","closeButtonTooltipText","closeButtonShowTooltip","closeButtonIconName","closeButtonIconText","hasCloseBorder","button","createNode","cx","HEADER_THEME_CSS","closeIconSvg","renderLucideIcon","portaledTooltip","showTooltip","tooltipDocument","tooltipContainer","createElementInDocument","arrow","buttonRect","PORTALED_OVERLAY_Z_INDEX","hideTooltip","DEFAULT_CLEAR_CHAT_WRAPPER_CLASS","createClearChatButton","_g","_h","_i","_j","_k","_l","_m","clearChatConfig","clearChatSize","clearChatIconName","clearChatIconColor","clearChatBgColor","clearChatBorderWidth","clearChatBorderColor","clearChatBorderRadius","clearChatPaddingX","clearChatPaddingY","clearChatTooltipText","clearChatShowTooltip","hasClearChatBorder","iconSvg","HEADER_THEME_CSS","buildHeader","context","_a","_b","_c","_d","_e","_f","_g","_h","_i","_j","_k","_l","_m","_n","_o","_p","config","showClose","header","createNode","launcher","headerIconSize","closeButtonPlacement","headerIconHidden","headerIconName","iconHolder","iconSize","iconSvg","renderLucideIcon","img","createElement","headerCopy","title","subtitle","clearChatConfig","clearChatEnabled","clearChatPlacement","clearChatButton","clearChatButtonWrapper","parts","createClearChatButton","closeButtonWrapperClass","closeButton","closeButtonWrapper","createCloseButton","attachHeaderToContainer","container","headerElements","createDropdownMenu","options","items","onSelect","anchor","position","portal","menu","createElement","PORTALED_OVERLAY_Z_INDEX","item","hr","btn","icon","renderLucideIcon","labelSpan","e","hide","cleanupClickOutside","reposition","rect","show","handler","toggle","destroy","createIconButton","options","icon","label","size","strokeWidth","className","onClick","aria","btn","createElement","svg","renderLucideIcon","key","value","createLabelButton","variant","iconSize","classString","span","createToggleGroup","items","selectedId","onSelect","wrapper","currentId","buttons","updatePressed","entry","item","setSelected","id","createComboButton","_a","_b","menuItems","position","portal","hover","labelEl","chevron","dropdown","createDropdownMenu","e","isOpen","text","buildDefaultHeader","context","_a","elements","buildHeader","onTitleClick","headerCopy","e","appendTrailingHeaderActions","container","actions","onAction","_b","_c","btn","createElement","ic","renderLucideIcon","wrapper","dropdown","createDropdownMenu","itemId","buildMinimalHeader","_d","_e","_f","_g","_h","_i","config","showClose","onClose","layoutHeaderConfig","onHeaderAction","launcher","header","titleMenuConfig","titleRow","headerTitle","createComboButton","HEADER_THEME_CSS","handleTitleClick","hoverCfg","closeButtonSize","closeButtonWrapper","closeButton","closeButtonIconName","closeIconSvg","iconHolder","headerSubtitle","headerLayouts","getHeaderLayout","layoutName","buildHeaderWithLayout","layoutConfig","customHeader","headerElements","createComposerTextarea","config","_a","_b","textarea","createElement","defaultMaxLines","lineHeight","readMaxHeight","parsed","attachAutoResize","newHeight","createSendButton","_c","_d","_e","_f","_g","_h","_i","_j","_k","_l","sendButtonConfig","useIcon","iconText","iconName","stopIconName","tooltipText","stopTooltipText","sendLabel","stopLabel","showTooltip","buttonSize","backgroundColor","textColor","wrapper","button","createNode","cx","sendIcon","stopIcon","iconSize","iconColor","renderLucideIcon","tooltip","currentMode","mode","label","next","createMicButton","voiceRecognitionConfig","hasSpeechRecognition","hasRuntypeProvider","micIconName","micIconSize","micIconSizeNum","micBackgroundColor","micIconColor","micIconSvg","micTooltipText","createAttachmentControls","attachmentsConfig","previewsContainer","input","ALL_SUPPORTED_MIME_TYPES","attachIconName","attachIconSize","buttonSizeNum","attachIconSizeNum","attachIconSvg","e","attachTooltipText","createStatusText","statusConfig","alignClass","statusText","isVisible","idleLabel","link","createSuggestionsRow","buildComposer","context","_a","_b","_c","_d","_e","_f","config","footer","createNode","suggestions","createSuggestionsRow","composerForm","textarea","attachAutoResize","createComposerTextarea","send","createSendButton","mic","createMicButton","attachment","createAttachmentControls","statusText","createStatusText","actionsRow","leftActions","createElement","rightActions","e","buildPillPeekBanner","root","createNode","iconHolder","createElement","messageIcon","renderLucideIcon","textNode","caret","caretIcon","buildPillComposer","context","_a","_b","_c","_d","_e","_f","config","footer","suggestions","createSuggestionsRow","statusText","createStatusText","textarea","attachAutoResize","createComposerTextarea","send","createSendButton","mic","createMicButton","attachment","createAttachmentControls","composerForm","leftActions","rightActions","e","actionsRow","createWrapper","config","_a","_b","_c","_d","_e","_f","_g","_h","_i","_j","_k","_l","_m","_n","_o","_p","_q","launcherEnabled","dockedMode","isDockedMountMode","isComposerBarMountMode","cb","wrapper","createElement","DEFAULT_OVERLAY_Z_INDEX","panel","pillRoot","inlineWidth","launcher","position","positionMap","launcherWidth","width","DEFAULT_FLOATING_LAUNCHER_WIDTH","buildComposerBarPanel","showClose","container","closeButton","closeButtonWrapper","createCloseButton","clearChatEnabled","clearChatButton","clearChatButtonWrapper","parts","createClearChatButton","headerPlaceholder","createNode","body","introTitle","introSubtitle","introCard","messagesWrapper","contentMaxWidth","composerOverlay","composerElements","buildPillComposer","peekBanner","peekTextNode","buildPillPeekBanner","buildPanel","headerLayoutConfig","showHeader","headerElements","buildHeaderWithLayout","buildHeader","buildComposer","showFooter","attachHeaderToContainer","createLauncherButton","config","onToggle","button","createElement","update","newConfig","_a","_b","_c","_d","_e","_f","_g","_h","_i","_j","_k","_l","_m","launcher","dockedMode","isDockedMountMode","titleEl","t","subtitleEl","s","textContainer","icon","iconSize","iconSizeNum","iconSvg","renderLucideIcon","img","callToActionIconEl","callToActionIconSize","paddingTotal","containerSize","positionClass","positionMap","floatingBase","dockedBase","DEFAULT_OVERLAY_Z_INDEX","defaultBorder","defaultShadow","destroy","createWidgetView","config","showClose","wrapper","panel","pillRoot","createWrapper","panelElements","buildPanel","shell","transcript","header","composer","next","nextFooter","resolveLauncher","plugins","onToggle","launcherPlugin","p","customLauncher","createLauncherButton","instance","getDefaultStopReasonNoticeCopy","stopReason","resolveStopReasonNoticeText","overrides","fallback","override","text","createStopReasonNotice","notice","createElement","isSafeImageSrc","src","lower","isSafeMediaSrc","MESSAGE_IMAGE_PREVIEW_MAX_WIDTH_PX","MESSAGE_IMAGE_PREVIEW_MAX_HEIGHT_PX","getMessageImageParts","message","part","getMessageAudioParts","getMessageVideoParts","getMessageFileParts","createMessageImagePreviews","imageParts","hasVisibleText","onPreviewFailed","container","visiblePreviewCount","failureHandled","handleTotalPreviewFailure","imagePart","index","_a","imageElement","settled","createMessageAudioPreviews","audioParts","visible","audioElement","createMessageVideoPreviews","videoParts","videoElement","createMessageFilePreviews","fileParts","link","createTypingIndicator","dot1","dot2","dot3","srOnly","renderLoadingIndicatorWithFallback","location","customRenderer","widgetConfig","context","result","createAvatar","avatarConfig","role","avatar","avatarContent","img","createTimestamp","timestampConfig","tagName","timestamp","date","getBubbleClasses","layout","baseClasses","createMessageActions","actionsConfig","_callbacks","_b","_c","_d","_e","_f","_g","showCopy","showUpvote","showDownvote","showReadAloud","empty","visibility","align","alignClass","layoutClass","createActionButton","iconName","label","dataAction","button","createIconButton","createStandardBubble","transform","layoutConfig","actionCallbacks","options","_h","_i","_j","_k","_l","_m","_n","_o","_p","_q","config","showAvatar","showTimestamp","avatarPosition","timestampPosition","classes","bubble","messageContentText","shouldHideTextUntilPreviewFails","IMAGE_ONLY_MESSAGE_FALLBACK_TEXT","streamAnimation","resolveStreamAnimation","streamPluginOverrides","streamPlugin","resolveStreamAnimationPlugin","pluginStillAnimating","streamAnimationActive","contentDiv","bufferedContent","applyStreamBuffer","transformedContent","animatedContent","wrapStreamAnimation","textContentDiv","caret","createStreamCaret","spans","lastSpan","lastChild","lastBlock","imagePreviews","audioPreviews","videoPreviews","filePreviews","stopReasonNoticeText","hasVisibleContent","skeletonEnabled","trailSkeleton","createSkeletonPlaceholder","indicator","actions","wrapper","createBubbleWithLayout","reasoningExpansionState","appendRenderedValue","container","value","getReasoningPreviewText","message","maxLines","_a","_b","text","line","updateReasoningBubbleUI","messageId","bubble","expanded","header","content","preview","headerMeta","toggleIcon","chevronIcon","renderLucideIcon","createReasoningBubble","config","_c","_d","_e","_f","_g","_h","_i","_j","_k","reasoning","createElement","reasoningDisplayConfig","expandable","isActive","previewText","headerContent","title","defaultSummary","reasoningConfig","startedAt","createElapsedSpan","span","computeReasoningElapsed","customSummary","status","describeReasonStatus","loadingAnimation","activeTemplate","completeTemplate","currentTemplate","skipCustomElement","appendCharSpans","startIndex","idx","char","renderFormattedTitle","template","animated","segments","parseFormattedTemplate","charIndex","seg","parent","w","s","animDuration","collapsedPreview","renderedPreview","body","toolExpansionState","appendRenderedValue","container","value","getToolPreviewText","message","maxLines","_a","tool","chunkText","line","argsText","formatUnknownValue","applyToolCodeBlockColors","pre","toolCallConfig","_b","_c","getToolSummaryText","config","_d","_e","toolDisplayConfig","collapsedMode","previewText","defaultSummary","describeToolTitle","isActive","summary","resolveToolHeaderText","updateToolBubbleUI","messageId","bubble","expanded","header","content","preview","headerMeta","toggleIcon","iconColor","chevronIcon","renderLucideIcon","createToolBubble","_f","_g","_h","_i","createElement","expandable","headerContent","title","startedAt","createElapsedSpan","span","computeToolElapsed","customSummary","loadingAnimation","activeTemplate","completeTemplate","currentTemplate","skipCustomElement","appendCharSpans","text","startIndex","idx","char","renderFormattedTitle","template","animated","toolName","segments","parseFormattedTemplate","charIndex","seg","parent","w","s","animDuration","collapsedPreview","renderedPreview","argsBlock","argsLabel","argsPre","logsBlock","logsLabel","logsPre","resultBlock","resultLabel","resultPre","duration","approvalDetailsExpansionState","humanizeToolName","toolName","words","WEBMCP_TOOL_PREFIX","sentence","resolveApprovalConfig","config","isDetailsExpanded","messageId","_a","_b","_c","detailsMode","applyDetailsToggleState","toggle","expanded","approvalConfig","label","chevronHolder","chevron","renderLucideIcon","updateApprovalDetailsUI","bubble","details","createApprovalBubble","message","config","_a","_b","_c","_d","_e","_f","_g","_h","_i","_j","_k","_l","_m","_n","_o","approval","approvalConfig","isPending","bubble","createElement","header","iconContainer","iconName","iconColor","icon","renderLucideIcon","content","titleRow","title","badge","declaredTitle","WEBMCP_TOOL_PREFIX","getWebMcpToolDisplayTitle","summaryFromConfig","summaryFallsBackToDescription","summaryText","humanizeToolName","summary","reasonLine","reasonLabel","detailsMode","showDescriptionInDetails","hasDetails","expanded","isDetailsExpanded","toggle","toggleLabel","chevronHolder","applyDetailsToggleState","details","description","paramsPre","formatUnknownValue","buttonsContainer","approveBtn","approveIcon","approveLabel","denyBtn","denyIcon","denyLabel","defaultContainer","anchor","_a","_b","root","createPopover","options","content","placement","offset","matchAnchorWidth","zIndex","onOpen","onDismiss","container","open","detach","reposition","rect","top","left","close","doOpen","_c","ownerWindow","ownerDocument","onReposition","onOutside","event","path","armId","isEditableEventTarget","el","createInstanceState","detachMessage","state","messageId","prevKey","popover","teardownMessage","idx","resolveApprovalConfig","config","isDetailsExpanded","approvalConfig","_a","_b","mode","approvalDetailsExpansionState","kbd","label","el","createElement","buildTitle","approval","title","declaredTitle","WEBMCP_TOOL_PREFIX","getWebMcpToolDisplayTitle","custom","toolDisplay","humanizeToolName","source","toolStrong","srcStrong","buildResolvedTrace","row","icon","renderLucideIcon","name","buildPending","message","approve","deny","enableAlways","_c","_d","_e","_f","_g","card","detailsMode","hasDescription","hasParams","hasDetails","expanded","showDetailsLabel","hideDetailsLabel","head","logo","glyph","toggle","chevron","body","details","desc","pre","formatUnknownValue","reasonLine","actions","applyPrimaryColors","denyBtn","split","primary","caret","caretIcon","menu","once","createPopover","allow","e","target","action","willOpen","createBuiltInApprovalPlugin","hidden","onKeydown","isEditableEventTarget","id","createSuggestions","container","suggestionButtons","lastAgentShownKey","chips","session","textarea","messages","chipsConfig","opts","agentPushed","msg","fragment","streaming","getFontFamilyValue","family","chip","btn","createElement","shownKey","EventStreamBuffer","maxSize","store","event","_a","events","toLoad","count","all","EventStreamStore","dbName","storeName","resolve","reject","request","db","err","event","events","store","toWrite","REQUEST_START_EVENTS","STEP_START_EVENTS","VISIBLE_DELTA_EVENTS","INTERMEDIATE_COMPLETE_EVENTS","TERMINAL_COMPLETE_EVENTS","ERROR_EVENTS","isRecord","value","toFiniteNumber","getRecord","key","nested","estimateTokensFromCharCount","charCount","calculateTokensPerSecond","outputTokens","durationMs","resolveEventType","eventType","payload","getTextDelta","isVisibleTextDelta","type","getOutputTokens","_a","_b","_c","_d","_e","result","getRecord","candidates","candidate","toFiniteNumber","getExecutionTimeMs","defaultClock","ThroughputTracker","now","run","isRecord","ERROR_EVENTS","REQUEST_START_EVENTS","STEP_START_EVENTS","VISIBLE_DELTA_EVENTS","text","stats","estimateTokensFromCharCount","INTERMEDIATE_COMPLETE_EVENTS","exact","usingExact","TERMINAL_COMPLETE_EVENTS","terminalExact","source","streamedDurationMs","executionTimeMs","applyCustomClasses","el","classes","c","DEFAULT_BADGE_COLORS","DEFAULT_BADGE_COLOR","DEFAULT_DESCRIPTION_FIELDS","UPDATE_THROTTLE_MS","getBadgeColor","eventType","customColors","allColors","prefix","formatRelativeTimestamp","ms","firstEventMs","formatAbsoluteTimestamp","d","hh","mm","ss","mmm","extractDescription","payload","fields","obj","field","parts","current","part","copyToClipboard","text","_a","resolve","textarea","formatEventForCopy","event","formattedPayload","formatThroughputValue","metric","formatThroughputMeta","renderInlinePayload","plugins","config","parsedPayload","payloadPlugin","p","customPayload","renderDefaultPayload","payloadContainer","createElement","payloadContent","renderEventRow","index","firstTimestamp","esConfig","expandedSet","onToggleExpand","isExpanded","wrapper","rowPlugin","customRow","buildDefaultRowContent","_b","container","row","chevron","chevronIcon","renderLucideIcon","timestamp","tsFormat","seqNum","badgeColor","badge","descFields","desc","descEl","spacer","copyBtn","clipIcon","e","checkIcon","restoreIcon","createEventStreamView","options","_c","_d","_e","buffer","getFullHistory","onClose","getThroughput","scrollToBottomConfig","scrollToBottomEnabled","scrollToBottomIconName","scrollToBottomLabel","viewPlugin","customView","buildDefaultView","customClasses","filteredEvents","selectedType","searchTerm","searchTimeout","lastKnownTypes","lastTypeCounts","lastFilteredCount","autoFollow","createFollowStateController","newEventsSincePause","lastRenderTime","pendingUpdate","pendingRafId","suppressScrollHandler","lastScrollTop","rowElements","lastRenderedFilter","lastRenderedSearch","dirtyExpandId","filterSelect","copyAllBtn","searchInput","searchClearBtn","throughputValueEl","throughputContainer","throughputTooltipEl","buildDefaultToolbar","toolbarOuter","headerBar","group","tooltip","showTooltip","gRect","pRect","hideTooltip","headerSpacer","allOption","copyAllIcon","copyAllLabel","searchBar","searchIcon","searchIconWrapper","clearSearchIcon","toolbar","toolbarPlugin","customToolbar","type","resetScrollState","updateNow","term","truncationBanner","updateThroughputSummary","value","meta","eventsListWrapper","eventsList","scrollIndicator","arrowIcon","indicatorText","noResultsMsg","updateFilterOptions","allEvents","typeCounts","types","typesChanged","t","i","countsChanged","totalChanged","a","b","currentValue","opt","getFilteredEvents","events","lower","hasActiveFilters","toggleExpand","eventId","savedScrollTop","wasAutoFollowing","isNearBottom","isElementNearBottom","evictedCount","newCount","bufferHasEvents","currentIds","id","filterChanged","isFirstRender","fragment","oldRow","idx","newRow","activeIds","evt","update","swapCopyAllIcon","iconName","restoreAfterMs","icon","label","original","restoreLabel","handleCopyAll","parsed","handleFilterChange","handleSearchInput","handleSearchClear","handleListScroll","currentScrollTop","action","nextLastScrollTop","resolveFollowStateFromScroll","handleWheel","resolveFollowStateFromWheel","handleScrollIndicatorClick","handleRowClick","target","handleKeyDown","destroy","renderDefaultArtifactCard","props","_context","title","artifactId","status","subtitle","root","iconBox","meta","titleEl","subtitleEl","dot","statusText","dl","PersonaArtifactCard","context","_a","_b","_c","customRenderer","artifactType","result","ComponentRegistry","name","renderer","components","componentRegistry","PersonaArtifactCard","fallbackComponentCard","sel","_a","card","createElement","title","pre","createArtifactPane","config","options","_b","_c","_d","layout","documentChrome","panePadding","md","createMarkdownProcessorFromConfig","sanitize","resolveSanitizer","toHtml","text","raw","escapeHtml","backdrop","dismissLocalUi","shell","copyMenuDropdown","toolbar","titleEl","closeBtn","viewMode","leftTools","viewBtn","createIconButton","codeBtn","actionsRight","showCopyLabel","showCopyChevron","copyMenuItems","showCopyMenu","copyWrap","copyBtn","copyMenuChevronBtn","createLabelButton","chev","renderLucideIcon","refreshBtn","closeIconBtn","getSelectedArtifactText","records","r","selectedId","id","markdown","jsonPayload","defaultCopy","handler","resolvePortal","initDropdown","createDropdownMenu","item","actionId","e","render","syncViewToggleState","centerTitle","list","content","mobileOpen","hideTabs","tab","x","kind","baseTitle","wrap","renderer","componentRegistry","ctx","el","applyLayoutVisibility","has","root","isMobile","state","open","artifactsSidebarEnabled","config","_a","_b","applyArtifactPaneBorderTheme","mount","config","_a","_b","_c","_d","artifactsSidebarEnabled","l","full","left","clearDocumentToolbarLayoutVars","applyArtifactLayoutCssVars","_e","_f","_g","_h","_i","_j","paneBg","panePad","iconColor","toggleBg","toggleBorder","ARTIFACT_APPEARANCE_MODES","applyArtifactPaneAppearance","m","layout","raw","mode","radius","shadow","outer","shouldExpandLauncherForArtifacts","launcherEnabled","parseArtifactResizePx","input","fallback","m","parseArtifactResizeMaxPxOptional","clampArtifactPaneWidth","widthPx","minPx","maxPx","maxArtifactWidthFromSplit","splitWidthPx","gapPx","handleWidthPx","chatMinPx","raw","readFlexGapPx","splitRoot","win","_a","first","m2","resolveArtifactPaneWidthPx","candidatePx","resizableMinWidth","resizableMaxWidth","cap","formDefinitions","enhanceWithForms","bubble","message","config","session","placeholders","placeholder","_a","_b","_c","type","definition","heading","createElement","title","desc","form","field","group","label","inputType","control","actions","status","submit","event","formEndpoint","formData","payload","value","key","response","data","error","PluginRegistry","plugin","_a","pluginId","a","b","_b","instancePlugins","allPlugins","instanceIds","p","pluginRegistry","createEventBus","listeners","on","event","handler","off","_a","payload","error","stripCodeFence","value","match","extractJsonObject","trimmed","start","depth","i","char","defaultJsonActionParser","text","withoutFence","jsonBody","parsed","action","payload","asString","defaultActionHandlers","context","_a","selector","element","ensureArrayOfStrings","entry","createActionManager","options","processedIds","syncFromMetadata","persistProcessedIds","latestIds","prev","parseSource","acc","parser","eventPayload","handler","triggerResubmit","handlerResult","persist","error","safeJsonParse","value","error","sanitizeMessages","messages","message","sanitizeArtifacts","artifacts","artifact","createLocalStorageAdapter","key","getStorage","storage","state","payload","import_partial_json","isComponentDirective","obj","component","extractComponentDirective","parsed","rawJson","props","createComponentStreamParser","extractedDirective","processedLength","accumulatedContent","trimmed","parsePartialJson","directive","isComponentDirectiveType","renderComponentDirective","directive","options","config","message","onPropsUpdate","renderer","componentRegistry","context","newProps","error","createComponentMiddleware","parser","createComponentStreamParser","accumulatedContent","selectDirectiveSource","trimmed","hasComponentDirective","source","parsed","extractComponentDirectiveFromMessage","defaultCSATLabels","createCSATFeedback","options","onSubmit","onDismiss","title","subtitle","commentPlaceholder","submitText","skipText","showComment","ratingLabels","container","selectedRating","content","header","titleEl","subtitleEl","ratingContainer","ratingButtons","i","ratingButton","btn","index","isSelected","commentTextarea","commentContainer","actions","skipButton","submitButton","comment","error","createNPSFeedback","lowLabel","highLabel","labelsRow","lowLabelEl","highLabelEl","numbersRow","DEFAULT_CHAT_HISTORY_STORAGE_KEY","VOICE_STATE_RESTORE_WINDOW","IMAGE_FILE_EXTENSION_BY_MIME_TYPE","getClipboardImageFiles","clipboardData","_a","_b","_c","imageFiles","clipboardItems","item","file","extension","dataTransferHasFiles","dataTransfer","types","normalizePersistStateConfig","config","_d","_e","_f","_g","_h","_i","getPersistStorage","storageType","storage","testKey","ensureRecord","value","stripStreamingFromMessages","messages","message","buildPostprocessor","cfg","actionManager","onResubmitRequested","markdownProcessor","createMarkdownProcessorFromConfig","sanitize","resolveSanitizer","context","nextText","rawPayload","actionResult","parsersReady","getMarkdownParsersSync","html","out","source","stabilizeStreamingTables","escapeHtml","buildDropOverlay","dropCfg","overlay","createElement","iconName","iconSize","iconColor","iconStrokeWidth","iconSvg","renderLucideIcon","labelEl","createAgentExperience","mount","initialConfig","runtimeOptions","_j","_k","_l","_m","_n","_o","_p","_q","_r","_s","_t","_u","_v","_w","_x","_y","_z","_A","_B","_C","_D","_E","_F","_G","_H","_I","_J","_K","_L","_M","_N","_O","_P","_Q","_R","_S","_T","_U","_V","_W","_X","mergeWithDefaults","plugins","pluginRegistry","builtInApprovalPlugin","teardownBuiltInApprovals","createBuiltInApprovalPlugin","componentRegistry","eventBus","createEventBus","storageAdapter","createLocalStorageAdapter","persistentMetadata","pendingStoredState","shouldOpenAfterStateLoaded","applyStateLoadedHook","state","result","processedState","open","error","storedState","resolved","baseState","getSessionMetadata","updateSessionMetadata","updater","persistState","resolvedActionParsers","defaultJsonActionParser","resolvedActionHandlers","defaultActionHandlers","createActionManager","launcherEnabled","autoExpand","autoFocusInput","prevAutoExpand","prevLauncherEnabled","prevHeaderLayout","wasMobileFullscreen","isComposerBar","isComposerBarMountMode","isPanelToggleable","pendingResubmit","pendingResubmitTimeout","handleResubmitRequested","postprocess","showReasoning","showToolCalls","showEventStreamToggle","scrollToBottomFeature","scrollBehaviorFeature","eventStreamDbName","eventStreamStore","EventStreamStore","eventStreamMaxEvents","eventStreamBuffer","EventStreamBuffer","throughputTracker","ThroughputTracker","eventStreamView","eventStreamVisible","eventStreamRAF","eventStreamLastUpdate","err","messageActionCallbacks","session","feedback","statusConfig","_getStatusText","status","statusCopy","applyStatusToElement","el","text","statusCfg","link","view","createWidgetView","wrapper","panel","pillRoot","panelElements","container","body","messagesWrapper","suggestions","textarea","sendButton","sendButtonWrapper","composerForm","statusText","introTitle","introSubtitle","closeButton","iconHolder","headerTitle","headerSubtitle","header","footer","_actionsRow","leftActions","rightActions","setSendButtonMode","micButton","micButtonWrapper","attachmentButton","attachmentButtonWrapper","attachmentInput","attachmentPreviewsContainer","SCROLL_TO_BOTTOM_EDGE_OFFSET","getScrollToBottomLabel","getScrollToBottomIconName","isScrollToBottomEnabled","getScrollMode","isFollowEffective","followFallbackActive","getAnchorTopOffset","getScrollRestorePosition","isPauseOnInteractionEnabled","isActivityWhilePinnedEnabled","isAnnounceEnabled","scrollToBottomButton","scrollToBottomIcon","scrollToBottomLabel","scrollToBottomCount","anchorSpacer","liveRegion","announceTimer","pendingAnnouncement","announce","updateScrollToBottomButtonOffset","footerHeight","renderScrollToBottomButton","hasLabel","icon","attachmentManager","composerVoiceBridge","headerPlugin","p","customHeader","headerElements","buildHeader","attachHeaderToContainer","setOpenState","existingHeader","toggleEventStreamOn","createEventStreamView","toggleEventStreamOff","eventStreamToggleBtn","HEADER_THEME_CSS","activeClasses","c","rafLoop","now","syncScrollToBottomButton","esClassNames","toggleBtnClasses","activityIcon","clearChatWrapper","closeWrapper","insertBefore","ensureComposerAttachmentSurface","rootFooter","att","previews","form","fileIn","ALL_SUPPORTED_MIME_TYPES","composerPlugin","composerCfg","customComposer","buildComposer","hasAttachments","maybeExpandComposerBar","contentParts","createTextPart","modelId","bindComposerRefsFromFooter","pick","selectors","selector","found","ta","sb","mic","st","sug","attBtn","ar","contentMaxWidth","AttachmentManager","e","target","slots","getDefaultSlotContent","slot","insertSlotContent","element","titleSection","introCard","slotName","renderer","slotElement","handleBubbleExpansion","event","headerButton","bubble","messageId","bubbleType","reasoningExpansionState","updateReasoningBubbleUI","toolExpansionState","updateToolBubbleUI","approvalConfig","defaultExpanded","expanded","approvalDetailsExpansionState","updateApprovalDetailsUI","messageCache","root","selection","raw","normalized","normalizeCopiedSelectionText","messageVoteState","readAloudActiveId","readAloudActiveState","READ_ALOUD_ICONS","applyReadAloudButton","btn","label","svg","refreshReadAloudButtons","id","actionBtn","actionsContainer","action","m","textToCopy","checkIcon","originalIcon","wasActive","outlineIcon","oppositeAction","oppositeBtn","filledIcon","approvalButton","approvalBubble","decision","approvalMessage","buttonsContainer","artifactPaneApi","artifactPanelResizeObs","lastArtifactsState","artifactsPaneUserHidden","sessionRef","dlBtn","artifactId","artifact","markdown","title","cardEl","msgEl","msgId","msg","parsed","blob","url","a","card","syncArtifactPane","askUserOverlay","submitAskUserAnswer","sheet","meta","trimmed","toolCallId","isFreeText","removeAskUserQuestionSheet","sourceMessage","persistGroupedProgress","buildStructuredAnswers","getCurrentIndex","stringifyStructured","answers","q","v","maybeAutoAdvance","idx","count","getQuestionCount","navigateToPage","trigger","multiSelect","grouped","isGroupedSheet","stored","readAnswersFromSheet","set","setCurrentAnswer","pressed","submitBtn","getSelectedLabels","labels","row","input","freeInput","pending","direction","nextIdx","structured","summary","isFinal","handleAskUserDigitKey","n","target_pill","artifactSplitRoot","artifactResizeHandle","artifactResizeUnbind","artifactResizeDocEnd","reconcileArtifactResize","stopArtifactResizePointer","positionExtensionArtifactResizeHandle","ext","mobile","chat","hitW","left","applyLauncherArtifactPanelWidth","artifactsSidebarEnabled","applyArtifactLayoutCssVars","applyArtifactPaneAppearance","threshold","w","chatColumn","splitRoot","createArtifactPane","handle","doc","win","onPointerDown","startX","startW","layout","onMove","ev","splitW","extensionChrome","gapPx","readFlexGapPx","handleW","next","clamped","resolveArtifactPaneWidthPx","onUp","has","isDockedMountMode","resolveDockConfig","ownerWindow","mobileFullscreen","mobileBreakpoint","shouldExpandLauncherForArtifacts","base","DEFAULT_FLOATING_LAUNCHER_WIDTH","applyFullHeightStyles","cb","isExpanded","expandedSize","panelPartial","activeTheme","getActiveTheme","resolveCb","fallback","resolveTokenValue","defaultBorder","defaultShadow","defaultRadius","dockedMode","sidebarMode","fullHeight","isInlineEmbed","resolvePanelChrome","isMobileViewport","shouldGoFullscreen","position","isLeftSidebar","overlayZIndex","DEFAULT_OVERLAY_Z_INDEX","defaultPanelBorder","defaultPanelShadow","defaultPanelBorderRadius","panelBorder","panelShadow","panelBorderRadius","prevBodyScrollTop","restoreBodyScrollTop","maxScrollTop","launcherWidth","width","dw","positionMap","cls","sidebarWidth","maxHeightStyles","paddingStyles","zIndexStyles","applyThemeVariables","destroyCallbacks","teardownHostStacking","releaseScrollLock","cleanupThemeObserver","setupThemeObserver","createThemeObserver","streamAnimationConfig","plugin","resolveStreamAnimationPlugin","ensurePluginActive","detachAllPlugins","suggestionsManager","createSuggestions","closeHandler","renderSuggestions","current","agentChips","latestAgentSuggestions","isStreaming","createMessageCache","lastAskBubbleFingerprint","lastComponentDirectiveFingerprint","lastApprovalBubbleFingerprint","configVersion","markdownReadyAtInit","autoFollow","createFollowStateController","lastScrollTop","scrollRAF","isAutoScrolling","hasPendingAutoScroll","newMessagesSincePause","anchorState","anchorRAF","scrollSendSeeded","suppressScrollSend","lastSentUserMessageId","currentTurnAnchored","lastHandledAssistantId","USER_SCROLL_THRESHOLD","BOTTOM_THRESHOLD","AUTO_SCROLL_SNAP_THRESHOLD","messageState","voiceState","voiceAutoResumeMode","emitVoiceState","persistVoiceMetadata","prev","maybeRestoreVoiceFromMetadata","rawVoiceState","timestamp","applyRuntypeMicRecordingStyles","startVoiceRecognition","getMessagesForPersistence","messagesOverride","payload","smoothScrollRAF","getScrollableContainer","cancelSmoothScroll","cancelAutoScroll","isStreamingOutOfView","isAwayFromLatest","updateScrollToBottomCountBadge","streamingBelow","resetNewMessagesCount","isElementNearBottom","show","getScrollBottomOffset","pauseAutoScroll","resumeAutoScroll","scheduleAutoScroll","force","smoothScrollToBottom","animateScrollTo","resolveTarget","duration","shouldContinue","start","distance","startTime","easeOutCubic","t","animate","currentTime","currentTarget","elapsed","progress","eased","currentScroll","jumpToBottomInstant","offsetTopWithinBody","top","node","restoreScrollPosition","lastUser","escapedId","setAnchorSpacerHeight","height","resetAnchorState","scheduleAnchorToUserMessage","anchorOffsetTop","previousSpacerHeight","contentHeight","targetScrollTop","spacerHeight","computeAnchorScrollState","handleContentResize","currentContentHeight","computeShrunkSpacerHeight","handleUserMessageSent","mode","handleAssistantTurnStarted","trackMessages","nextState","previous","key","renderMessagesWithPluginsImpl","transform","tempContainer","inlineLoadingRenderer","loadingPlugin","appendRenderedValue","containerEl","activeMessageIds","liveAskToolIds","hasAskPlugin","askPluginHydrate","componentDirectiveHydrate","componentStreamingEnabled","hasApprovalPlugin","approvalPluginHydrate","askWithPlugin","isAskUserQuestionMessage","approvalWithPlugin","hasDirectiveBubble","hasComponentDirective","existing","askMeta","fingerprint","computeMessageFingerprint","cachedWrapper","getCachedWrapper","ensureAskUserQuestionSheet","matchingPlugin","messageLayoutConfig","isSuggestRepliesMessage","askPlugin","lastFp","needsRebuild","pluginBubble","complete","parseAskUserQuestionPayload","liveMessage","answer","live","previouslyMounted","stub","approvalPlugin","liveBubble","approvalMessageId","resolveDecision","options","createApprovalBubble","createReasoningBubble","createToolBubble","b","createStandardBubble","enhanceWithForms","directive","extractComponentDirectiveFromMessage","wrapChrome","componentBubble","renderComponentDirective","componentWrapper","textDiv","stack","setCachedWrapper","toolGroups","currentGroup","group","groupIndex","wrappers","groupMessage","child","groupWrapper","groupContainer","defaultSummary","renderedSummary","toolCall","wrapperIndex","pruneCache","hasStreamingAssistantMessage","lastMessage","hasRecentAssistantResponse","loadingIndicatorContext","createTypingIndicator","typingIndicator","typingBubble","showBubble","typingWrapper","idleIndicatorContext","idlePlugin","idleIndicator","idleBubble","idleWrapper","morphMessages","renderMessagesWithPlugins","composerBarOutsideClickListener","attachComposerBarOutsideClickDismiss","listener","path","detachComposerBarOutsideClickDismiss","composerBarEscapeListener","attachComposerBarEscapeDismiss","detachComposerBarEscapeDismiss","composerHovered","peekActivatedPlugins","resolvePeekStreamAnimationFeature","peekFeature","syncComposerBarPeek","peekBanner","peekTextNode","lastAssistant","i","streaming","feature","streamAnimation","resolveStreamAnimation","pluginStillAnimating","animationActive","desiredContainerClass","currentContainerClass","buffered","applyStreamBuffer","skeleton","createSkeletonPlaceholder","sliceStart","slice","escaped","preview","wrapStreamAnimation","caret","createStreamCaret","spans","lastSpan","shouldShow","onPeekPointerDown","onPanelPointerEnter","onPanelPointerLeave","applyComposerBarGeometry","isOpen","bottomOffset","collapsedMaxWidth","expandedMaxWidth","expandedTopOffset","modalMaxWidth","modalMaxHeight","viewportClamp","pillAreaClearance","s","ps","updateOpenState","dockReveal","launcherButtonInstance","customLauncherElement","nextOpen","prevOpen","isViewportCovering","sm","ow","mf","mb","isMobile","dockedMF","composerBarFS","hostEl","syncOverlayHostStacking","acquireScrollLock","recalcPanelHeight","stateEvent","setComposerDisabled","disabled","maybeFocusInput","updateCopy","showCard","storedId","sessionId","toolElapsedTimerId","ensureToolElapsedTimer","span","startedAt","formatElapsedMs","AgentWidgetSession","lastUserMessage","lastAssistantMessage","prevLastUserMessageId","currentStatusConfig","removeRuntypeMicStateStyles","applyRuntypeMicProcessingStyles","applyRuntypeMicSpeakingStyles","lastReadAloudId","activeId","type","handleSubmit","resetHistoryNavigation","historyNavigationEnabled","composerHistoryState","INITIAL_HISTORY_STATE","suppressHistoryReset","getUserMessageHistory","applyHistoryValue","end","handleComposerInput","handleComposerKeydown","atStart","navigateComposerHistory","handleEscStop","handleInputPaste","clipboardImageFiles","speechRecognition","isRecording","pauseTimer","originalMicStyles","getSpeechRecognitionClass","SpeechRecognitionClass","pauseDuration","initialText","fullTranscript","interimTranscript","transcript","newValue","finalValue","stopVoiceRecognition","voiceConfig","recordingBackgroundColor","recordingIconColor","recordingBorderColor","createMicButton","sendButtonConfig","hasSpeechRecognition","hasRuntypeProvider","hasCustomProvider","micIconName","buttonSize","micIconSize","micIconSizeNum","backgroundColor","micIconSvg","tooltipText","tooltip","storeOriginalMicStyles","swapMicIcon","color","existingSvg","size","newSvg","removeAllVoiceStateClasses","interruptionMode","bgColor","borderColor","resolvedColor","defaultSpeakingIcon","ariaLabel","handleMicButtonClick","voiceStatus","autoResumeUnsub","resubmitUnsub","toggleOpen","instance","resolveLauncher","viewportHeight","verticalMargin","heightOffset","available","finalHeight","isVC","footerResizeObserver","lastBottomOffset","getTranscriptSelection","shadowSelection","hasActiveTranscriptSelection","hasSelectionWithin","handleScroll","scrollTop","currentBottomOffset","bottomOffsetShrank","nextLastScrollTop","resolveFollowStateFromScroll","contentResizeObserver","handleSelectionChange","selectionDocument","NAV_KEYS","handleTranscriptKeydown","handleTranscriptFocusIn","handleWheel","resolveFollowStateFromWheel","refreshCloseButton","clearChatButton","clearEvent","escStopDoc","ATTACHMENT_DROP_ACTIVE_CLASS","attachmentFileDragDepth","clearAttachmentDropVisual","attachmentDropHandlingActive","handleAttachmentDragEnterCapture","handleAttachmentDragLeaveCapture","handleAttachmentDragOverCapture","handleAttachmentDropCapture","files","attachmentDropCapture","ownerDoc","handleDocDragOver","handleDocDrop","controller","nextConfig","_Y","_Z","__","_$","_aa","_ba","_ca","_da","_ea","_fa","_ga","_ha","_ia","_ja","_ka","_la","_ma","_na","_oa","_pa","_qa","_ra","_sa","_ta","_ua","_va","_wa","_xa","_ya","_za","_Aa","_Ba","_Ca","_Da","_Ea","_Fa","_Ga","_Ha","_Ia","_Ja","_Ka","_La","_Ma","_Na","_Oa","_Pa","_Qa","_Ra","_Sa","_Ta","_Ua","_Va","_Wa","_Xa","_Ya","_Za","__a","_$a","_ab","_bb","_cb","_db","_eb","_fb","_gb","previousToolCallConfig","previousMessageActions","previousLayoutMessages","previousColorScheme","previousLoadingIndicator","previousIterationDisplay","previousShowReasoning","previousShowToolCalls","previousToolCallDisplay","previousReasoningDisplay","newPlugins","prevScrollMode","prevShowEventStreamToggle","dynEsClassNames","dynToggleBtnClasses","headerLayoutConfig","newHeaderElements","buildHeaderWithLayout","showClearChat","closeButtonWrapper","showHeader","showFooter","toolCallConfigChanged","messageActionsChanged","layoutMessagesChanged","loadingIndicatorChanged","iterationDisplayChanged","featuresChanged","launcher","headerIconHidden","layoutShowIcon","shouldHideIcon","headerIconName","headerIconSize","headerEl","headerCopy","img","newImg","existingImg","layoutShowTitle","layoutShowSubtitle","closeButtonSize","closeButtonPlacement","isTopRight","currentlyTopRight","clearChatPlacement","clearChatEnabled","borderWidth","closeButtonIconName","closeButtonIconText","closeButtonTooltipText","closeButtonShowTooltip","portaledTooltip","showTooltip","tooltipDocument","tooltipContainer","createElementInDocument","arrow","buttonRect","PORTALED_OVERLAY_Z_INDEX","hideTooltip","clearChatButtonWrapper","clearChatConfig","layoutShowClearChat","shouldShowClearChat","closeButtonWrapperEl","clearChatSize","clearChatIconName","clearChatIconColor","clearChatIconSize","clearChatTooltipText","clearChatShowTooltip","nextParsers","nextHandlers","voiceRecognitionEnabled","micButtonResult","newTooltip","attachmentsConfig","attachIconName","attachIconSize","buttonSizeNum","attachIconSizeNum","attachIconSvg","attachTooltipText","useIcon","iconText","textColor","updatedContentMaxWidth","statusIndicatorConfig","isVisible","currentStatus","alignClass","valueToSubmit","optionsList","stream","manual","approvalId","handler","existingFeedback","feedbackEl","createCSATFeedback","rating","comment","createNPSFeedback","previousDebug","debugApi","active","instanceId","handleFocusInput","detail","handleShowEvent","handleHideEvent","handleShowArtifacts","handleHideArtifacts","handleUpsertArtifact","handleSelectArtifact","handleClearArtifacts","persistConfig","openKey","voiceKey","voiceModeKey","wasOpen","wasVoiceActive","wasInVoiceMode","clearPersistState","handleClearChat","loadMarkdownParsers","parseDockWidthToPx","width","shellClientWidth","w","px","pct","applyDockSlotMaxHeight","dockSlot","maxHeight","applyInFlowDockSlotPosition","warnIfDockHeightChainUnresolved","shell","dock","parent","probe","measurable","resolved","setDirectHostStyles","host","config","_a","_b","launcherEnabled","clearOverlayDockSlotStyles","clearMobileFullscreenDockSlotStyles","clearResizeDockSlotTransition","clearPushTrackStyles","pushTrack","resetContentSlotFlexSizing","contentSlot","clearEmergeDockStyles","migrateDockChildren","usePush","orderDockChildren","side","applyDockStyles","expanded","_c","_d","_e","_f","resolveDockConfig","ownerWindow","mobileFullscreenEnabled","mobileBreakpoint","isMobileViewport","DEFAULT_OVERLAY_Z_INDEX","dockTransition","translateClosed","translate","panelPx","contentPx","marginOffsetPx","isEmerge","collapsedClosed","createDirectLayout","target","nextConfig","createDockedLayout","ownerDocument","originalParent","tagName","originalNextSibling","resizeObserver","disconnectResizeObserver","syncPushResizeObserver","heightChainChecked","layout","onViewportResize","state","nextExpanded","createWidgetHostLayout","isDockedMountMode","import_meta","ensureTarget","target","element","widgetCssHref","mountStyles","root","ownerDocument","href","adoptExistingStylesheet","globalLink","clonedLink","link","initAgentWidget","options","_a","useShadow","config","hostLayout","createWidgetHostLayout","controller","stateUnsubs","createMount","host","nextConfig","_b","shouldFillHost","isDockedMountMode","mount","shadowRoot","syncHostState","bindHostState","unsubscribe","mountController","createAgentExperience","destroyCurrentController","rebuildLayout","handleBase","_c","_d","_e","_f","mergedConfig","previousDocked","nextDocked","previousComposerBar","isComposerBarMountMode","nextComposerBar","handle","targetObject","prop","receiver","value","SKIP_TAGS","INTERACTIVE_TAGS","INTERACTIVE_ROLES","CARD_HINT_RE","CURRENCY_RE","BASE_SCORE_INTERACTIVE","BASE_SCORE_STATIC","hasCardHint","el","cls","i","a","subtreeHasCurrency","_a","subtreeHasNonTrivialLink","anchors","href","subtreeHasButtonLike","extractFirstPrice","text","m","extractTitleAndHref","_b","_c","link","heading","extractCtaLabels","labels","push","s","t","b","inp","COMMERCE_CARD_RULE_ID","RESULT_CARD_RULE_ID","commerceCardScore","resultCardScore","defaultParseRules","owner","descendant","enriched","title","price","ctas","warnSimpleWithRules","resolveDomContextConfig","options","_d","_e","_f","_g","_h","_i","_j","_k","_l","_m","nested","maxElements","excludeSelector","maxTextLength","visibleOnly","root","mode","maxCandidates","rules","cssEscape","str","DATA_ATTR_PRIORITY","classifyInteractivity","tag","role","isElementVisible","style","collectAttributes","attrs","id","ariaLabel","type","value","name","attr","generateStableSelector","sel","val","classes","c","classSel","parent","index","nthSel","baseInteractivityScore","interactivity","buildEnriched","scoreCandidate","ctx","score","formattingRule","rule","bonus","shouldSuppress","kept","cand","k","collectStructured","cfg","rootEl","seenSelectors","raw","domIndex","walker","node","hasText","hasMeaningfulAttrs","sa","sb","formattedSummary","line","out","collectSimple","elements","interactive","staticEls","collectEnrichedPageContext","TEXT_PREVIEW_LEN","formatEnrichedContext","sections","summaries","groups","lines","accessibilityPlugin","theme","animationsPlugin","brandPlugin","brandConfig","_a","newPalette","adjustColor","reducedMotionPlugin","resolved","highContrastPlugin","hex","factor","r","g","b","nr","ng","nb","createPlugin","config","SHOP_THEME","PANEL_EDGELESS_THEME","PRESET_SHOP","DEFAULT_FLOATING_LAUNCHER_WIDTH","PRESET_MINIMAL","PRESET_FULLSCREEN","PRESETS","getPreset","id","index_core_default","initAgentWidget","serializeHook","hook","fn","normalizeHooks","hooks","TEMPLATE_MARKDOWN_JSON_PARSER_TS","TEMPLATE_MARKDOWN_JSON_PARSER_ES5","TEMPLATE_NAV_THEN_CLICK_HANDLER_TS","TEMPLATE_NAV_THEN_CLICK_HANDLER_ES5","TEMPLATE_STREAM_PARSER_CALLBACK_TS","TEMPLATE_STREAM_PARSER_CALLBACK_ES5","detectParserTypeFromStreamParser","streamParser","fnString","getParserTypeFromConfig","config","_a","_b","generateToolCallConfig","indent","lines","key","value","generateMessageActionsConfig","hasSerializableProps","hasHookCallbacks","generateMarkdownConfig","hasOptions","hasDisableDefaultStyles","generateLayoutConfig","hasHeader","hasMessages","avatarKey","avatarValue","k","tsKey","tsValue","generateHooksConfig","appendSerializableObjectEntries","entryValue","appendSerializableObjectBlock","emitTargetSelector","options","generateCodeSnippet","format","cleanConfig","normalizedOptions","generateESMCode","generateScriptInstallerCode","generateScriptAdvancedCode","generateReactComponentCode","generateReactAdvancedCode","generateScriptManualCode","parserType","shouldEmitParserType","chip","buildSerializableConfig","serializableConfig","toolCallConfig","messageActionsConfig","markdownConfig","layoutConfig","headerConfig","messagesConfig","tsConfig","payload","configJson","VERSION","line","DEVICE_DIMENSIONS","ZOOM_STEP","ZOOM_MIN","ZOOM_MAX","STAGE_PADDING","SHADOW_MARGIN","CAROUSEL_CSS","injectStyles","style","computeFitScale","stage","dims","availW","availH","applyScale","wrapper","iframe","scale","device","createDemoCarousel","container","options","items","initialIndex","initialDevice","initialColorScheme","showZoomControls","showDeviceToggle","showColorSchemeToggle","onChange","currentIndex","currentDevice","currentScheme","zoomOverride","lastAutoScale","destroyed","root","createElement","toolbar","toolbarLead","toolbarTrail","prevBtn","createIconButton","navigate","titleWrap","titleBtn","titleText","titleChevron","chevronSvg","renderLucideIcon","dropdown","dropdownOpen","buildDropdownItems","i","item","btn","titleSpan","desc","closeDropdown","goTo","toggleDropdown","e","onDocClick","nextBtn","counterEl","deviceToggle","createToggleGroup","id","rescale","zoomLevelEl","zoomWrap","zoomOut","current","zoomIn","zoomFit","sep","schemeToggle","applySchemeToIframe","sep2","openBtn","_a","body","updateDisplay","delta","next","index","resizeObserver","destroy","scheme","zoom"]}