@mcp-b/char 0.1.1 → 0.1.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.js +1 -1
- package/dist/index.js.map +1 -1
- package/dist/shell-component.js +1 -1
- package/dist/shell-component.js.map +1 -1
- package/dist/web-component-standalone.iife.js +1 -1
- package/dist/web-component-standalone.iife.js.map +1 -1
- package/dist/web-component.js +1 -1
- package/dist/web-component.js.map +1 -1
- package/package.json +12 -12
- package/LICENSE +0 -23
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"web-component.js","names":[],"sources":["../src/host/char-iframe-proxy.ts","../src/utils/constants.ts","../src/utils/css-sanitizer.ts","../src/utils/css-resolver.ts","../src/utils/css-variables.ts","../src/display-mode-policy.ts","../src/utils/display-mode.ts","../src/utils/host-context-merge.ts","../src/utils/parse-boolean-attribute.ts","../src/web-component.tsx"],"sourcesContent":["/**\n * CharIframeProxy\n *\n * Host-side relay that bridges MCP server-to-client responses from the page's\n * TabServerTransport back to the iframe (embedded agent).\n *\n * Client-to-server messages (iframe → host) are handled directly by\n * TabServerTransport via allowedOrigins, so no relay is needed in that\n * direction.\n *\n * Zero runtime dependencies — uses only browser postMessage APIs.\n */\n\nexport class CharIframeProxy {\n\tprivate _cleanup: (() => void)[] = []\n\n\tconstructor(\n\t\tprivate _iframe: HTMLIFrameElement,\n\t\tprivate _iframeOrigin: string,\n\t\tprivate _channelId = 'mcp-default'\n\t) {}\n\n\tstart(): void {\n\t\t// window → iframe: relay server-to-client messages so the iframe client transport picks them up\n\t\tconst fromTab = (event: MessageEvent) => {\n\t\t\tif (event.source !== window) return\n\t\t\tconst d = event.data\n\t\t\tif (!d || d.channel !== this._channelId || d.type !== 'mcp' || d.direction !== 'server-to-client') return\n\t\t\tif (d._proxied) return // defense-in-depth: don't relay our own relayed messages\n\t\t\tif (!this._iframe.contentWindow) {\n\t\t\t\tconsole.error('[CharIframeProxy] Cannot relay to iframe: contentWindow is null')\n\t\t\t\treturn\n\t\t\t}\n\t\t\tthis._iframe.contentWindow.postMessage(d, this._iframeOrigin)\n\t\t}\n\n\t\twindow.addEventListener('message', fromTab)\n\t\tthis._cleanup = [\n\t\t\t() => window.removeEventListener('message', fromTab),\n\t\t]\n\t}\n\n\tdestroy(): void {\n\t\tfor (const fn of this._cleanup) fn()\n\t\tthis._cleanup.length = 0\n\t}\n}\n","export const WEBMCP_PRODUCTION_API_BASE = 'https://app.usechar.ai'\n","/**\n * Maximum allowed length for CSS custom property values transported to iframe.\n *\n * NOTE: This value and the blocked-token policy intentionally mirror\n * `packages/shared-types/src/schemas/embed-bridge.ts` so validation happens\n * both before transport (host) and at contract parse-time (iframe).\n *\n * CONTROL_CHAR_PATTERN here is intentionally more permissive than\n * embed-bridge.ts: it allows tabs/newlines/CR/FF because\n * normalizeCssFormattingWhitespace converts them to spaces before the\n * control-char check. The iframe-side schema (embed-bridge.ts) uses\n * the stricter pattern as defense-in-depth for values that bypass\n * host-side sanitization.\n */\nexport const MAX_CSS_VARIABLE_VALUE_LENGTH = 1024\nexport const MAX_HOST_FONT_CSS_LENGTH = 16_384\n\nconst CONTROL_CHAR_PATTERN = /[\\u0000-\\u0008\\u000b\\u000e-\\u001f\\u007f]/u\nconst FONT_RULES_PATTERN = /^(?:\\s*@font-face\\s*\\{[^{}]*\\}\\s*)+$/iu\nconst CSS_COMMENT_PATTERN = /\\/\\*[\\s\\S]*?\\*\\//g\nconst CSS_FORMATTING_WHITESPACE_PATTERN = /[\\t\\n\\r\\f]+/gu\n\n/**\n * Normalizes CSS values for dangerous-token scanning.\n * Lowercases and removes CSS comments/whitespace/control chars to catch obfuscation.\n */\nfunction normalizeForSecurityScan(value: string): string {\n\tconst withoutComments = value.replace(CSS_COMMENT_PATTERN, '')\n\treturn withoutComments.toLowerCase().replace(/[\\s\\u0000-\\u001f\\u007f]+/gu, '')\n}\n\n/**\n * Collapses CSS formatting whitespace (tabs, newlines, carriage returns, form feeds)\n * into single spaces. Applied to the output value before trimming, so multiline\n * host-provided values (e.g., font stacks) become single-line without losing\n * meaningful token boundaries. This also ensures split-token obfuscation\n * (e.g., \"java\\nscript:\") is collapsed before the security scan.\n */\nfunction normalizeCssFormattingWhitespace(value: string): string {\n\treturn value.replace(CSS_FORMATTING_WHITESPACE_PATTERN, ' ')\n}\n\n/**\n * Sanitizes host-provided CSS variable values before iframe transport.\n * Returns `undefined` for unsafe or invalid values.\n *\n * @param value - The raw CSS property value.\n * @param variableName - Optional variable name for diagnostic logging.\n */\nexport function sanitizeCssVariableValue(value: string, variableName?: string): string | undefined {\n\tconst trimmed = normalizeCssFormattingWhitespace(value).trim()\n\tif (!trimmed) return undefined\n\tif (trimmed.length > MAX_CSS_VARIABLE_VALUE_LENGTH) {\n\t\tconsole.warn(`[Char] CSS variable ${variableName ?? '(unknown)'} rejected: value exceeds max length (${trimmed.length} > ${MAX_CSS_VARIABLE_VALUE_LENGTH})`)\n\t\treturn undefined\n\t}\n\tif (CONTROL_CHAR_PATTERN.test(trimmed)) {\n\t\tconsole.warn(`[Char] CSS variable ${variableName ?? '(unknown)'} rejected: contains control characters`)\n\t\treturn undefined\n\t}\n\n\tconst normalized = normalizeForSecurityScan(trimmed)\n\tconst blockedToken = getBlockedCssVariableToken(normalized)\n\tif (blockedToken) return warnAndReject(variableName, blockedToken)\n\n\treturn trimmed\n}\n\n/**\n * Sanitizes host-provided font CSS before iframe injection.\n * Only `@font-face` rules are allowed.\n */\nexport function sanitizeHostFontCss(fontCss: string): string | undefined {\n\tconst trimmed = fontCss.trim()\n\tif (!trimmed) return undefined\n\tif (trimmed.length > MAX_HOST_FONT_CSS_LENGTH) {\n\t\tconsole.warn(`[Char] Host font CSS rejected: value exceeds max length (${trimmed.length} > ${MAX_HOST_FONT_CSS_LENGTH})`)\n\t\treturn undefined\n\t}\n\tif (CONTROL_CHAR_PATTERN.test(trimmed)) {\n\t\tconsole.warn('[Char] Host font CSS rejected: contains control characters')\n\t\treturn undefined\n\t}\n\n\tconst normalized = normalizeForSecurityScan(trimmed)\n\tconst blockedToken = getBlockedFontToken(normalized)\n\tif (blockedToken) {\n\t\tconsole.warn(`[Char] Host font CSS rejected: contains blocked token \"${blockedToken}\"`)\n\t\treturn undefined\n\t}\n\n\tconst withoutComments = trimmed.replace(/\\/\\*[\\s\\S]*?\\*\\//g, '').trim()\n\tif (!FONT_RULES_PATTERN.test(withoutComments)) {\n\t\tconsole.warn('[Char] Host font CSS rejected: only @font-face rules are allowed')\n\t\treturn undefined\n\t}\n\n\treturn trimmed\n}\n\nfunction getBlockedCssVariableToken(normalizedValue: string): string | undefined {\n\tif (normalizedValue.includes('javascript:')) return 'javascript:'\n\tif (normalizedValue.includes('vbscript:')) return 'vbscript:'\n\tif (normalizedValue.includes('expression(')) return 'expression('\n\tif (normalizedValue.includes('@import')) return '@import'\n\tif (normalizedValue.includes('-moz-binding')) return '-moz-binding'\n\tif (normalizedValue.includes('url(')) return 'url('\n\treturn undefined\n}\n\nfunction getBlockedFontToken(normalizedValue: string): string | undefined {\n\tif (normalizedValue.includes('javascript:')) return 'javascript:'\n\tif (normalizedValue.includes('vbscript:')) return 'vbscript:'\n\tif (normalizedValue.includes('expression(')) return 'expression('\n\tif (normalizedValue.includes('-moz-binding')) return '-moz-binding'\n\treturn undefined\n}\n\nfunction warnAndReject(variableName: string | undefined, blockedToken: string): undefined {\n\tconsole.warn(`[Char] CSS variable ${variableName ?? '(unknown)'} rejected: contains blocked token \"${blockedToken}\"`)\n\treturn undefined\n}\n","/**\n * CSS var() Resolver\n *\n * Recursively resolves CSS custom property references (`var(--x, fallback)`)\n * to concrete values using getComputedStyle(). Necessary because `var()`\n * references are not valid across iframe boundaries.\n *\n * Handles nested var() references, fallbacks, and cycle detection.\n */\n\nexport const MAX_CSS_VAR_RESOLVE_DEPTH = 12\n\nexport function findMatchingParen(value: string, openParenIndex: number): number {\n\tlet depth = 1\n\tfor (let i = openParenIndex + 1; i < value.length; i++) {\n\t\tconst char = value[i]\n\t\tif (char === '(') depth += 1\n\t\telse if (char === ')') depth -= 1\n\t\tif (depth === 0) return i\n\t}\n\treturn -1\n}\n\nexport function splitTopLevelComma(value: string): [string, string | undefined] {\n\tlet depth = 0\n\tfor (let i = 0; i < value.length; i++) {\n\t\tconst char = value[i]\n\t\tif (char === '(') depth += 1\n\t\telse if (char === ')') depth = Math.max(0, depth - 1)\n\t\telse if (char === ',' && depth === 0) return [value.slice(0, i), value.slice(i + 1)]\n\t}\n\treturn [value, undefined]\n}\n\nexport function resolveFallback(\n\tfallback: string | undefined,\n\tcomputed: CSSStyleDeclaration,\n\tseen: Set<string>,\n\tdepth: number,\n): string | undefined {\n\tif (!fallback) return undefined\n\treturn resolveCssValue(fallback, computed, seen, depth + 1) || undefined\n}\n\nexport function resolveCssVariable(\n\tvariableName: string,\n\tfallback: string | undefined,\n\tcomputed: CSSStyleDeclaration,\n\tseen: Set<string>,\n\tdepth: number,\n): string | undefined {\n\tif (depth > MAX_CSS_VAR_RESOLVE_DEPTH) {\n\t\tconsole.warn(`[Char] CSS variable resolution depth exceeded for \"${variableName}\". Possible circular reference.`)\n\t\treturn fallback?.trim()\n\t}\n\n\tif (!variableName.startsWith('--') || seen.has(variableName)) {\n\t\treturn resolveFallback(fallback, computed, seen, depth)\n\t}\n\n\tconst nextSeen = new Set(seen)\n\tnextSeen.add(variableName)\n\n\tconst rawValue = computed.getPropertyValue(variableName).trim()\n\tif (rawValue) {\n\t\tconst resolvedRaw = resolveCssValue(rawValue, computed, nextSeen, depth + 1)\n\t\tif (resolvedRaw) return resolvedRaw\n\t}\n\n\treturn resolveFallback(fallback, computed, seen, depth)\n}\n\nexport function resolveCssValue(\n\tvalue: string,\n\tcomputed: CSSStyleDeclaration,\n\tseen: Set<string>,\n\tdepth = 0,\n): string {\n\tconst trimmed = value.trim()\n\tif (!trimmed || !trimmed.includes('var(') || depth > MAX_CSS_VAR_RESOLVE_DEPTH) {\n\t\treturn trimmed\n\t}\n\n\tlet cursor = 0\n\tlet resolved = ''\n\tlet replacedAny = false\n\n\twhile (cursor < trimmed.length) {\n\t\tconst varStart = trimmed.indexOf('var(', cursor)\n\t\tif (varStart === -1) {\n\t\t\tresolved += trimmed.slice(cursor)\n\t\t\tbreak\n\t\t}\n\n\t\tresolved += trimmed.slice(cursor, varStart)\n\n\t\tconst openParenIndex = varStart + 3\n\t\tconst closeParenIndex = findMatchingParen(trimmed, openParenIndex)\n\t\tif (closeParenIndex === -1) {\n\t\t\tresolved += trimmed.slice(varStart)\n\t\t\tbreak\n\t\t}\n\n\t\tconst rawArgs = trimmed.slice(openParenIndex + 1, closeParenIndex)\n\t\tconst [rawVariableName, rawFallback] = splitTopLevelComma(rawArgs)\n\t\tconst variableName = rawVariableName.trim()\n\t\tconst fallback = rawFallback?.trim()\n\n\t\tconst replacement = variableName\n\t\t\t? resolveCssVariable(variableName, fallback, computed, seen, depth + 1)\n\t\t\t: undefined\n\n\t\tif (replacement !== undefined) {\n\t\t\tresolved += replacement\n\t\t\treplacedAny = true\n\t\t} else {\n\t\t\tresolved += trimmed.slice(varStart, closeParenIndex + 1)\n\t\t}\n\n\t\tcursor = closeParenIndex + 1\n\t}\n\n\tconst normalized = resolved.trim()\n\tif (!replacedAny || !normalized.includes('var(')) {\n\t\treturn normalized\n\t}\n\n\treturn resolveCssValue(normalized, computed, seen, depth + 1)\n}\n","/**\n * Public CSS variable names for the Char widget.\n *\n * These are the external API — host pages set these to customise the widget.\n * We maintain static lists because `getComputedStyle()` cannot enumerate\n * custom properties.\n *\n * Source of truth:\n * - `src/styles/globals.css` (`--char-*`)\n * - WebMCP ext-apps UI spec (`--color-*`, `--font-*`, etc.)\n */\n\nconst CHAR_PUBLIC_VARIABLE_NAMES = [\n\t// Colors - Core\n\t'--char-color-background',\n\t'--char-color-foreground',\n\t'--char-color-card',\n\t'--char-color-card-foreground',\n\t'--char-color-popover',\n\t'--char-color-popover-foreground',\n\t'--char-color-primary',\n\t'--char-color-primary-foreground',\n\t'--char-color-secondary',\n\t'--char-color-secondary-foreground',\n\t'--char-color-muted',\n\t'--char-color-muted-foreground',\n\t'--char-color-accent',\n\t'--char-color-accent-foreground',\n\t'--char-color-destructive',\n\t'--char-color-destructive-foreground',\n\t'--char-color-border',\n\n\t// Colors - Semantic\n\t'--char-color-success',\n\t'--char-color-warning',\n\t'--char-color-error',\n\n\t// Colors - Derived\n\t'--char-color-input',\n\t'--char-color-ring',\n\n\t// Message Bubbles\n\t'--char-user-bubble-bg',\n\t'--char-user-bubble-text',\n\t'--char-assistant-bubble-bg',\n\t'--char-assistant-bubble-text',\n\n\t// Composer\n\t'--char-composer-bg',\n\t'--char-composer-border',\n\t'--char-composer-text',\n\t'--char-composer-placeholder',\n\t'--char-composer-button-bg',\n\t'--char-composer-button-text',\n\n\t// Tool Cards\n\t'--char-tool-bg',\n\t'--char-tool-border',\n\t'--char-tool-text',\n\t'--char-tool-header-bg',\n\t'--char-tool-approve-bg',\n\t'--char-tool-approve-text',\n\t'--char-tool-deny-bg',\n\t'--char-tool-deny-text',\n\n\t// Code Blocks\n\t'--char-code-bg',\n\t'--char-code-text',\n\t'--char-code-header-bg',\n\n\t// Sizing\n\t'--char-radius',\n\t'--char-radius-sm',\n\t'--char-radius-md',\n\t'--char-radius-lg',\n\t'--char-radius-xl',\n\t'--char-radius-2xl',\n\t'--char-radius-3xl',\n\t'--char-radius-full',\n\t'--char-spacing-unit',\n\n\t// Typography\n\t'--char-font-sans',\n\t'--char-font-mono',\n\t'--char-font-size-xs',\n\t'--char-font-size-sm',\n\t'--char-font-size-base',\n\t'--char-font-size-lg',\n\n\t// Motion\n\t'--char-duration-fast',\n\t'--char-duration-normal',\n\t'--char-duration-slow',\n\t'--char-easing-default',\n\t'--char-easing-spring',\n\n\t// Z-index\n\t'--char-z-base',\n\t'--char-z-content',\n\t'--char-z-overlay',\n\t'--char-z-max',\n\n\t// Shadows\n\t'--char-shadow-sm',\n\t'--char-shadow-md',\n\t'--char-shadow-lg',\n\n\t// Blur\n\t'--char-blur-sm',\n\t'--char-blur-md',\n\t'--char-blur-lg',\n] as const\n\nconst MCP_UI_STYLE_VARIABLE_NAMES = [\n\t// Background colors\n\t'--color-background-primary',\n\t'--color-background-secondary',\n\t'--color-background-tertiary',\n\t'--color-background-inverse',\n\t'--color-background-ghost',\n\t'--color-background-info',\n\t'--color-background-danger',\n\t'--color-background-success',\n\t'--color-background-warning',\n\t'--color-background-disabled',\n\n\t// Text colors\n\t'--color-text-primary',\n\t'--color-text-secondary',\n\t'--color-text-tertiary',\n\t'--color-text-inverse',\n\t'--color-text-ghost',\n\t'--color-text-info',\n\t'--color-text-danger',\n\t'--color-text-success',\n\t'--color-text-warning',\n\t'--color-text-disabled',\n\n\t// Border colors\n\t'--color-border-primary',\n\t'--color-border-secondary',\n\t'--color-border-tertiary',\n\t'--color-border-inverse',\n\t'--color-border-ghost',\n\t'--color-border-info',\n\t'--color-border-danger',\n\t'--color-border-success',\n\t'--color-border-warning',\n\t'--color-border-disabled',\n\n\t// Ring colors\n\t'--color-ring-primary',\n\t'--color-ring-secondary',\n\t'--color-ring-inverse',\n\t'--color-ring-info',\n\t'--color-ring-danger',\n\t'--color-ring-success',\n\t'--color-ring-warning',\n\n\t// Typography - Family\n\t'--font-sans',\n\t'--font-mono',\n\n\t// Typography - Weight\n\t'--font-weight-normal',\n\t'--font-weight-medium',\n\t'--font-weight-semibold',\n\t'--font-weight-bold',\n\n\t// Typography - Text Size\n\t'--font-text-xs-size',\n\t'--font-text-sm-size',\n\t'--font-text-md-size',\n\t'--font-text-lg-size',\n\n\t// Typography - Heading Size\n\t'--font-heading-xs-size',\n\t'--font-heading-sm-size',\n\t'--font-heading-md-size',\n\t'--font-heading-lg-size',\n\t'--font-heading-xl-size',\n\t'--font-heading-2xl-size',\n\t'--font-heading-3xl-size',\n\n\t// Typography - Text Line Height\n\t'--font-text-xs-line-height',\n\t'--font-text-sm-line-height',\n\t'--font-text-md-line-height',\n\t'--font-text-lg-line-height',\n\n\t// Typography - Heading Line Height\n\t'--font-heading-xs-line-height',\n\t'--font-heading-sm-line-height',\n\t'--font-heading-md-line-height',\n\t'--font-heading-lg-line-height',\n\t'--font-heading-xl-line-height',\n\t'--font-heading-2xl-line-height',\n\t'--font-heading-3xl-line-height',\n\n\t// Border radius\n\t'--border-radius-xs',\n\t'--border-radius-sm',\n\t'--border-radius-md',\n\t'--border-radius-lg',\n\t'--border-radius-xl',\n\t'--border-radius-full',\n\n\t// Border width\n\t'--border-width-regular',\n\n\t// Shadows\n\t'--shadow-hairline',\n\t'--shadow-sm',\n\t'--shadow-md',\n\t'--shadow-lg',\n] as const\n\nexport const CHAR_CSS_VARIABLE_NAMES = [\n\t...CHAR_PUBLIC_VARIABLE_NAMES,\n\t...MCP_UI_STYLE_VARIABLE_NAMES,\n] as const\n","import type { CharDisplayMode } from './types'\nexport type { CharDisplayMode } from './types'\n\ntype AssertTrue<T extends true> = T\n\n/**\n * Default display modes advertised by Char hosts.\n *\n * Order matters for fallback behavior. When a preferred mode is unavailable,\n * the first available mode is selected.\n *\n * @public\n */\nexport const DEFAULT_AVAILABLE_DISPLAY_MODES = [\n\t'inline',\n\t'fullscreen',\n\t'pip',\n] as const satisfies readonly CharDisplayMode[]\n\n/**\n * Allowed display mode values for Char host policy resolution.\n */\ntype DisplayModesFromDefaults = (typeof DEFAULT_AVAILABLE_DISPLAY_MODES)[number]\n\ntype _AssertDisplayModesStayInSync = AssertTrue<\n\t[DisplayModesFromDefaults] extends [CharDisplayMode]\n\t\t? [CharDisplayMode] extends [DisplayModesFromDefaults]\n\t\t\t? true\n\t\t\t: false\n\t\t: false\n>\n\n/**\n * Runtime guard for Char display mode values.\n *\n * @param value - Raw value from host attributes, events, or external input.\n * @returns `true` when the value is one of `inline`, `fullscreen`, or `pip`.\n *\n * @public\n */\nexport function isDisplayMode(\n\tvalue: string | null | undefined,\n): value is CharDisplayMode {\n\treturn value === 'inline' || value === 'fullscreen' || value === 'pip'\n}\n\n/**\n * Checks whether a display mode represents an expanded agent surface.\n *\n * Expanded modes are:\n * - `inline` (panel-style)\n * - `fullscreen` (viewport-filling)\n *\n * Collapsed mode is:\n * - `pip` (launcher-only)\n *\n * @param mode - Current display mode from host context.\n * @returns `true` for expanded modes and `false` for collapsed/undefined modes.\n *\n * @public\n */\nexport function isExpandedDisplayMode(mode: CharDisplayMode | undefined): boolean {\n\treturn mode === 'inline' || mode === 'fullscreen'\n}\n\n/**\n * Resolves a preferred display mode against host-supported modes.\n *\n * An empty `availableModes` array means \"no constraint\" — the preferred mode\n * is returned unconditionally. This is intentional: it allows callers to opt\n * out of mode filtering without a separate code path.\n *\n * @param preferred - Preferred mode requested by host policy.\n * @param availableModes - Supported modes advertised by the host.\n * @returns Preferred mode when supported, otherwise the first available mode.\n *\n * @public\n */\nexport function resolveSupportedDisplayMode(\n\tpreferred: CharDisplayMode,\n\tavailableModes: readonly CharDisplayMode[] = DEFAULT_AVAILABLE_DISPLAY_MODES,\n): CharDisplayMode {\n\tif (availableModes.length === 0) {\n\t\treturn preferred\n\t}\n\n\tif (availableModes.includes(preferred)) {\n\t\treturn preferred\n\t}\n\n\tconsole.debug(`[Char] Display mode \"${preferred}\" not in available modes [${availableModes.join(', ')}], using \"${availableModes[0]}\"`)\n\treturn availableModes[0]\n}\n\n/**\n * Resolves Char's standard host display-mode policy.\n *\n * Policy:\n * - Closed state to `pip`\n * - Open on desktop to `inline`\n * - Open on narrow viewport to `fullscreen`\n *\n * The resolved mode is then constrained to `availableModes`.\n *\n * @param options - Host orchestration inputs: `open`, `isNarrowViewport`,\n * and optional `availableModes`.\n * @returns Final mode that satisfies policy and availability constraints.\n *\n * @public\n */\nexport function resolvePolicyDisplayMode(options: {\n\topen: boolean\n\tisNarrowViewport: boolean\n\tavailableModes?: readonly CharDisplayMode[]\n}): CharDisplayMode {\n\tconst availableModes = options.availableModes ?? DEFAULT_AVAILABLE_DISPLAY_MODES\n\tconst preferred: CharDisplayMode = options.open\n\t\t? options.isNarrowViewport\n\t\t\t? 'fullscreen'\n\t\t\t: 'inline'\n\t\t: 'pip'\n\n\treturn resolveSupportedDisplayMode(preferred, availableModes)\n}\n","import type { CharDisplayMode } from '../types'\n\nimport { isDisplayMode } from '../display-mode-policy'\n\nexport function resolveDisplayModeFromAttribute(\n\tdisplayModeAttr: string | null,\n): CharDisplayMode | undefined {\n\tif (!isDisplayMode(displayModeAttr)) {\n\t\treturn undefined\n\t}\n\n\treturn displayModeAttr\n}\n","/**\n * Host Context Merge Utilities\n *\n * Canonical merge logic for CharHostContext deltas. Used by both the host-side\n * web component and the iframe-side useHostContext hook to ensure identical\n * merge behavior on both sides of the postMessage bridge.\n */\n\nimport type { CharHostContext } from '../types'\n\nexport function mergeStyles(\n\tcurrent: CharHostContext['styles'] | undefined,\n\tpatch: CharHostContext['styles'] | undefined,\n): CharHostContext['styles'] | undefined {\n\tif (!patch) return current\n\treturn {\n\t\t...current,\n\t\t...patch,\n\t\t// variables are sent as a snapshot so removals propagate\n\t\tvariables: patch.variables ?? current?.variables,\n\t\tfonts: patch.fonts ?? current?.fonts,\n\t}\n}\n\nexport function shallowMerge<T extends object>(\n\tcurrent: T | undefined,\n\tpatch: T | undefined,\n): T | undefined {\n\tif (!patch) return current\n\treturn { ...current, ...patch }\n}\n\nexport function mergeHostContext(\n\tcurrent: CharHostContext | null,\n\tpatch: CharHostContext,\n): CharHostContext {\n\tconst previous = current ?? {}\n\treturn {\n\t\t...previous,\n\t\t...patch,\n\t\tstyles: mergeStyles(previous.styles, patch.styles),\n\t\tcontainerDimensions: shallowMerge(previous.containerDimensions, patch.containerDimensions),\n\t\tdeviceCapabilities: shallowMerge(previous.deviceCapabilities, patch.deviceCapabilities),\n\t\tsafeAreaInsets: shallowMerge(previous.safeAreaInsets, patch.safeAreaInsets),\n\t\thostCapabilities: shallowMerge(previous.hostCapabilities, patch.hostCapabilities),\n\t}\n}\n","/**\n * Attribute helper treating any value except `null` and `'false'` as enabled.\n */\nexport function parseBooleanAttribute(value: string | null): boolean {\n\treturn value !== null && value !== 'false'\n}\n","/**\n * Char - Web Component (iframe-based)\n *\n * A vanilla custom element that creates an iframe pointing to the SaaS app's\n * `/embed/` entrypoint. Exposes `connect()`, CSS variable theming, and DOM events\n * while keeping host and iframe concerns isolated.\n *\n * ## Authentication (Imperative API)\n *\n * Use the `connect()` method for secure authentication.\n *\n * @example Vanilla JS\n * ```js\n * const agent = document.querySelector('char-agent')\n * agent.connect({ idToken: 'eyJhbGciOi...', clientId: 'your-client-id' })\n * ```\n *\n * @example React\n * ```tsx\n * const agentRef = useRef<CharAgentElement>(null)\n * useEffect(() => {\n * agentRef.current?.connect({ idToken: session.idToken, clientId: 'your-client-id' })\n * }, [session.idToken])\n * return <char-agent ref={agentRef} />\n * ```\n *\n * @example CSS variable theming\n * ```css\n * char-agent {\n * --char-color-primary: #0f766e;\n * --char-radius: 12px;\n * }\n * ```\n */\n\nimport type {\n\tCharErrorDetail,\n\tCharContainerDimensions,\n\tCharDeviceCapabilities,\n\tCharDisplayMode,\n\tCharHostCapabilities,\n\tCharHostContext,\n\tCharHostToIframeMessage,\n\tCharOpenLinkDetail,\n\tCharRequestDisplayModeDetail,\n\tCharSizeChangedDetail,\n\tConnectOptions,\n\tDevModeConfig,\n\tTicketAuth,\n} from './types'\n\nimport { CharIframeProxy } from './host/char-iframe-proxy'\nimport { WEBMCP_PRODUCTION_API_BASE } from './utils/constants'\nimport { sanitizeCssVariableValue } from './utils/css-sanitizer'\nimport { resolveCssValue } from './utils/css-resolver'\nimport { CHAR_CSS_VARIABLE_NAMES } from './utils/css-variables'\nimport { resolveDisplayModeFromAttribute } from './utils/display-mode'\nimport { mergeHostContext } from './utils/host-context-merge'\n\n/**\n * Shared options used by custom events emitted from the host element.\n */\nconst CHAR_CUSTOM_EVENT_OPTIONS = { bubbles: true, composed: true } as const\n\n/**\n * Inline iframe styles used to make the host element a transparent container.\n */\nconst IFRAME_STYLE = 'width:100%;height:100%;border:none;display:block;'\n\n/**\n * Display mode values advertised to the embedded runtime.\n */\nconst DISPLAY_MODES: readonly CharDisplayMode[] = ['inline', 'fullscreen', 'pip']\n\n/**\n * Minimal structural shape for iframe-originated messages.\n */\ninterface CharIframeMessageData extends Record<string, unknown> {\n\ttype: string\n}\n\n/**\n * Normalizes an API key by trimming whitespace and collapsing empty strings.\n */\nfunction normalizeApiKey(value?: string): string | undefined {\n\tconst trimmed = value?.trim()\n\treturn trimmed ? trimmed : undefined\n}\n\n/**\n * Produces a sanitized dev-mode configuration and drops empty payloads.\n */\nfunction resolveDevMode(devMode?: DevModeConfig): DevModeConfig | undefined {\n\tif (!devMode) return undefined\n\n\tconst normalized = {\n\t\tanthropicApiKey: normalizeApiKey(devMode.anthropicApiKey),\n\t\topenaiApiKey: normalizeApiKey(devMode.openaiApiKey),\n\t\tuseLocalApi: devMode.useLocalApi === true ? true : undefined,\n\t} satisfies DevModeConfig\n\n\tconst hasAnyValue =\n\t\t!!normalized.anthropicApiKey || !!normalized.openaiApiKey || !!normalized.useLocalApi\n\n\treturn hasAnyValue ? normalized : undefined\n}\n\nimport { parseBooleanAttribute } from './utils/parse-boolean-attribute'\n\n/**\n * Runtime guard for the message envelope emitted by the iframe.\n */\nfunction isCharIframeMessageData(value: unknown): value is CharIframeMessageData {\n\tif (!value || typeof value !== 'object') return false\n\tconst maybeType = (value as { type?: unknown }).type\n\treturn typeof maybeType === 'string' && maybeType.startsWith('char-')\n}\n\nfunction getStringOrUndefined(value: unknown): string | undefined {\n\treturn typeof value === 'string' ? value : undefined\n}\n\nfunction getNumberOrUndefined(value: unknown): number | undefined {\n\treturn typeof value === 'number' ? value : undefined\n}\n\nexport type {\n\tCharContainerDimensions,\n\tCharDeviceCapabilities,\n\tCharDisplayMode,\n\tCharErrorDetail,\n\tCharHostCapabilities,\n\tCharHostContext,\n\tCharHostStyles,\n\tCharOpenLinkDetail,\n\tCharPlatform,\n\tCharRequestDisplayModeDetail,\n\tCharSafeAreaInsets,\n\tCharSizeChangedDetail,\n\tCharTheme,\n\tConnectOptions,\n\tDevModeConfig,\n\tTicketAuth,\n} from './types'\n\n/**\n * Get the iframe src URL.\n * Points to the SaaS app's static /embed/ entrypoint with the parent origin as a query parameter.\n *\n * @param apiBase - Public base URL for the Char SaaS application.\n * @param parentOrigin - Origin of the host page embedding the widget.\n * @returns Fully qualified iframe URL targeting the embed entrypoint.\n */\nfunction getIframeSrc(apiBase: string, parentOrigin: string): string {\n\treturn `${apiBase}/embed/?parentOrigin=${encodeURIComponent(parentOrigin)}`\n}\n\n/**\n * Extract host CSS variable values from a host element.\n * Includes both `--char-*` and MCP UI style `--color-*`/`--font-*` tokens.\n * Uses a static list since getComputedStyle() cannot enumerate custom properties.\n * Values containing `var()` are resolved to concrete values before transport\n * so the iframe can safely apply them in its own CSS scope.\n *\n * @param el - Host `<char-agent>` element.\n * @returns A snapshot of resolved CSS custom properties for the iframe.\n */\nfunction extractCharVariables(el: HTMLElement): Record<string, string> {\n\tconst computed = getComputedStyle(el)\n\tconst vars: Record<string, string> = {}\n\tfor (const name of CHAR_CSS_VARIABLE_NAMES) {\n\t\tconst rawValue = computed.getPropertyValue(name).trim()\n\t\tif (rawValue) {\n\t\t\tconst resolvedValue = resolveCssValue(rawValue, computed, new Set([name]))\n\t\t\tconst sanitizedValue = sanitizeCssVariableValue(resolvedValue || rawValue, name)\n\t\t\tif (sanitizedValue !== undefined) {\n\t\t\t\tvars[name] = sanitizedValue\n\t\t\t}\n\t\t}\n\t}\n\treturn vars\n}\n\n/**\n * Detect whether the host page is in dark mode.\n * Checks the data-theme attribute, the `dark` class on <html>, and the prefers-color-scheme media query.\n *\n * @returns `true` when the host page should be treated as dark mode.\n */\nfunction isDarkMode(): boolean {\n\tif (typeof document === 'undefined') return false\n\tconst themeAttr = document.documentElement.getAttribute('data-theme')\n\tif (themeAttr === 'dark') return true\n\tif (themeAttr === 'light') return false\n\treturn (\n\t\tdocument.documentElement.classList.contains('dark') ||\n\t\twindow.matchMedia('(prefers-color-scheme: dark)').matches\n\t)\n}\n\n/**\n * Deep equality check using JSON.stringify. Sufficient for the flat/nested\n * structures in CharHostContext (no circular refs, no functions).\n *\n * @param a - First value.\n * @param b - Second value.\n * @returns `true` when both values serialize identically.\n */\nfunction deepEqual(a: unknown, b: unknown): boolean {\n\t// Key-order sensitive — acceptable here because both sides produce objects\n\t// with deterministic key order (host context built from static field lists).\n\treturn JSON.stringify(a) === JSON.stringify(b)\n}\n\n/**\n * Parses a CSS pixel value into a finite number.\n */\nfunction parsePxToNumber(value: string): number {\n\tconst parsed = Number.parseFloat(value)\n\treturn Number.isFinite(parsed) ? parsed : 0\n}\n\n/**\n * Reads safe-area inset values using `env(safe-area-inset-*)`.\n */\nfunction getSafeAreaInsets(): CharHostContext['safeAreaInsets'] | undefined {\n\tif (typeof document === 'undefined') return undefined\n\n\tconst probe = document.createElement('div')\n\tprobe.style.cssText = [\n\t\t'position:fixed',\n\t\t'visibility:hidden',\n\t\t'pointer-events:none',\n\t\t'inset:0 auto auto 0',\n\t\t'padding-top:env(safe-area-inset-top)',\n\t\t'padding-right:env(safe-area-inset-right)',\n\t\t'padding-bottom:env(safe-area-inset-bottom)',\n\t\t'padding-left:env(safe-area-inset-left)',\n\t].join(';')\n\n\tdocument.documentElement.appendChild(probe)\n\tconst computed = getComputedStyle(probe)\n\tconst insets = {\n\t\ttop: parsePxToNumber(computed.paddingTop),\n\t\tright: parsePxToNumber(computed.paddingRight),\n\t\tbottom: parsePxToNumber(computed.paddingBottom),\n\t\tleft: parsePxToNumber(computed.paddingLeft),\n\t}\n\tprobe.remove()\n\n\treturn insets\n}\n\n/**\n * Identifies the host platform category from the user agent.\n * Note: 'desktop' detection not yet implemented; non-mobile UAs resolve to 'web'.\n */\nfunction detectPlatform(): 'web' | 'desktop' | 'mobile' {\n\tif (typeof navigator === 'undefined') return 'web'\n\tconst ua = navigator.userAgent.toLowerCase()\n\tif (/(iphone|ipad|ipod|android|mobile)/.test(ua)) return 'mobile'\n\treturn 'web'\n}\n\n/**\n * Captures lightweight host device capability hints.\n */\nfunction getDeviceCapabilities(): CharDeviceCapabilities {\n\tif (typeof window === 'undefined' || typeof navigator === 'undefined') {\n\t\treturn {}\n\t}\n\treturn {\n\t\ttouch: 'ontouchstart' in window || navigator.maxTouchPoints > 0,\n\t\thover: window.matchMedia('(hover: hover)').matches,\n\t\tpointerFine: window.matchMedia('(pointer: fine)').matches,\n\t}\n}\n\n/**\n * `<char-agent>` custom element.\n *\n * Creates an iframe pointing to the SaaS app's /embed/ entrypoint and relays\n * auth, styles, dark mode, display mode, and MCP messages via postMessage.\n *\n * Uses a unified `char-context` message with diffing (only changed fields\n * are sent) instead of separate messages per concern.\n *\n * @public\n */\nclass CharAgentElement extends HTMLElement {\n\tstatic observedAttributes = [\n\t\t'dev-mode',\n\t\t'enable-debug-tools',\n\t\t'display-mode',\n\t\t'api-base',\n\t]\n\n\t/**\n\t * React 19-friendly property form of `dev-mode`.\n\t * Read once during `connectedCallback`.\n\t */\n\tdeclare devMode?: DevModeConfig\n\n\t/**\n\t * React 19-friendly property form of `api-base`.\n\t * Read once during `connectedCallback`.\n\t */\n\tdeclare apiBase?: string\n\n\t/** Active iframe element owned by this custom element instance. */\n\tprivate _iframe: HTMLIFrameElement | null = null\n\t/** MCP relay between host window and embed iframe. */\n\tprivate _proxy: CharIframeProxy | null = null\n\t/** Indicates whether the iframe has emitted `char-ready`. */\n\tprivate _iframeReady = false\n\t/** Bound window message handler for iframe-originated events. */\n\tprivate _messageListener: ((event: MessageEvent) => void) | null = null\n\t/** Observer that tracks host theme mutations. */\n\tprivate _darkModeObserver: MutationObserver | null = null\n\t/** Media query object for `prefers-color-scheme`. */\n\tprivate _darkModeMediaQuery: MediaQueryList | null = null\n\t/** Registered media query callback. */\n\tprivate _darkModeMediaHandler: (() => void) | null = null\n\t/** Observer that tracks host style/class mutations. */\n\tprivate _styleObserver: MutationObserver | null = null\n\t/** Observer that tracks container size changes. */\n\tprivate _containerResizeObserver: ResizeObserver | null = null\n\t/** In-flight teardown request waiting for iframe acknowledgement. */\n\tprivate _teardownPending:\n\t\t| { requestId: string; resolve: () => void; timeoutId: number }\n\t\t| null = null\n\t/** Monotonic sequence used to construct teardown request IDs. */\n\tprivate _teardownSequence = 0\n\t/** Re-entrancy guard used during disconnect lifecycle. */\n\tprivate _isDisconnecting = false\n\n\t/** Last host context snapshot used for delta emission. */\n\tprivate _hostContext: CharHostContext = {}\n\n\t/** Pending credentials that are flushed when the iframe is ready. */\n\tprivate _pendingAuth: {\n\t\tidToken?: string\n\t\tclientId?: string\n\t\torganizationId?: string\n\t\tticketAuth?: TicketAuth\n\t\tanthropicApiKey?: string\n\t} | null = null\n\n\t/**\n\t * Mount lifecycle: builds iframe runtime, starts proxying, and starts host observers.\n\t * The existing runtime is reused when reconnecting during fast detach/reattach cycles.\n\t */\n\tconnectedCallback() {\n\t\tif (this._hasActiveRuntime()) {\n\t\t\tthis._isDisconnecting = false\n\t\t\tthis._teardownSequence = 0\n\t\t\tif (this._teardownPending) {\n\t\t\t\twindow.clearTimeout(this._teardownPending.timeoutId)\n\t\t\t\tthis._teardownPending.resolve()\n\t\t\t\tthis._teardownPending = null\n\t\t\t}\n\t\t\tif (this._iframe && !this.contains(this._iframe)) {\n\t\t\t\tthis.appendChild(this._iframe)\n\t\t\t}\n\t\t\treturn\n\t\t}\n\n\t\tif (!this.style.display) {\n\t\t\tthis.style.display = 'block'\n\t\t}\n\n\t\tconst devMode = this._resolveDevMode()\n\t\tconst resolvedDevMode = resolveDevMode(devMode)\n\t\tconst apiBase =\n\t\t\tthis._resolveApiBaseOverride() ??\n\t\t\t(resolvedDevMode?.useLocalApi ? window.location.origin : WEBMCP_PRODUCTION_API_BASE)\n\t\tconst iframeOrigin = this._resolveIframeOrigin(apiBase)\n\t\tconst iframe = this._createIframe(apiBase)\n\t\tthis._iframe = iframe\n\n\t\t// Queue dev-mode API key for relay to the iframe on char-ready\n\t\tif (resolvedDevMode?.anthropicApiKey) {\n\t\t\tthis._pendingAuth = { anthropicApiKey: resolvedDevMode.anthropicApiKey }\n\t\t}\n\n\t\tthis._proxy = new CharIframeProxy(iframe, iframeOrigin)\n\t\tthis._proxy.start()\n\n\t\tthis._messageListener = this._createMessageListener(iframe, iframeOrigin)\n\t\twindow.addEventListener('message', this._messageListener)\n\n\t\tthis._observeDarkMode()\n\t\tthis._observeStyleChanges()\n\t\tthis._observeContainerDimensions()\n\t\tthis.appendChild(iframe)\n\t}\n\n\t/**\n\t * Determines whether this instance already has an initialized runtime.\n\t */\n\tprivate _hasActiveRuntime(): boolean {\n\t\treturn !!(this._iframe || this._proxy || this._messageListener)\n\t}\n\n\t/**\n\t * Parses and validates the iframe origin from the configured API base URL.\n\t *\n\t * @throws Error When `apiBase` is not a valid URL.\n\t */\n\tprivate _resolveIframeOrigin(apiBase: string): string {\n\t\ttry {\n\t\t\treturn new URL(apiBase).origin\n\t\t} catch {\n\t\t\tthrow new Error(`[Char] Invalid api-base: \"${apiBase}\". Must be a valid URL.`)\n\t\t}\n\t}\n\n\t/**\n\t * Creates a fully configured iframe element for the embed runtime.\n\t */\n\tprivate _createIframe(apiBase: string): HTMLIFrameElement {\n\t\tconst iframe = document.createElement('iframe')\n\t\tiframe.src = getIframeSrc(apiBase, window.location.origin)\n\t\tiframe.style.cssText = IFRAME_STYLE\n\t\tiframe.title = 'Char AI Assistant'\n\t\tiframe.setAttribute('sandbox', 'allow-scripts allow-same-origin allow-forms')\n\t\tiframe.setAttribute('allow', 'microphone')\n\t\treturn iframe\n\t}\n\n\t/**\n\t * Creates the message listener that filters by source/origin and dispatches typed events.\n\t */\n\tprivate _createMessageListener(\n\t\tiframe: HTMLIFrameElement,\n\t\tiframeOrigin: string,\n\t): (event: MessageEvent) => void {\n\t\treturn (event: MessageEvent) => {\n\t\t\t// TODO(security#2): trusted-host assumption currently defers per-origin message rate limits\n\t\t\t// and payload-size caps for iframe -> host postMessage handling.\n\t\t\tif (event.source !== iframe.contentWindow) return\n\t\t\tif (event.origin !== iframeOrigin) return\n\t\t\tif (!isCharIframeMessageData(event.data)) return\n\t\t\tthis._handleIframeMessage(event.data, iframeOrigin)\n\t\t}\n\t}\n\n\t/**\n\t * Routes validated iframe messages to local handlers and host DOM events.\n\t */\n\tprivate _handleIframeMessage(data: CharIframeMessageData, iframeOrigin: string): void {\n\t\tswitch (data.type) {\n\t\t\tcase 'char-ready':\n\t\t\t\tthis._iframeReady = true\n\t\t\t\tthis._sendInitialContext(iframeOrigin)\n\t\t\t\treturn\n\t\t\tcase 'char-initialized':\n\t\t\t\tthis._emitCharEvent('char-initialized')\n\t\t\t\treturn\n\t\t\tcase 'char-size-changed': {\n\t\t\t\tconst width = getNumberOrUndefined(data.width)\n\t\t\t\tconst height = getNumberOrUndefined(data.height)\n\t\t\t\tconst rawSizeMode = getStringOrUndefined(data.displayMode)\n\t\t\t\tconst sizeDisplayMode = rawSizeMode && (DISPLAY_MODES as readonly string[]).includes(rawSizeMode)\n\t\t\t\t\t? (rawSizeMode as CharDisplayMode)\n\t\t\t\t\t: undefined\n\t\t\t\tif (width !== undefined || height !== undefined) {\n\t\t\t\t\tconst detail: CharSizeChangedDetail = { width, height, displayMode: sizeDisplayMode }\n\t\t\t\t\tthis._emitCharEventWithDetail('char-size-changed', detail)\n\t\t\t\t}\n\t\t\t\treturn\n\t\t\t}\n\t\t\tcase 'char-request-display-mode': {\n\t\t\t\tconst rawMode = getStringOrUndefined(data.mode)\n\t\t\t\tconst mode = rawMode && (DISPLAY_MODES as readonly string[]).includes(rawMode)\n\t\t\t\t\t? (rawMode as CharDisplayMode)\n\t\t\t\t\t: undefined\n\t\t\t\tconst detail: CharRequestDisplayModeDetail = { mode }\n\t\t\t\tthis._emitCharEventWithDetail('char-request-display-mode', detail)\n\t\t\t\treturn\n\t\t\t}\n\t\t\tcase 'char-close':\n\t\t\t\tthis._emitCharEvent('char-close')\n\t\t\t\treturn\n\t\t\tcase 'char-error':\n\t\t\t\tthis._emitCharEventWithDetail('char-error', {\n\t\t\t\t\tcode: getStringOrUndefined(data.code) ?? 'UNKNOWN',\n\t\t\t\t\tmessage: getStringOrUndefined(data.message),\n\t\t\t\t})\n\t\t\t\treturn\n\t\t\tcase 'char-open-link': {\n\t\t\t\tconst requestId = getStringOrUndefined(data.requestId)\n\t\t\t\tconst url = getStringOrUndefined(data.url)\n\t\t\t\tif (requestId && url) {\n\t\t\t\t\tthis._handleOpenLinkRequest(requestId, url, iframeOrigin)\n\t\t\t\t} else if (requestId) {\n\t\t\t\t\tthis._iframe?.contentWindow?.postMessage(\n\t\t\t\t\t\t{ type: 'char-open-link-result', requestId, ok: false, error: 'Missing url in char-open-link message' } satisfies CharHostToIframeMessage,\n\t\t\t\t\t\tiframeOrigin,\n\t\t\t\t\t)\n\t\t\t\t} else {\n\t\t\t\t\tconsole.error('[Char] Received char-open-link without requestId')\n\t\t\t\t}\n\t\t\t\treturn\n\t\t\t}\n\t\t\tcase 'char-teardown-complete':\n\t\t\t\tthis._resolvePendingTeardown(getStringOrUndefined(data.requestId))\n\t\t\t\treturn\n\t\t\tdefault:\n\t\t\t\tconsole.warn(`[Char] Unrecognized iframe message type: \"${(data as { type?: string }).type}\"`)\n\t\t\t\treturn\n\t\t}\n\t}\n\n\t/**\n\t * Emits a host-facing custom event without detail payload.\n\t */\n\tprivate _emitCharEvent(type: string): void {\n\t\tthis.dispatchEvent(new CustomEvent(type, CHAR_CUSTOM_EVENT_OPTIONS))\n\t}\n\n\t/**\n\t * Emits a host-facing custom event with detail payload.\n\t */\n\tprivate _emitCharEventWithDetail(type: string, detail: unknown): void {\n\t\tthis.dispatchEvent(new CustomEvent(type, { ...CHAR_CUSTOM_EVENT_OPTIONS, detail }))\n\t}\n\n\t/**\n\t * Emits a standardized `char-error` event.\n\t */\n\tprivate _emitCharError(code: string, message: string): void {\n\t\tconst detail: CharErrorDetail = { code, message }\n\t\tthis._emitCharEventWithDetail('char-error', detail)\n\t}\n\n\t/**\n\t * Unmount lifecycle: requests iframe teardown and releases all host resources.\n\t */\n\tdisconnectedCallback() {\n\t\tif (this._isDisconnecting) return\n\t\tthis._isDisconnecting = true\n\n\t\ttry {\n\t\t\tconst iframeOrigin = this._getIframeOrigin()\n\t\t\tif (this._iframeReady && iframeOrigin) {\n\t\t\t\tthis._requestTeardown(iframeOrigin, 'unmount')\n\t\t\t\t\t.catch((err) => console.error('[Char] Teardown failed:', err))\n\t\t\t\t\t.finally(() => {\n\t\t\t\t\t\tif (this.isConnected) {\n\t\t\t\t\t\t\tthis._isDisconnecting = false\n\t\t\t\t\t\t\treturn\n\t\t\t\t\t\t}\n\t\t\t\t\t\tthis._finalizeDisconnect()\n\t\t\t\t\t})\n\t\t\t\treturn\n\t\t\t}\n\t\t} catch (err) {\n\t\t\tconsole.error('[Char] Error during disconnect:', err)\n\t\t}\n\n\t\tthis._finalizeDisconnect()\n\t}\n\n\t/**\n\t * Releases iframe, observers, listeners, and pending teardown state.\n\t */\n\tprivate _finalizeDisconnect(): void {\n\t\tif (this._proxy) {\n\t\t\tthis._proxy.destroy()\n\t\t\tthis._proxy = null\n\t\t}\n\n\t\tif (this._messageListener) {\n\t\t\twindow.removeEventListener('message', this._messageListener)\n\t\t\tthis._messageListener = null\n\t\t}\n\n\t\tif (this._darkModeObserver) {\n\t\t\tthis._darkModeObserver.disconnect()\n\t\t\tthis._darkModeObserver = null\n\t\t}\n\t\tif (this._darkModeMediaQuery && this._darkModeMediaHandler) {\n\t\t\tthis._darkModeMediaQuery.removeEventListener('change', this._darkModeMediaHandler)\n\t\t\tthis._darkModeMediaQuery = null\n\t\t\tthis._darkModeMediaHandler = null\n\t\t}\n\n\t\tif (this._styleObserver) {\n\t\t\tthis._styleObserver.disconnect()\n\t\t\tthis._styleObserver = null\n\t\t}\n\n\t\tif (this._containerResizeObserver) {\n\t\t\tthis._containerResizeObserver.disconnect()\n\t\t\tthis._containerResizeObserver = null\n\t\t}\n\n\t\tif (this._iframe) {\n\t\t\tthis._iframe.remove()\n\t\t\tthis._iframe = null\n\t\t}\n\n\t\tif (this._teardownPending) {\n\t\t\twindow.clearTimeout(this._teardownPending.timeoutId)\n\t\t\tthis._teardownPending.resolve()\n\t\t\tthis._teardownPending = null\n\t\t}\n\n\t\tthis._iframeReady = false\n\t\tthis._pendingAuth = null\n\t\tthis._hostContext = {}\n\t\tthis._teardownSequence = 0\n\t\tthis._isDisconnecting = false\n\t}\n\n\t/**\n\t * Reacts to observed attribute updates after iframe readiness.\n\t * Attributes that affect iframe boot configuration are intentionally ignored\n\t * after mount (`dev-mode`, `enable-debug-tools`, `api-base`).\n\t */\n\tattributeChangedCallback(name: string, _oldValue: string | null, newValue: string | null) {\n\t\tif (!this._iframe || !this._iframeReady) return\n\n\t\tswitch (name) {\n\t\t\tcase 'display-mode':\n\t\t\t\tthis.setHostContext({\n\t\t\t\t\tdisplayMode: resolveDisplayModeFromAttribute(newValue),\n\t\t\t\t})\n\t\t\t\tbreak\n\t\t\tcase 'dev-mode':\n\t\t\tcase 'enable-debug-tools':\n\t\t\tcase 'api-base':\n\t\t\t\tbreak\n\t\t}\n\t}\n\n\t/**\n\t * Connect to the Char agent with authentication.\n\t *\n\t * The token is stored as a JavaScript property (not as a DOM attribute),\n\t * preventing exposure to DOM inspection and session replay tools.\n\t *\n\t * @param options - Authentication payload for either ID token or ticket auth.\n\t * @returns `true` when the payload is accepted; `false` when validation fails.\n\t */\n\tconnect(options: ConnectOptions): boolean {\n\t\tif (!options?.idToken && !options?.ticketAuth) {\n\t\t\tthis._emitCharError(\n\t\t\t\t'MISSING_TOKEN',\n\t\t\t\t'connect() requires either idToken or ticketAuth parameter',\n\t\t\t)\n\t\t\treturn false\n\t\t}\n\n\t\tif (options?.idToken && !options?.ticketAuth && !options?.clientId) {\n\t\t\tthis._emitCharError(\n\t\t\t\t'MISSING_CLIENT_ID',\n\t\t\t\t'connect() requires clientId when using idToken authentication',\n\t\t\t)\n\t\t\treturn false\n\t\t}\n\n\t\tthis._pendingAuth = {\n\t\t\tidToken: options.ticketAuth ? undefined : options.idToken,\n\t\t\tclientId: options.ticketAuth ? undefined : options.clientId,\n\t\t\torganizationId: options.ticketAuth ? undefined : options.organizationId,\n\t\t\tticketAuth: options.ticketAuth,\n\t\t}\n\n\t\tconst iframeOrigin = this._getIframeOrigin()\n\t\tif (this._iframeReady && iframeOrigin) {\n\t\t\tthis._postAuth(iframeOrigin)\n\t\t}\n\n\t\treturn true\n\t}\n\n\t/**\n\t * Convenience method for declarative wrappers.\n\t * Applies auth when provided, otherwise clears auth.\n\t *\n\t * @param options - Auth payload to connect, or `null` to disconnect.\n\t * @returns Result from `connect()` or `disconnect()`.\n\t */\n\tsetAuth(options: ConnectOptions | null): boolean {\n\t\tif (!options) {\n\t\t\treturn this.disconnect()\n\t\t}\n\t\treturn this.connect(options)\n\t}\n\n\t/**\n\t * Disconnect from the Char agent.\n\t * Clears the authentication token.\n\t *\n\t * @returns `true` when the disconnect message was sent; `false` when the iframe was not ready.\n\t */\n\tdisconnect(): boolean {\n\t\tthis._pendingAuth = null\n\t\tconst iframeOrigin = this._getIframeOrigin()\n\t\tif (this._iframeReady && this._iframe && iframeOrigin) {\n\t\t\tthis._iframe.contentWindow?.postMessage(\n\t\t\t\t{ type: 'char-disconnect' } satisfies CharHostToIframeMessage,\n\t\t\t\tiframeOrigin,\n\t\t\t)\n\t\t\treturn true\n\t\t}\n\t\treturn false\n\t}\n\n\t/**\n\t * Resolves the current iframe origin when mounted.\n\t */\n\tprivate _getIframeOrigin(): string | null {\n\t\tif (!this._iframe?.src) return null\n\t\ttry {\n\t\t\treturn new URL(this._iframe.src).origin\n\t\t} catch {\n\t\t\tconsole.error('[Char] Failed to parse iframe origin from src:', this._iframe.src)\n\t\t\treturn null\n\t\t}\n\t}\n\n\t/**\n\t * Update the host context sent to the iframe.\n\t * Only changed fields are transmitted (diffing pattern).\n\t *\n\t * @param hostContext - Partial host context patch to merge and emit.\n\t */\n\tsetHostContext(hostContext: CharHostContext): void {\n\t\tconst changes: CharHostContext = {}\n\t\tconst nextContext = mergeHostContext(this._hostContext, hostContext)\n\n\t\tlet hasChanges = false\n\t\tfor (const key of Object.keys(hostContext) as Array<keyof CharHostContext>) {\n\t\t\tconst oldValue = this._hostContext[key]\n\t\t\tconst newValue = nextContext[key]\n\t\t\tif (deepEqual(oldValue, newValue)) {\n\t\t\t\tcontinue\n\t\t\t}\n\t\t\t;(changes as Record<string, unknown>)[key] = hostContext[key]\n\t\t\thasChanges = true\n\t\t}\n\t\tif (hasChanges) {\n\t\t\tthis._hostContext = nextContext\n\t\t\tthis._sendContextDelta(changes)\n\t\t}\n\t}\n\n\t/**\n\t * Build and send the full initial context after iframe signals char-ready.\n\t *\n\t * @param iframeOrigin - Trusted origin used for postMessage target filtering.\n\t */\n\tprivate _sendInitialContext(iframeOrigin: string): void {\n\t\tconst vars = extractCharVariables(this)\n\t\tconst theme = isDarkMode() ? 'dark' as const : 'light' as const\n\t\tconst displayModeAttr = this.getAttribute('display-mode')\n\t\tconst locale = typeof navigator !== 'undefined' ? navigator.language : undefined\n\t\tconst timeZone = Intl.DateTimeFormat().resolvedOptions().timeZone\n\t\tconst containerDimensions: CharContainerDimensions = {\n\t\t\twidth: this.clientWidth,\n\t\t\theight: this.clientHeight,\n\t\t}\n\t\tconst safeAreaInsets = getSafeAreaInsets()\n\n\t\tconst displayMode = resolveDisplayModeFromAttribute(displayModeAttr)\n\n\t\tconst hostCapabilities: CharHostCapabilities = {\n\t\t\tsupportedDisplayModes: [...DISPLAY_MODES],\n\t\t\tsupportsOpenLink: true,\n\t\t\tsupportsTeardown: true,\n\t\t}\n\n\t\tconst context: CharHostContext = {\n\t\t\ttheme,\n\t\t\tstyles: { variables: vars },\n\t\t\tdisplayMode,\n\t\t\tavailableDisplayModes: [...DISPLAY_MODES],\n\t\t\tcontainerDimensions,\n\t\t\tlocale,\n\t\t\ttimeZone,\n\t\t\tplatform: detectPlatform(),\n\t\t\tdeviceCapabilities: getDeviceCapabilities(),\n\t\t\tsafeAreaInsets,\n\t\t\tuserAgent: typeof navigator !== 'undefined' ? navigator.userAgent : undefined,\n\t\t\thostCapabilities,\n\t\t}\n\n\t\tthis._hostContext = context\n\t\tif (!this._iframe?.contentWindow) {\n\t\t\tconsole.error('[Char] iframe signaled char-ready but contentWindow is null')\n\t\t\tthis._iframeReady = false\n\t\t\tthis._emitCharError('IFRAME_CONTENT_WINDOW_NULL', 'iframe signaled char-ready but contentWindow is null')\n\t\t\treturn\n\t\t}\n\t\tthis._iframe.contentWindow.postMessage(\n\t\t\t{ type: 'char-context', ...context } satisfies CharHostToIframeMessage,\n\t\t\tiframeOrigin\n\t\t)\n\n\t\tconst debugAttr = this.getAttribute('enable-debug-tools')\n\t\tif (parseBooleanAttribute(debugAttr)) {\n\t\t\tthis._iframe?.contentWindow?.postMessage(\n\t\t\t\t{ type: 'char-debug-tools', enabled: true } satisfies CharHostToIframeMessage,\n\t\t\t\tiframeOrigin\n\t\t\t)\n\t\t}\n\n\t\tif (this._pendingAuth) {\n\t\t\tthis._postAuth(iframeOrigin)\n\t\t}\n\t}\n\n\t/**\n\t * Send a context delta to the iframe.\n\t *\n\t * @param changes - Minimal context patch derived from diffing.\n\t */\n\tprivate _sendContextDelta(changes: CharHostContext): void {\n\t\tif (!this._iframeReady || !this._iframe?.contentWindow) return\n\t\tconst iframeOrigin = this._getIframeOrigin()\n\t\tif (!iframeOrigin) {\n\t\t\tconsole.warn('[Char] Cannot send context delta: iframe origin is null')\n\t\t\treturn\n\t\t}\n\t\tthis._iframe.contentWindow.postMessage(\n\t\t\t{ type: 'char-context', ...changes } satisfies CharHostToIframeMessage,\n\t\t\tiframeOrigin\n\t\t)\n\t}\n\n\t/**\n\t * Post authentication credentials to the iframe.\n\t *\n\t * @param iframeOrigin - Trusted origin used for postMessage target filtering.\n\t */\n\tprivate _postAuth(iframeOrigin: string): void {\n\t\tif (!this._pendingAuth || !this._iframe?.contentWindow) return\n\n\t\tconst message: CharHostToIframeMessage = this._pendingAuth.ticketAuth\n\t\t\t? { type: 'char-auth', ticketAuth: this._pendingAuth.ticketAuth }\n\t\t\t: this._pendingAuth.anthropicApiKey\n\t\t\t\t? { type: 'char-auth', anthropicApiKey: this._pendingAuth.anthropicApiKey }\n\t\t\t\t: {\n\t\t\t\t\t\ttype: 'char-auth',\n\t\t\t\t\t\tidToken: this._pendingAuth.idToken!,\n\t\t\t\t\t\tclientId: this._pendingAuth.clientId,\n\t\t\t\t\t\torganizationId: this._pendingAuth.organizationId,\n\t\t\t\t\t}\n\n\t\tthis._iframe.contentWindow.postMessage(message, iframeOrigin)\n\t}\n\n\t/**\n\t * Requests graceful iframe teardown and resolves once acknowledged or timed out.\n\t *\n\t * @param iframeOrigin - Trusted origin used for postMessage target filtering.\n\t * @param reason - Lifecycle reason for teardown.\n\t * @returns Promise resolved when teardown flow completes.\n\t */\n\tprivate _requestTeardown(\n\t\tiframeOrigin: string,\n\t\treason: 'disconnect' | 'unmount' | 'reload',\n\t): Promise<void> {\n\t\tif (!this._iframeReady || !this._iframe?.contentWindow) {\n\t\t\treturn Promise.resolve()\n\t\t}\n\n\t\tif (this._teardownPending) {\n\t\t\twindow.clearTimeout(this._teardownPending.timeoutId)\n\t\t\tthis._teardownPending.resolve()\n\t\t\tthis._teardownPending = null\n\t\t}\n\n\t\tconst requestId = `teardown-${Date.now()}-${++this._teardownSequence}`\n\t\treturn new Promise((resolve) => {\n\t\t\tconst timeoutId = window.setTimeout(() => {\n\t\t\t\tif (this._teardownPending?.requestId === requestId) {\n\t\t\t\t\tconsole.debug('[Char] Teardown timed out after 1000ms, requestId:', requestId)\n\t\t\t\t\tthis._teardownPending = null\n\t\t\t\t\tresolve()\n\t\t\t\t}\n\t\t\t}, 1000)\n\n\t\t\tthis._teardownPending = {\n\t\t\t\trequestId,\n\t\t\t\ttimeoutId,\n\t\t\t\tresolve: () => {\n\t\t\t\t\twindow.clearTimeout(timeoutId)\n\t\t\t\t\tresolve()\n\t\t\t\t},\n\t\t\t}\n\n\t\t\tthis._iframe?.contentWindow?.postMessage(\n\t\t\t\t{\n\t\t\t\t\ttype: 'char-teardown',\n\t\t\t\t\trequestId,\n\t\t\t\t\treason,\n\t\t\t\t} satisfies CharHostToIframeMessage,\n\t\t\t\tiframeOrigin\n\t\t\t)\n\t\t})\n\t}\n\n\t/**\n\t * Completes an in-flight teardown promise when the iframe acknowledges it.\n\t *\n\t * @param requestId - Optional request identifier to match against pending teardown.\n\t */\n\tprivate _resolvePendingTeardown(requestId?: string): void {\n\t\tconst pending = this._teardownPending\n\t\tif (!pending) return\n\t\tif (requestId && requestId !== pending.requestId) return\n\t\tthis._teardownPending = null\n\t\tpending.resolve()\n\t}\n\n\t/**\n\t * Handles open-link requests from the iframe with protocol allowlisting.\n\t * This allowlist intentionally mirrors the iframe-side pre-filter.\n\t *\n\t * @param requestId - Link request identifier supplied by the iframe.\n\t * @param url - Requested URL string.\n\t * @param iframeOrigin - Trusted origin used for postMessage target filtering.\n\t */\n\tprivate _handleOpenLinkRequest(requestId: string, url: string, iframeOrigin: string): void {\n\t\t// TODO(security#4): trusted-host assumption currently defers destination domain allowlisting.\n\t\tlet ok = false\n\t\tlet error: string | undefined\n\t\ttry {\n\t\t\tconst parsed = new URL(url, window.location.href)\n\t\t\tconst protocol = parsed.protocol.toLowerCase()\n\t\t\tconst isAllowedProtocol =\n\t\t\t\tprotocol === 'http:' ||\n\t\t\t\tprotocol === 'https:' ||\n\t\t\t\tprotocol === 'mailto:' ||\n\t\t\t\tprotocol === 'tel:'\n\t\t\tif (!isAllowedProtocol) {\n\t\t\t\tthrow new Error(`Unsupported URL protocol: ${protocol}`)\n\t\t\t}\n\n\t\t\tconst popup = window.open(parsed.toString(), '_blank', 'noopener,noreferrer')\n\t\t\tok = popup !== null || protocol === 'mailto:' || protocol === 'tel:'\n\t\t\tif (!ok) {\n\t\t\t\terror = 'Popup blocked by browser'\n\t\t\t}\n\n\t\t\tthis.dispatchEvent(\n\t\t\t\tnew CustomEvent('char-open-link', {\n\t\t\t\t\t...CHAR_CUSTOM_EVENT_OPTIONS,\n\t\t\t\t\tdetail: {\n\t\t\t\t\t\trequestId,\n\t\t\t\t\t\turl: parsed.toString(),\n\t\t\t\t\t\tok,\n\t\t\t\t\t} satisfies CharOpenLinkDetail,\n\t\t\t\t})\n\t\t\t)\n\t\t} catch (err) {\n\t\t\terror = err instanceof Error ? err.message : String(err)\n\t\t\tconsole.error('[Char] open-link request failed:', err)\n\t\t}\n\n\t\tthis._iframe?.contentWindow?.postMessage(\n\t\t\t{ type: 'char-open-link-result', requestId, ok, error } satisfies CharHostToIframeMessage,\n\t\t\tiframeOrigin\n\t\t)\n\t}\n\n\t/**\n\t * Observe dark mode changes and forward to iframe via unified context.\n\t */\n\tprivate _observeDarkMode(): void {\n\t\tconst syncThemeAndStyles = () => {\n\t\t\tif (!this._iframeReady) return\n\t\t\tthis.setHostContext({\n\t\t\t\ttheme: isDarkMode() ? 'dark' : 'light',\n\t\t\t\tstyles: { variables: extractCharVariables(this) },\n\t\t\t})\n\t\t}\n\n\t\tthis._darkModeObserver = new MutationObserver(syncThemeAndStyles)\n\t\tthis._darkModeObserver.observe(document.documentElement, {\n\t\t\tattributes: true,\n\t\t\tattributeFilter: ['class', 'style', 'data-theme'],\n\t\t})\n\n\t\tthis._darkModeMediaQuery = window.matchMedia('(prefers-color-scheme: dark)')\n\t\tthis._darkModeMediaHandler = syncThemeAndStyles\n\t\tthis._darkModeMediaQuery.addEventListener('change', this._darkModeMediaHandler)\n\t}\n\n\t/**\n\t * Observe style attribute changes on <char-agent> to detect CSS variable updates.\n\t * Re-extracts variables and sends delta via unified context.\n\t */\n\tprivate _observeStyleChanges(): void {\n\t\tthis._styleObserver = new MutationObserver(() => {\n\t\t\tif (!this._iframeReady) return\n\t\t\tthis.setHostContext({ styles: { variables: extractCharVariables(this) } })\n\t\t})\n\t\tthis._styleObserver.observe(this, {\n\t\t\tattributes: true,\n\t\t\tattributeFilter: ['style', 'class'],\n\t\t})\n\t}\n\n\t/**\n\t * Observe host element dimensions to provide containerDimensions context.\n\t */\n\tprivate _observeContainerDimensions(): void {\n\t\tthis._containerResizeObserver = new ResizeObserver((entries) => {\n\t\t\tif (!this._iframeReady) return\n\t\t\tconst entry = entries[0]\n\t\t\tif (!entry) return\n\t\t\tthis.setHostContext({\n\t\t\t\tcontainerDimensions: {\n\t\t\t\t\twidth: Math.round(entry.contentRect.width),\n\t\t\t\t\theight: Math.round(entry.contentRect.height),\n\t\t\t\t},\n\t\t\t\tsafeAreaInsets: getSafeAreaInsets(),\n\t\t\t})\n\t\t})\n\t\tthis._containerResizeObserver.observe(this)\n\t}\n\n\t/**\n\t * Resolve devMode from property (React 19) or attribute.\n\t *\n\t * @returns Parsed dev-mode configuration when valid.\n\t */\n\tprivate _resolveDevMode(): DevModeConfig | undefined {\n\t\tif (this.devMode && typeof this.devMode === 'object') {\n\t\t\treturn this.devMode\n\t\t}\n\n\t\tconst devModeAttr = this.getAttribute('dev-mode')\n\t\tif (!devModeAttr) return undefined\n\n\t\ttry {\n\t\t\tconst parsed: unknown = JSON.parse(devModeAttr)\n\t\t\tif (!parsed || typeof parsed !== 'object' || Array.isArray(parsed)) {\n\t\t\t\tconst actual = Array.isArray(parsed) ? 'array' : typeof parsed\n\t\t\t\tconst msg = `dev-mode attribute must be a JSON object, got: ${actual}`\n\t\t\t\tconsole.warn(`[Char] ${msg}`)\n\t\t\t\tthis._emitCharError('INVALID_DEV_MODE', msg)\n\t\t\t\treturn undefined\n\t\t\t}\n\t\t\tconst obj = parsed as Record<string, unknown>\n\t\t\treturn {\n\t\t\t\tanthropicApiKey: typeof obj.anthropicApiKey === 'string' ? obj.anthropicApiKey : undefined,\n\t\t\t\topenaiApiKey: typeof obj.openaiApiKey === 'string' ? obj.openaiApiKey : undefined,\n\t\t\t\tuseLocalApi: typeof obj.useLocalApi === 'boolean' ? obj.useLocalApi : undefined,\n\t\t\t}\n\t\t} catch (e) {\n\t\t\tconst msg = `Failed to parse dev-mode attribute as JSON: ${e instanceof Error ? e.message : String(e)}`\n\t\t\tconsole.warn(`[Char] ${msg}`)\n\t\t\tthis._emitCharError('INVALID_DEV_MODE', msg)\n\t\t\treturn undefined\n\t\t}\n\t}\n\n\t/**\n\t * Resolve apiBase override from property (React 19) or attribute.\n\t *\n\t * @returns Sanitized API base URL without trailing slash.\n\t */\n\tprivate _resolveApiBaseOverride(): string | undefined {\n\t\tconst raw =\n\t\t\t(typeof this.apiBase === 'string' ? this.apiBase : this.getAttribute('api-base')) ??\n\t\t\tundefined\n\t\tconst trimmed = raw?.trim()\n\t\tif (!trimmed) return undefined\n\t\treturn trimmed.replace(/\\/+$/, '')\n\t}\n}\n\n/**\n * Type declaration for the Char custom element.\n * Useful for TypeScript consumers using refs.\n *\n * @public\n */\nexport type { CharAgentElement }\n\n/**\n * Legacy alias for `CharAgentElement`.\n *\n * @public\n */\nexport type CharElement = CharAgentElement\n\n/**\n * Backward-compatible alias for `CharAgentElement`.\n *\n * @public\n */\nexport type WebMCPAgentElement = CharAgentElement\n\n/**\n * Register the <char-agent> custom element.\n * Called automatically when importing this module.\n *\n * @param tagName - Optional custom tag name override.\n *\n * @public\n */\nexport function registerChar(tagName = 'char-agent'): void {\n\tif (typeof window === 'undefined') return\n\n\tif (!customElements.get(tagName)) {\n\t\tcustomElements.define(tagName, CharAgentElement)\n\t}\n}\n\nregisterChar()\n\ndeclare global {\n\tinterface HTMLElementTagNameMap {\n\t\t'char-agent': CharAgentElement\n\t}\n\n\t/* biome-ignore lint/style/noNamespace: JSX intrinsic-elements augmentation uses namespace syntax by TypeScript design. */\n\tnamespace JSX {\n\t\tinterface IntrinsicElements {\n\t\t\t'char-agent': {\n\t\t\t\t/** Development mode configuration (JSON string for HTML attributes). */\n\t\t\t\t'dev-mode'?: string\n\t\t\t\t/** Property form of dev mode config. */\n\t\t\t\tdevMode?: DevModeConfig\n\t\t\t\t/** Explicit API base override, e.g. `https://staging-app.usechar.ai`. */\n\t\t\t\t'api-base'?: string\n\t\t\t\t/** Property form of API base override. */\n\t\t\t\tapiBase?: string\n\t\t\t\t/** Enable debug tools for inspecting embedded agent internal state. */\n\t\t\t\t'enable-debug-tools'?: boolean\n\t\t\t\t/** Display mode for host orchestration: `pip` is collapsed launcher mode. */\n\t\t\t\t'display-mode'?: CharDisplayMode\n\t\t\t\t/** Callback when the close button inside the agent panel is clicked. */\n\t\t\t\tonClose?: () => void\n\t\t\t\t/** Imperative ref to the custom element instance. */\n\t\t\t\tref?: unknown\n\t\t\t\t[key: string]: unknown\n\t\t\t}\n\t\t}\n\t}\n}\n\ndeclare module 'react/jsx-runtime' {\n\t/* biome-ignore lint/style/noNamespace: JSX intrinsic-elements augmentation uses namespace syntax by TypeScript design. */\n\tnamespace JSX {\n\t\tinterface IntrinsicElements {\n\t\t\t'char-agent': {\n\t\t\t\t'dev-mode'?: string\n\t\t\t\tdevMode?: DevModeConfig\n\t\t\t\t'api-base'?: string\n\t\t\t\tapiBase?: string\n\t\t\t\t'enable-debug-tools'?: boolean\n\t\t\t\t'display-mode'?: CharDisplayMode\n\t\t\t\tonClose?: () => void\n\t\t\t\tref?: unknown\n\t\t\t\t[key: string]: unknown\n\t\t\t}\n\t\t}\n\t}\n}\n"],"mappings":"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ACA;CAGA;CACA;CACA;CACA;CACA;CACA;CAGA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CAGA;CACA;CACA;CAGA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CAGA;CACA;CACA;CACA;CACA;CACA;CAGA;CACA;CACA;CACA;CACA;CAGA;CACA;CACA;CACA;CAGA;CACA;CACA;CAGA;CACA;CACA;CACA;AAED,MAAM,8BAA8B;CAEnC;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CAGA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CAGA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CAGA;CACA;CACA;CACA;CACA;CACA;CACA;CAGA;CACA;CAGA;CACA;CACA;CACA;CAGA;CACA;CACA;CACA;CAGA;CACA;CACA;CACA;CACA;CACA;CACA;CAGA;CACA;CACA;CACA;CAGA;CACA;CACA;CACA;CACA;CACA;CACA;CAGA;CACA;CACA;CACA;CACA;CACA;CAGA;CAGA;CACA;CACA;CACA;CACA;AAED,MAAa,0BAA0B,CACtC,GAAG,4BACH,GAAG,4BACH;;;;;;;;;;;;ACpLD,SAAgB,cACf,OAC2B;AAC3B,QAAO,UAAU,YAAY,UAAU,gBAAgB,UAAU;;;;;ACvClE,SAAgB,gCACf,iBAC8B;AAC9B,KAAI,CAAC,cAAc,gBAAgB,CAClC;AAGD,QAAO;;;;;ACDR,SAAgB,YACf,SACA,OACwC;AACxC,KAAI,CAAC,MAAO,QAAO;AACnB,QAAO;EACN,GAAG;EACH,GAAG;EAEH,WAAW,MAAM,aAAa,SAAS;EACvC,OAAO,MAAM,SAAS,SAAS;EAC/B;;AAGF,SAAgB,aACf,SACA,OACgB;AAChB,KAAI,CAAC,MAAO,QAAO;AACnB,QAAO;EAAE,GAAG;EAAS,GAAG;EAAO;;AAGhC,SAAgB,iBACf,SACA,OACkB;CAClB,MAAM,WAAW,WAAW,EAAE;AAC9B,QAAO;EACN,GAAG;EACH,GAAG;EACH,QAAQ,YAAY,SAAS,QAAQ,MAAM,OAAO;EAClD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"}
|
|
1
|
+
{"version":3,"file":"web-component.js","names":[],"sources":["../src/host/char-iframe-proxy.ts","../src/utils/constants.ts","../src/utils/css-sanitizer.ts","../src/utils/css-resolver.ts","../src/utils/css-variables.ts","../src/display-mode-policy.ts","../src/utils/display-mode.ts","../src/utils/host-context-merge.ts","../src/utils/parse-boolean-attribute.ts","../src/web-component.tsx"],"sourcesContent":["/**\n * CharIframeProxy\n *\n * Host-side relay that bridges MCP server-to-client responses from the page's\n * TabServerTransport back to the iframe (embedded agent).\n *\n * Client-to-server messages (iframe → host) are handled directly by\n * TabServerTransport via allowedOrigins, so no relay is needed in that\n * direction.\n *\n * Zero runtime dependencies — uses only browser postMessage APIs.\n */\n\nexport class CharIframeProxy {\n\tprivate _cleanup: (() => void)[] = []\n\n\tconstructor(\n\t\tprivate _iframe: HTMLIFrameElement,\n\t\tprivate _iframeOrigin: string,\n\t\tprivate _channelId = 'mcp-default'\n\t) {}\n\n\tstart(): void {\n\t\t// window → iframe: relay server-to-client messages so the iframe client transport picks them up\n\t\tconst fromTab = (event: MessageEvent) => {\n\t\t\tif (event.source !== window) return\n\t\t\tconst d = event.data\n\t\t\tif (!d || d.channel !== this._channelId || d.type !== 'mcp' || d.direction !== 'server-to-client') return\n\t\t\tif (d._proxied) return // defense-in-depth: don't relay our own relayed messages\n\t\t\tif (!this._iframe.contentWindow) {\n\t\t\t\tconsole.error('[CharIframeProxy] Cannot relay to iframe: contentWindow is null')\n\t\t\t\treturn\n\t\t\t}\n\t\t\tthis._iframe.contentWindow.postMessage(d, this._iframeOrigin)\n\t\t}\n\n\t\twindow.addEventListener('message', fromTab)\n\t\tthis._cleanup = [\n\t\t\t() => window.removeEventListener('message', fromTab),\n\t\t]\n\t}\n\n\tdestroy(): void {\n\t\tfor (const fn of this._cleanup) fn()\n\t\tthis._cleanup.length = 0\n\t}\n}\n","export const WEBMCP_PRODUCTION_API_BASE = 'https://app.usechar.ai'\n","/**\n * Maximum allowed length for CSS custom property values transported to iframe.\n *\n * NOTE: This value and the blocked-token policy intentionally mirror\n * `packages/shared-types/src/schemas/embed-bridge.ts` so validation happens\n * both before transport (host) and at contract parse-time (iframe).\n *\n * CONTROL_CHAR_PATTERN here is intentionally more permissive than\n * embed-bridge.ts: it allows tabs/newlines/CR/FF because\n * normalizeCssFormattingWhitespace converts them to spaces before the\n * control-char check. The iframe-side schema (embed-bridge.ts) uses\n * the stricter pattern as defense-in-depth for values that bypass\n * host-side sanitization.\n */\nexport const MAX_CSS_VARIABLE_VALUE_LENGTH = 1024\nexport const MAX_HOST_FONT_CSS_LENGTH = 16_384\n\nconst CONTROL_CHAR_PATTERN = /[\\u0000-\\u0008\\u000b\\u000e-\\u001f\\u007f]/u\nconst FONT_RULES_PATTERN = /^(?:\\s*@font-face\\s*\\{[^{}]*\\}\\s*)+$/iu\nconst CSS_COMMENT_PATTERN = /\\/\\*[\\s\\S]*?\\*\\//g\nconst CSS_FORMATTING_WHITESPACE_PATTERN = /[\\t\\n\\r\\f]+/gu\n\n/**\n * Normalizes CSS values for dangerous-token scanning.\n * Lowercases and removes CSS comments/whitespace/control chars to catch obfuscation.\n */\nfunction normalizeForSecurityScan(value: string): string {\n\tconst withoutComments = value.replace(CSS_COMMENT_PATTERN, '')\n\treturn withoutComments.toLowerCase().replace(/[\\s\\u0000-\\u001f\\u007f]+/gu, '')\n}\n\n/**\n * Collapses CSS formatting whitespace (tabs, newlines, carriage returns, form feeds)\n * into single spaces. Applied to the output value before trimming, so multiline\n * host-provided values (e.g., font stacks) become single-line without losing\n * meaningful token boundaries. This also ensures split-token obfuscation\n * (e.g., \"java\\nscript:\") is collapsed before the security scan.\n */\nfunction normalizeCssFormattingWhitespace(value: string): string {\n\treturn value.replace(CSS_FORMATTING_WHITESPACE_PATTERN, ' ')\n}\n\n/**\n * Sanitizes host-provided CSS variable values before iframe transport.\n * Returns `undefined` for unsafe or invalid values.\n *\n * @param value - The raw CSS property value.\n * @param variableName - Optional variable name for diagnostic logging.\n */\nexport function sanitizeCssVariableValue(value: string, variableName?: string): string | undefined {\n\tconst trimmed = normalizeCssFormattingWhitespace(value).trim()\n\tif (!trimmed) return undefined\n\tif (trimmed.length > MAX_CSS_VARIABLE_VALUE_LENGTH) {\n\t\tconsole.warn(`[Char] CSS variable ${variableName ?? '(unknown)'} rejected: value exceeds max length (${trimmed.length} > ${MAX_CSS_VARIABLE_VALUE_LENGTH})`)\n\t\treturn undefined\n\t}\n\tif (CONTROL_CHAR_PATTERN.test(trimmed)) {\n\t\tconsole.warn(`[Char] CSS variable ${variableName ?? '(unknown)'} rejected: contains control characters`)\n\t\treturn undefined\n\t}\n\n\tconst normalized = normalizeForSecurityScan(trimmed)\n\tconst blockedToken = getBlockedCssVariableToken(normalized)\n\tif (blockedToken) return warnAndReject(variableName, blockedToken)\n\n\treturn trimmed\n}\n\n/**\n * Sanitizes host-provided font CSS before iframe injection.\n * Only `@font-face` rules are allowed.\n */\nexport function sanitizeHostFontCss(fontCss: string): string | undefined {\n\tconst trimmed = fontCss.trim()\n\tif (!trimmed) return undefined\n\tif (trimmed.length > MAX_HOST_FONT_CSS_LENGTH) {\n\t\tconsole.warn(`[Char] Host font CSS rejected: value exceeds max length (${trimmed.length} > ${MAX_HOST_FONT_CSS_LENGTH})`)\n\t\treturn undefined\n\t}\n\tif (CONTROL_CHAR_PATTERN.test(trimmed)) {\n\t\tconsole.warn('[Char] Host font CSS rejected: contains control characters')\n\t\treturn undefined\n\t}\n\n\tconst normalized = normalizeForSecurityScan(trimmed)\n\tconst blockedToken = getBlockedFontToken(normalized)\n\tif (blockedToken) {\n\t\tconsole.warn(`[Char] Host font CSS rejected: contains blocked token \"${blockedToken}\"`)\n\t\treturn undefined\n\t}\n\n\tconst withoutComments = trimmed.replace(/\\/\\*[\\s\\S]*?\\*\\//g, '').trim()\n\tif (!FONT_RULES_PATTERN.test(withoutComments)) {\n\t\tconsole.warn('[Char] Host font CSS rejected: only @font-face rules are allowed')\n\t\treturn undefined\n\t}\n\n\treturn trimmed\n}\n\nfunction getBlockedCssVariableToken(normalizedValue: string): string | undefined {\n\tif (normalizedValue.includes('javascript:')) return 'javascript:'\n\tif (normalizedValue.includes('vbscript:')) return 'vbscript:'\n\tif (normalizedValue.includes('expression(')) return 'expression('\n\tif (normalizedValue.includes('@import')) return '@import'\n\tif (normalizedValue.includes('-moz-binding')) return '-moz-binding'\n\tif (normalizedValue.includes('url(')) return 'url('\n\treturn undefined\n}\n\nfunction getBlockedFontToken(normalizedValue: string): string | undefined {\n\tif (normalizedValue.includes('javascript:')) return 'javascript:'\n\tif (normalizedValue.includes('vbscript:')) return 'vbscript:'\n\tif (normalizedValue.includes('expression(')) return 'expression('\n\tif (normalizedValue.includes('-moz-binding')) return '-moz-binding'\n\treturn undefined\n}\n\nfunction warnAndReject(variableName: string | undefined, blockedToken: string): undefined {\n\tconsole.warn(`[Char] CSS variable ${variableName ?? '(unknown)'} rejected: contains blocked token \"${blockedToken}\"`)\n\treturn undefined\n}\n","/**\n * CSS var() Resolver\n *\n * Recursively resolves CSS custom property references (`var(--x, fallback)`)\n * to concrete values using getComputedStyle(). Necessary because `var()`\n * references are not valid across iframe boundaries.\n *\n * Handles nested var() references, fallbacks, and cycle detection.\n */\n\nexport const MAX_CSS_VAR_RESOLVE_DEPTH = 12\n\nexport function findMatchingParen(value: string, openParenIndex: number): number {\n\tlet depth = 1\n\tfor (let i = openParenIndex + 1; i < value.length; i++) {\n\t\tconst char = value[i]\n\t\tif (char === '(') depth += 1\n\t\telse if (char === ')') depth -= 1\n\t\tif (depth === 0) return i\n\t}\n\treturn -1\n}\n\nexport function splitTopLevelComma(value: string): [string, string | undefined] {\n\tlet depth = 0\n\tfor (let i = 0; i < value.length; i++) {\n\t\tconst char = value[i]\n\t\tif (char === '(') depth += 1\n\t\telse if (char === ')') depth = Math.max(0, depth - 1)\n\t\telse if (char === ',' && depth === 0) return [value.slice(0, i), value.slice(i + 1)]\n\t}\n\treturn [value, undefined]\n}\n\nexport function resolveFallback(\n\tfallback: string | undefined,\n\tcomputed: CSSStyleDeclaration,\n\tseen: Set<string>,\n\tdepth: number,\n): string | undefined {\n\tif (!fallback) return undefined\n\treturn resolveCssValue(fallback, computed, seen, depth + 1) || undefined\n}\n\nexport function resolveCssVariable(\n\tvariableName: string,\n\tfallback: string | undefined,\n\tcomputed: CSSStyleDeclaration,\n\tseen: Set<string>,\n\tdepth: number,\n): string | undefined {\n\tif (depth > MAX_CSS_VAR_RESOLVE_DEPTH) {\n\t\tconsole.warn(`[Char] CSS variable resolution depth exceeded for \"${variableName}\". Possible circular reference.`)\n\t\treturn fallback?.trim()\n\t}\n\n\tif (!variableName.startsWith('--') || seen.has(variableName)) {\n\t\treturn resolveFallback(fallback, computed, seen, depth)\n\t}\n\n\tconst nextSeen = new Set(seen)\n\tnextSeen.add(variableName)\n\n\tconst rawValue = computed.getPropertyValue(variableName).trim()\n\tif (rawValue) {\n\t\tconst resolvedRaw = resolveCssValue(rawValue, computed, nextSeen, depth + 1)\n\t\tif (resolvedRaw) return resolvedRaw\n\t}\n\n\treturn resolveFallback(fallback, computed, seen, depth)\n}\n\nexport function resolveCssValue(\n\tvalue: string,\n\tcomputed: CSSStyleDeclaration,\n\tseen: Set<string>,\n\tdepth = 0,\n): string {\n\tconst trimmed = value.trim()\n\tif (!trimmed || !trimmed.includes('var(') || depth > MAX_CSS_VAR_RESOLVE_DEPTH) {\n\t\treturn trimmed\n\t}\n\n\tlet cursor = 0\n\tlet resolved = ''\n\tlet replacedAny = false\n\n\twhile (cursor < trimmed.length) {\n\t\tconst varStart = trimmed.indexOf('var(', cursor)\n\t\tif (varStart === -1) {\n\t\t\tresolved += trimmed.slice(cursor)\n\t\t\tbreak\n\t\t}\n\n\t\tresolved += trimmed.slice(cursor, varStart)\n\n\t\tconst openParenIndex = varStart + 3\n\t\tconst closeParenIndex = findMatchingParen(trimmed, openParenIndex)\n\t\tif (closeParenIndex === -1) {\n\t\t\tresolved += trimmed.slice(varStart)\n\t\t\tbreak\n\t\t}\n\n\t\tconst rawArgs = trimmed.slice(openParenIndex + 1, closeParenIndex)\n\t\tconst [rawVariableName, rawFallback] = splitTopLevelComma(rawArgs)\n\t\tconst variableName = rawVariableName.trim()\n\t\tconst fallback = rawFallback?.trim()\n\n\t\tconst replacement = variableName\n\t\t\t? resolveCssVariable(variableName, fallback, computed, seen, depth + 1)\n\t\t\t: undefined\n\n\t\tif (replacement !== undefined) {\n\t\t\tresolved += replacement\n\t\t\treplacedAny = true\n\t\t} else {\n\t\t\tresolved += trimmed.slice(varStart, closeParenIndex + 1)\n\t\t}\n\n\t\tcursor = closeParenIndex + 1\n\t}\n\n\tconst normalized = resolved.trim()\n\tif (!replacedAny || !normalized.includes('var(')) {\n\t\treturn normalized\n\t}\n\n\treturn resolveCssValue(normalized, computed, seen, depth + 1)\n}\n","/**\n * Public CSS variable names for the Char widget.\n *\n * These are the external API — host pages set these to customise the widget.\n * We maintain static lists because `getComputedStyle()` cannot enumerate\n * custom properties.\n *\n * Source of truth:\n * - `src/styles/globals.css` (`--char-*`)\n * - WebMCP ext-apps UI spec (`--color-*`, `--font-*`, etc.)\n */\n\nconst CHAR_PUBLIC_VARIABLE_NAMES = [\n\t// Colors - Core\n\t'--char-color-background',\n\t'--char-color-foreground',\n\t'--char-color-card',\n\t'--char-color-card-foreground',\n\t'--char-color-popover',\n\t'--char-color-popover-foreground',\n\t'--char-color-primary',\n\t'--char-color-primary-foreground',\n\t'--char-color-secondary',\n\t'--char-color-secondary-foreground',\n\t'--char-color-muted',\n\t'--char-color-muted-foreground',\n\t'--char-color-accent',\n\t'--char-color-accent-foreground',\n\t'--char-color-destructive',\n\t'--char-color-destructive-foreground',\n\t'--char-color-border',\n\n\t// Colors - Semantic\n\t'--char-color-success',\n\t'--char-color-warning',\n\t'--char-color-error',\n\n\t// Colors - Derived\n\t'--char-color-input',\n\t'--char-color-ring',\n\n\t// Message Bubbles\n\t'--char-user-bubble-bg',\n\t'--char-user-bubble-text',\n\t'--char-assistant-bubble-bg',\n\t'--char-assistant-bubble-text',\n\n\t// Composer\n\t'--char-composer-bg',\n\t'--char-composer-border',\n\t'--char-composer-text',\n\t'--char-composer-placeholder',\n\t'--char-composer-button-bg',\n\t'--char-composer-button-text',\n\n\t// Tool Cards\n\t'--char-tool-bg',\n\t'--char-tool-border',\n\t'--char-tool-text',\n\t'--char-tool-header-bg',\n\t'--char-tool-approve-bg',\n\t'--char-tool-approve-text',\n\t'--char-tool-deny-bg',\n\t'--char-tool-deny-text',\n\n\t// Code Blocks\n\t'--char-code-bg',\n\t'--char-code-text',\n\t'--char-code-header-bg',\n\n\t// Sizing\n\t'--char-radius',\n\t'--char-radius-sm',\n\t'--char-radius-md',\n\t'--char-radius-lg',\n\t'--char-radius-xl',\n\t'--char-radius-2xl',\n\t'--char-radius-3xl',\n\t'--char-radius-full',\n\t'--char-spacing-unit',\n\n\t// Typography\n\t'--char-font-sans',\n\t'--char-font-mono',\n\t'--char-font-size-xs',\n\t'--char-font-size-sm',\n\t'--char-font-size-base',\n\t'--char-font-size-lg',\n\n\t// Motion\n\t'--char-duration-fast',\n\t'--char-duration-normal',\n\t'--char-duration-slow',\n\t'--char-easing-default',\n\t'--char-easing-spring',\n\n\t// Z-index\n\t'--char-z-base',\n\t'--char-z-content',\n\t'--char-z-overlay',\n\t'--char-z-max',\n\n\t// Shadows\n\t'--char-shadow-sm',\n\t'--char-shadow-md',\n\t'--char-shadow-lg',\n\n\t// Blur\n\t'--char-blur-sm',\n\t'--char-blur-md',\n\t'--char-blur-lg',\n] as const\n\nconst MCP_UI_STYLE_VARIABLE_NAMES = [\n\t// Background colors\n\t'--color-background-primary',\n\t'--color-background-secondary',\n\t'--color-background-tertiary',\n\t'--color-background-inverse',\n\t'--color-background-ghost',\n\t'--color-background-info',\n\t'--color-background-danger',\n\t'--color-background-success',\n\t'--color-background-warning',\n\t'--color-background-disabled',\n\n\t// Text colors\n\t'--color-text-primary',\n\t'--color-text-secondary',\n\t'--color-text-tertiary',\n\t'--color-text-inverse',\n\t'--color-text-ghost',\n\t'--color-text-info',\n\t'--color-text-danger',\n\t'--color-text-success',\n\t'--color-text-warning',\n\t'--color-text-disabled',\n\n\t// Border colors\n\t'--color-border-primary',\n\t'--color-border-secondary',\n\t'--color-border-tertiary',\n\t'--color-border-inverse',\n\t'--color-border-ghost',\n\t'--color-border-info',\n\t'--color-border-danger',\n\t'--color-border-success',\n\t'--color-border-warning',\n\t'--color-border-disabled',\n\n\t// Ring colors\n\t'--color-ring-primary',\n\t'--color-ring-secondary',\n\t'--color-ring-inverse',\n\t'--color-ring-info',\n\t'--color-ring-danger',\n\t'--color-ring-success',\n\t'--color-ring-warning',\n\n\t// Typography - Family\n\t'--font-sans',\n\t'--font-mono',\n\n\t// Typography - Weight\n\t'--font-weight-normal',\n\t'--font-weight-medium',\n\t'--font-weight-semibold',\n\t'--font-weight-bold',\n\n\t// Typography - Text Size\n\t'--font-text-xs-size',\n\t'--font-text-sm-size',\n\t'--font-text-md-size',\n\t'--font-text-lg-size',\n\n\t// Typography - Heading Size\n\t'--font-heading-xs-size',\n\t'--font-heading-sm-size',\n\t'--font-heading-md-size',\n\t'--font-heading-lg-size',\n\t'--font-heading-xl-size',\n\t'--font-heading-2xl-size',\n\t'--font-heading-3xl-size',\n\n\t// Typography - Text Line Height\n\t'--font-text-xs-line-height',\n\t'--font-text-sm-line-height',\n\t'--font-text-md-line-height',\n\t'--font-text-lg-line-height',\n\n\t// Typography - Heading Line Height\n\t'--font-heading-xs-line-height',\n\t'--font-heading-sm-line-height',\n\t'--font-heading-md-line-height',\n\t'--font-heading-lg-line-height',\n\t'--font-heading-xl-line-height',\n\t'--font-heading-2xl-line-height',\n\t'--font-heading-3xl-line-height',\n\n\t// Border radius\n\t'--border-radius-xs',\n\t'--border-radius-sm',\n\t'--border-radius-md',\n\t'--border-radius-lg',\n\t'--border-radius-xl',\n\t'--border-radius-full',\n\n\t// Border width\n\t'--border-width-regular',\n\n\t// Shadows\n\t'--shadow-hairline',\n\t'--shadow-sm',\n\t'--shadow-md',\n\t'--shadow-lg',\n] as const\n\nexport const CHAR_CSS_VARIABLE_NAMES = [\n\t...CHAR_PUBLIC_VARIABLE_NAMES,\n\t...MCP_UI_STYLE_VARIABLE_NAMES,\n] as const\n","import type { CharDisplayMode } from './types'\nexport type { CharDisplayMode } from './types'\n\ntype AssertTrue<T extends true> = T\n\n/**\n * Default display modes advertised by Char hosts.\n *\n * Order matters for fallback behavior. When a preferred mode is unavailable,\n * the first available mode is selected.\n *\n * @public\n */\nexport const DEFAULT_AVAILABLE_DISPLAY_MODES = [\n\t'inline',\n\t'fullscreen',\n\t'pip',\n] as const satisfies readonly CharDisplayMode[]\n\n/**\n * Allowed display mode values for Char host policy resolution.\n */\ntype DisplayModesFromDefaults = (typeof DEFAULT_AVAILABLE_DISPLAY_MODES)[number]\n\ntype _AssertDisplayModesStayInSync = AssertTrue<\n\t[DisplayModesFromDefaults] extends [CharDisplayMode]\n\t\t? [CharDisplayMode] extends [DisplayModesFromDefaults]\n\t\t\t? true\n\t\t\t: false\n\t\t: false\n>\n\n/**\n * Runtime guard for Char display mode values.\n *\n * @param value - Raw value from host attributes, events, or external input.\n * @returns `true` when the value is one of `inline`, `fullscreen`, or `pip`.\n *\n * @public\n */\nexport function isDisplayMode(\n\tvalue: string | null | undefined,\n): value is CharDisplayMode {\n\treturn value === 'inline' || value === 'fullscreen' || value === 'pip'\n}\n\n/**\n * Checks whether a display mode represents an expanded agent surface.\n *\n * Expanded modes are:\n * - `inline` (panel-style)\n * - `fullscreen` (viewport-filling)\n *\n * Collapsed mode is:\n * - `pip` (launcher-only)\n *\n * @param mode - Current display mode from host context.\n * @returns `true` for expanded modes and `false` for collapsed/undefined modes.\n *\n * @public\n */\nexport function isExpandedDisplayMode(mode: CharDisplayMode | undefined): boolean {\n\treturn mode === 'inline' || mode === 'fullscreen'\n}\n\n/**\n * Resolves a preferred display mode against host-supported modes.\n *\n * An empty `availableModes` array means \"no constraint\" — the preferred mode\n * is returned unconditionally. This is intentional: it allows callers to opt\n * out of mode filtering without a separate code path.\n *\n * @param preferred - Preferred mode requested by host policy.\n * @param availableModes - Supported modes advertised by the host.\n * @returns Preferred mode when supported, otherwise the first available mode.\n *\n * @public\n */\nexport function resolveSupportedDisplayMode(\n\tpreferred: CharDisplayMode,\n\tavailableModes: readonly CharDisplayMode[] = DEFAULT_AVAILABLE_DISPLAY_MODES,\n): CharDisplayMode {\n\tif (availableModes.length === 0) {\n\t\treturn preferred\n\t}\n\n\tif (availableModes.includes(preferred)) {\n\t\treturn preferred\n\t}\n\n\tconsole.debug(`[Char] Display mode \"${preferred}\" not in available modes [${availableModes.join(', ')}], using \"${availableModes[0]}\"`)\n\treturn availableModes[0]\n}\n\n/**\n * Resolves Char's standard host display-mode policy.\n *\n * Policy:\n * - Closed state to `pip`\n * - Open on desktop to `inline`\n * - Open on narrow viewport to `fullscreen`\n *\n * The resolved mode is then constrained to `availableModes`.\n *\n * @param options - Host orchestration inputs: `open`, `isNarrowViewport`,\n * and optional `availableModes`.\n * @returns Final mode that satisfies policy and availability constraints.\n *\n * @public\n */\nexport function resolvePolicyDisplayMode(options: {\n\topen: boolean\n\tisNarrowViewport: boolean\n\tavailableModes?: readonly CharDisplayMode[]\n}): CharDisplayMode {\n\tconst availableModes = options.availableModes ?? DEFAULT_AVAILABLE_DISPLAY_MODES\n\tconst preferred: CharDisplayMode = options.open\n\t\t? options.isNarrowViewport\n\t\t\t? 'fullscreen'\n\t\t\t: 'inline'\n\t\t: 'pip'\n\n\treturn resolveSupportedDisplayMode(preferred, availableModes)\n}\n","import type { CharDisplayMode } from '../types'\n\nimport { isDisplayMode } from '../display-mode-policy'\n\nexport function resolveDisplayModeFromAttribute(\n\tdisplayModeAttr: string | null,\n): CharDisplayMode | undefined {\n\tif (!isDisplayMode(displayModeAttr)) {\n\t\treturn undefined\n\t}\n\n\treturn displayModeAttr\n}\n","/**\n * Host Context Merge Utilities\n *\n * Canonical merge logic for CharHostContext deltas. Used by both the host-side\n * web component and the iframe-side useHostContext hook to ensure identical\n * merge behavior on both sides of the postMessage bridge.\n */\n\nimport type { CharHostContext } from '../types'\n\nexport function mergeStyles(\n\tcurrent: CharHostContext['styles'] | undefined,\n\tpatch: CharHostContext['styles'] | undefined,\n): CharHostContext['styles'] | undefined {\n\tif (!patch) return current\n\treturn {\n\t\t...current,\n\t\t...patch,\n\t\t// variables are sent as a snapshot so removals propagate\n\t\tvariables: patch.variables ?? current?.variables,\n\t\tfonts: patch.fonts ?? current?.fonts,\n\t}\n}\n\nexport function shallowMerge<T extends object>(\n\tcurrent: T | undefined,\n\tpatch: T | undefined,\n): T | undefined {\n\tif (!patch) return current\n\treturn { ...current, ...patch }\n}\n\nexport function mergeHostContext(\n\tcurrent: CharHostContext | null,\n\tpatch: CharHostContext,\n): CharHostContext {\n\tconst previous = current ?? {}\n\treturn {\n\t\t...previous,\n\t\t...patch,\n\t\tstyles: mergeStyles(previous.styles, patch.styles),\n\t\tcontainerDimensions: shallowMerge(previous.containerDimensions, patch.containerDimensions),\n\t\tdeviceCapabilities: shallowMerge(previous.deviceCapabilities, patch.deviceCapabilities),\n\t\tsafeAreaInsets: shallowMerge(previous.safeAreaInsets, patch.safeAreaInsets),\n\t\thostCapabilities: shallowMerge(previous.hostCapabilities, patch.hostCapabilities),\n\t}\n}\n","/**\n * Attribute helper treating any value except `null` and `'false'` as enabled.\n */\nexport function parseBooleanAttribute(value: string | null): boolean {\n\treturn value !== null && value !== 'false'\n}\n","/**\n * Char - Web Component (iframe-based)\n *\n * A vanilla custom element that creates an iframe pointing to the SaaS app's\n * `/embed/` entrypoint. Exposes `connect()`, CSS variable theming, and DOM events\n * while keeping host and iframe concerns isolated.\n *\n * ## Authentication (Imperative API)\n *\n * Use the `connect()` method for secure authentication.\n *\n * @example Vanilla JS\n * ```js\n * const agent = document.querySelector('char-agent')\n * agent.connect({ idToken: 'eyJhbGciOi...', clientId: 'your-client-id' })\n * ```\n *\n * @example React\n * ```tsx\n * const agentRef = useRef<CharAgentElement>(null)\n * useEffect(() => {\n * agentRef.current?.connect({ idToken: session.idToken, clientId: 'your-client-id' })\n * }, [session.idToken])\n * return <char-agent ref={agentRef} />\n * ```\n *\n * @example CSS variable theming\n * ```css\n * char-agent {\n * --char-color-primary: #0f766e;\n * --char-radius: 12px;\n * }\n * ```\n */\n\nimport type {\n\tCharErrorDetail,\n\tCharContainerDimensions,\n\tCharDeviceCapabilities,\n\tCharDisplayMode,\n\tCharHostCapabilities,\n\tCharHostContext,\n\tCharHostToIframeMessage,\n\tCharOpenLinkDetail,\n\tCharRequestDisplayModeDetail,\n\tCharSizeChangedDetail,\n\tConnectOptions,\n\tDevModeConfig,\n\tTicketAuth,\n} from './types'\n\nimport { CharIframeProxy } from './host/char-iframe-proxy'\nimport { WEBMCP_PRODUCTION_API_BASE } from './utils/constants'\nimport { sanitizeCssVariableValue } from './utils/css-sanitizer'\nimport { resolveCssValue } from './utils/css-resolver'\nimport { CHAR_CSS_VARIABLE_NAMES } from './utils/css-variables'\nimport { resolveDisplayModeFromAttribute } from './utils/display-mode'\nimport { mergeHostContext } from './utils/host-context-merge'\n\n/**\n * Shared options used by custom events emitted from the host element.\n */\nconst CHAR_CUSTOM_EVENT_OPTIONS = { bubbles: true, composed: true } as const\n\n/**\n * Inline iframe styles used to make the host element a transparent container.\n */\nconst IFRAME_STYLE = 'width:100%;height:100%;border:none;display:block;'\n\n/**\n * Display mode values advertised to the embedded runtime.\n */\nconst DISPLAY_MODES: readonly CharDisplayMode[] = ['inline', 'fullscreen', 'pip']\n\n/**\n * Minimal structural shape for iframe-originated messages.\n */\ninterface CharIframeMessageData extends Record<string, unknown> {\n\ttype: string\n}\n\n/**\n * Normalizes an API key by trimming whitespace and collapsing empty strings.\n */\nfunction normalizeApiKey(value?: string): string | undefined {\n\tconst trimmed = value?.trim()\n\treturn trimmed ? trimmed : undefined\n}\n\n/**\n * Produces a sanitized dev-mode configuration and drops empty payloads.\n */\nfunction resolveDevMode(devMode?: DevModeConfig): DevModeConfig | undefined {\n\tif (!devMode) return undefined\n\n\tconst normalized = {\n\t\tanthropicApiKey: normalizeApiKey(devMode.anthropicApiKey),\n\t\topenaiApiKey: normalizeApiKey(devMode.openaiApiKey),\n\t\tuseLocalApi: devMode.useLocalApi === true ? true : undefined,\n\t} satisfies DevModeConfig\n\n\tconst hasAnyValue =\n\t\t!!normalized.anthropicApiKey || !!normalized.openaiApiKey || !!normalized.useLocalApi\n\n\treturn hasAnyValue ? normalized : undefined\n}\n\nimport { parseBooleanAttribute } from './utils/parse-boolean-attribute'\n\n/**\n * Runtime guard for the message envelope emitted by the iframe.\n */\nfunction isCharIframeMessageData(value: unknown): value is CharIframeMessageData {\n\tif (!value || typeof value !== 'object') return false\n\tconst maybeType = (value as { type?: unknown }).type\n\treturn typeof maybeType === 'string' && maybeType.startsWith('char-')\n}\n\nfunction getStringOrUndefined(value: unknown): string | undefined {\n\treturn typeof value === 'string' ? value : undefined\n}\n\nfunction getNumberOrUndefined(value: unknown): number | undefined {\n\treturn typeof value === 'number' ? value : undefined\n}\n\nexport type {\n\tCharContainerDimensions,\n\tCharDeviceCapabilities,\n\tCharDisplayMode,\n\tCharErrorDetail,\n\tCharHostCapabilities,\n\tCharHostContext,\n\tCharHostStyles,\n\tCharOpenLinkDetail,\n\tCharPlatform,\n\tCharRequestDisplayModeDetail,\n\tCharSafeAreaInsets,\n\tCharSizeChangedDetail,\n\tCharTheme,\n\tConnectOptions,\n\tDevModeConfig,\n\tTicketAuth,\n} from './types'\n\n/**\n * Get the iframe src URL.\n * Points to the SaaS app's static /embed/ entrypoint with the parent origin as a query parameter.\n *\n * @param apiBase - Public base URL for the Char SaaS application.\n * @param parentOrigin - Origin of the host page embedding the widget.\n * @returns Fully qualified iframe URL targeting the embed entrypoint.\n */\nfunction getIframeSrc(apiBase: string, parentOrigin: string): string {\n\treturn `${apiBase}/embed/?parentOrigin=${encodeURIComponent(parentOrigin)}`\n}\n\n/**\n * Extract host CSS variable values from a host element.\n * Includes both `--char-*` and MCP UI style `--color-*`/`--font-*` tokens.\n * Uses a static list since getComputedStyle() cannot enumerate custom properties.\n * Values containing `var()` are resolved to concrete values before transport\n * so the iframe can safely apply them in its own CSS scope.\n *\n * @param el - Host `<char-agent>` element.\n * @returns A snapshot of resolved CSS custom properties for the iframe.\n */\nfunction extractCharVariables(el: HTMLElement): Record<string, string> {\n\tconst computed = getComputedStyle(el)\n\tconst vars: Record<string, string> = {}\n\tfor (const name of CHAR_CSS_VARIABLE_NAMES) {\n\t\tconst rawValue = computed.getPropertyValue(name).trim()\n\t\tif (rawValue) {\n\t\t\tconst resolvedValue = resolveCssValue(rawValue, computed, new Set([name]))\n\t\t\tconst sanitizedValue = sanitizeCssVariableValue(resolvedValue || rawValue, name)\n\t\t\tif (sanitizedValue !== undefined) {\n\t\t\t\tvars[name] = sanitizedValue\n\t\t\t}\n\t\t}\n\t}\n\treturn vars\n}\n\n/**\n * Detect whether the host page is in dark mode.\n * Checks the data-theme attribute, the `dark` class on <html>, and the prefers-color-scheme media query.\n *\n * @returns `true` when the host page should be treated as dark mode.\n */\nfunction isDarkMode(): boolean {\n\tif (typeof document === 'undefined') return false\n\tconst themeAttr = document.documentElement.getAttribute('data-theme')\n\tif (themeAttr === 'dark') return true\n\tif (themeAttr === 'light') return false\n\treturn (\n\t\tdocument.documentElement.classList.contains('dark') ||\n\t\twindow.matchMedia('(prefers-color-scheme: dark)').matches\n\t)\n}\n\n/**\n * Deep equality check using JSON.stringify. Sufficient for the flat/nested\n * structures in CharHostContext (no circular refs, no functions).\n *\n * @param a - First value.\n * @param b - Second value.\n * @returns `true` when both values serialize identically.\n */\nfunction deepEqual(a: unknown, b: unknown): boolean {\n\t// Key-order sensitive — acceptable here because both sides produce objects\n\t// with deterministic key order (host context built from static field lists).\n\treturn JSON.stringify(a) === JSON.stringify(b)\n}\n\n/**\n * Parses a CSS pixel value into a finite number.\n */\nfunction parsePxToNumber(value: string): number {\n\tconst parsed = Number.parseFloat(value)\n\treturn Number.isFinite(parsed) ? parsed : 0\n}\n\n/**\n * Reads safe-area inset values using `env(safe-area-inset-*)`.\n */\nfunction getSafeAreaInsets(): CharHostContext['safeAreaInsets'] | undefined {\n\tif (typeof document === 'undefined') return undefined\n\n\tconst probe = document.createElement('div')\n\tprobe.style.cssText = [\n\t\t'position:fixed',\n\t\t'visibility:hidden',\n\t\t'pointer-events:none',\n\t\t'inset:0 auto auto 0',\n\t\t'padding-top:env(safe-area-inset-top)',\n\t\t'padding-right:env(safe-area-inset-right)',\n\t\t'padding-bottom:env(safe-area-inset-bottom)',\n\t\t'padding-left:env(safe-area-inset-left)',\n\t].join(';')\n\n\tdocument.documentElement.appendChild(probe)\n\tconst computed = getComputedStyle(probe)\n\tconst insets = {\n\t\ttop: parsePxToNumber(computed.paddingTop),\n\t\tright: parsePxToNumber(computed.paddingRight),\n\t\tbottom: parsePxToNumber(computed.paddingBottom),\n\t\tleft: parsePxToNumber(computed.paddingLeft),\n\t}\n\tprobe.remove()\n\n\treturn insets\n}\n\n/**\n * Identifies the host platform category from the user agent.\n * Note: 'desktop' detection not yet implemented; non-mobile UAs resolve to 'web'.\n */\nfunction detectPlatform(): 'web' | 'desktop' | 'mobile' {\n\tif (typeof navigator === 'undefined') return 'web'\n\tconst ua = navigator.userAgent.toLowerCase()\n\tif (/(iphone|ipad|ipod|android|mobile)/.test(ua)) return 'mobile'\n\treturn 'web'\n}\n\n/**\n * Captures lightweight host device capability hints.\n */\nfunction getDeviceCapabilities(): CharDeviceCapabilities {\n\tif (typeof window === 'undefined' || typeof navigator === 'undefined') {\n\t\treturn {}\n\t}\n\treturn {\n\t\ttouch: 'ontouchstart' in window || navigator.maxTouchPoints > 0,\n\t\thover: window.matchMedia('(hover: hover)').matches,\n\t\tpointerFine: window.matchMedia('(pointer: fine)').matches,\n\t}\n}\n\n/**\n * `<char-agent>` custom element.\n *\n * Creates an iframe pointing to the SaaS app's /embed/ entrypoint and relays\n * auth, styles, dark mode, display mode, and MCP messages via postMessage.\n *\n * Uses a unified `char-context` message with diffing (only changed fields\n * are sent) instead of separate messages per concern.\n *\n * @public\n */\nclass CharAgentElement extends HTMLElement {\n\tstatic observedAttributes = [\n\t\t'dev-mode',\n\t\t'enable-debug-tools',\n\t\t'display-mode',\n\t\t'api-base',\n\t]\n\n\t/**\n\t * React 19-friendly property form of `dev-mode`.\n\t * Read once during `connectedCallback`.\n\t */\n\tdeclare devMode?: DevModeConfig\n\n\t/**\n\t * React 19-friendly property form of `api-base`.\n\t * Read once during `connectedCallback`.\n\t */\n\tdeclare apiBase?: string\n\n\t/** Active iframe element owned by this custom element instance. */\n\tprivate _iframe: HTMLIFrameElement | null = null\n\t/** MCP relay between host window and embed iframe. */\n\tprivate _proxy: CharIframeProxy | null = null\n\t/** Indicates whether the iframe has emitted `char-ready`. */\n\tprivate _iframeReady = false\n\t/** Bound window message handler for iframe-originated events. */\n\tprivate _messageListener: ((event: MessageEvent) => void) | null = null\n\t/** Observer that tracks host theme mutations. */\n\tprivate _darkModeObserver: MutationObserver | null = null\n\t/** Media query object for `prefers-color-scheme`. */\n\tprivate _darkModeMediaQuery: MediaQueryList | null = null\n\t/** Registered media query callback. */\n\tprivate _darkModeMediaHandler: (() => void) | null = null\n\t/** Observer that tracks host style/class mutations. */\n\tprivate _styleObserver: MutationObserver | null = null\n\t/** Observer that tracks container size changes. */\n\tprivate _containerResizeObserver: ResizeObserver | null = null\n\t/** In-flight teardown request waiting for iframe acknowledgement. */\n\tprivate _teardownPending:\n\t\t| { requestId: string; resolve: () => void; timeoutId: number }\n\t\t| null = null\n\t/** Monotonic sequence used to construct teardown request IDs. */\n\tprivate _teardownSequence = 0\n\t/** Re-entrancy guard used during disconnect lifecycle. */\n\tprivate _isDisconnecting = false\n\n\t/** Last host context snapshot used for delta emission. */\n\tprivate _hostContext: CharHostContext = {}\n\n\t/** Pending credentials that are flushed when the iframe is ready. */\n\tprivate _pendingAuth: {\n\t\tidToken?: string\n\t\tclientId?: string\n\t\torganizationId?: string\n\t\tticketAuth?: TicketAuth\n\t\tanthropicApiKey?: string\n\t} | null = null\n\n\t/**\n\t * Mount lifecycle: builds iframe runtime, starts proxying, and starts host observers.\n\t * The existing runtime is reused when reconnecting during fast detach/reattach cycles.\n\t */\n\tconnectedCallback() {\n\t\tif (this._hasActiveRuntime()) {\n\t\t\tthis._isDisconnecting = false\n\t\t\tthis._teardownSequence = 0\n\t\t\tif (this._teardownPending) {\n\t\t\t\twindow.clearTimeout(this._teardownPending.timeoutId)\n\t\t\t\tthis._teardownPending.resolve()\n\t\t\t\tthis._teardownPending = null\n\t\t\t}\n\t\t\tif (this._iframe && !this.contains(this._iframe)) {\n\t\t\t\tthis.appendChild(this._iframe)\n\t\t\t}\n\t\t\treturn\n\t\t}\n\n\t\tif (!this.style.display) {\n\t\t\tthis.style.display = 'block'\n\t\t}\n\n\t\tconst devMode = this._resolveDevMode()\n\t\tconst resolvedDevMode = resolveDevMode(devMode)\n\t\tconst apiBase =\n\t\t\tthis._resolveApiBaseOverride() ??\n\t\t\t(resolvedDevMode?.useLocalApi ? window.location.origin : WEBMCP_PRODUCTION_API_BASE)\n\t\tconst iframeOrigin = this._resolveIframeOrigin(apiBase)\n\t\tconst iframe = this._createIframe(apiBase)\n\t\tthis._iframe = iframe\n\n\t\t// Queue dev-mode API key for relay to the iframe on char-ready\n\t\tif (resolvedDevMode?.anthropicApiKey) {\n\t\t\tthis._pendingAuth = { anthropicApiKey: resolvedDevMode.anthropicApiKey }\n\t\t}\n\n\t\tthis._proxy = new CharIframeProxy(iframe, iframeOrigin)\n\t\tthis._proxy.start()\n\n\t\tthis._messageListener = this._createMessageListener(iframe, iframeOrigin)\n\t\twindow.addEventListener('message', this._messageListener)\n\n\t\tthis._observeDarkMode()\n\t\tthis._observeStyleChanges()\n\t\tthis._observeContainerDimensions()\n\t\tthis.appendChild(iframe)\n\t}\n\n\t/**\n\t * Determines whether this instance already has an initialized runtime.\n\t */\n\tprivate _hasActiveRuntime(): boolean {\n\t\treturn !!(this._iframe || this._proxy || this._messageListener)\n\t}\n\n\t/**\n\t * Parses and validates the iframe origin from the configured API base URL.\n\t *\n\t * @throws Error When `apiBase` is not a valid URL.\n\t */\n\tprivate _resolveIframeOrigin(apiBase: string): string {\n\t\ttry {\n\t\t\treturn new URL(apiBase).origin\n\t\t} catch {\n\t\t\tthrow new Error(`[Char] Invalid api-base: \"${apiBase}\". Must be a valid URL.`)\n\t\t}\n\t}\n\n\t/**\n\t * Creates a fully configured iframe element for the embed runtime.\n\t */\n\tprivate _createIframe(apiBase: string): HTMLIFrameElement {\n\t\tconst iframe = document.createElement('iframe')\n\t\tiframe.src = getIframeSrc(apiBase, window.location.origin)\n\t\tiframe.style.cssText = IFRAME_STYLE\n\t\tiframe.title = 'Char AI Assistant'\n\t\tiframe.setAttribute('sandbox', 'allow-scripts allow-same-origin allow-forms')\n\t\tiframe.setAttribute('allow', 'microphone')\n\t\treturn iframe\n\t}\n\n\t/**\n\t * Creates the message listener that filters by source/origin and dispatches typed events.\n\t */\n\tprivate _createMessageListener(\n\t\tiframe: HTMLIFrameElement,\n\t\tiframeOrigin: string,\n\t): (event: MessageEvent) => void {\n\t\treturn (event: MessageEvent) => {\n\t\t\t// TODO(security#2): trusted-host assumption currently defers per-origin message rate limits\n\t\t\t// and payload-size caps for iframe -> host postMessage handling.\n\t\t\tif (event.source !== iframe.contentWindow) return\n\t\t\tif (event.origin !== iframeOrigin) return\n\t\t\tif (!isCharIframeMessageData(event.data)) return\n\t\t\tthis._handleIframeMessage(event.data, iframeOrigin)\n\t\t}\n\t}\n\n\t/**\n\t * Routes validated iframe messages to local handlers and host DOM events.\n\t */\n\tprivate _handleIframeMessage(data: CharIframeMessageData, iframeOrigin: string): void {\n\t\tswitch (data.type) {\n\t\t\tcase 'char-ready':\n\t\t\t\tthis._iframeReady = true\n\t\t\t\tthis._sendInitialContext(iframeOrigin)\n\t\t\t\treturn\n\t\t\tcase 'char-initialized':\n\t\t\t\tthis._emitCharEvent('char-initialized')\n\t\t\t\treturn\n\t\t\tcase 'char-size-changed': {\n\t\t\t\tconst width = getNumberOrUndefined(data.width)\n\t\t\t\tconst height = getNumberOrUndefined(data.height)\n\t\t\t\tconst rawSizeMode = getStringOrUndefined(data.displayMode)\n\t\t\t\tconst sizeDisplayMode = rawSizeMode && (DISPLAY_MODES as readonly string[]).includes(rawSizeMode)\n\t\t\t\t\t? (rawSizeMode as CharDisplayMode)\n\t\t\t\t\t: undefined\n\t\t\t\tif (width !== undefined || height !== undefined) {\n\t\t\t\t\tconst detail: CharSizeChangedDetail = { width, height, displayMode: sizeDisplayMode }\n\t\t\t\t\tthis._emitCharEventWithDetail('char-size-changed', detail)\n\t\t\t\t}\n\t\t\t\treturn\n\t\t\t}\n\t\t\tcase 'char-request-display-mode': {\n\t\t\t\tconst rawMode = getStringOrUndefined(data.mode)\n\t\t\t\tconst mode = rawMode && (DISPLAY_MODES as readonly string[]).includes(rawMode)\n\t\t\t\t\t? (rawMode as CharDisplayMode)\n\t\t\t\t\t: undefined\n\t\t\t\tconst detail: CharRequestDisplayModeDetail = { mode }\n\t\t\t\tthis._emitCharEventWithDetail('char-request-display-mode', detail)\n\t\t\t\treturn\n\t\t\t}\n\t\t\tcase 'char-close':\n\t\t\t\tthis._emitCharEvent('char-close')\n\t\t\t\treturn\n\t\t\tcase 'char-error':\n\t\t\t\tthis._emitCharEventWithDetail('char-error', {\n\t\t\t\t\tcode: getStringOrUndefined(data.code) ?? 'UNKNOWN',\n\t\t\t\t\tmessage: getStringOrUndefined(data.message),\n\t\t\t\t})\n\t\t\t\treturn\n\t\t\tcase 'char-open-link': {\n\t\t\t\tconst requestId = getStringOrUndefined(data.requestId)\n\t\t\t\tconst url = getStringOrUndefined(data.url)\n\t\t\t\tif (requestId && url) {\n\t\t\t\t\tthis._handleOpenLinkRequest(requestId, url, iframeOrigin)\n\t\t\t\t} else if (requestId) {\n\t\t\t\t\tthis._iframe?.contentWindow?.postMessage(\n\t\t\t\t\t\t{ type: 'char-open-link-result', requestId, ok: false, error: 'Missing url in char-open-link message' } satisfies CharHostToIframeMessage,\n\t\t\t\t\t\tiframeOrigin,\n\t\t\t\t\t)\n\t\t\t\t} else {\n\t\t\t\t\tconsole.error('[Char] Received char-open-link without requestId')\n\t\t\t\t}\n\t\t\t\treturn\n\t\t\t}\n\t\t\tcase 'char-teardown-complete':\n\t\t\t\tthis._resolvePendingTeardown(getStringOrUndefined(data.requestId))\n\t\t\t\treturn\n\t\t\tdefault:\n\t\t\t\tconsole.warn(`[Char] Unrecognized iframe message type: \"${(data as { type?: string }).type}\"`)\n\t\t\t\treturn\n\t\t}\n\t}\n\n\t/**\n\t * Emits a host-facing custom event without detail payload.\n\t */\n\tprivate _emitCharEvent(type: string): void {\n\t\tthis.dispatchEvent(new CustomEvent(type, CHAR_CUSTOM_EVENT_OPTIONS))\n\t}\n\n\t/**\n\t * Emits a host-facing custom event with detail payload.\n\t */\n\tprivate _emitCharEventWithDetail(type: string, detail: unknown): void {\n\t\tthis.dispatchEvent(new CustomEvent(type, { ...CHAR_CUSTOM_EVENT_OPTIONS, detail }))\n\t}\n\n\t/**\n\t * Emits a standardized `char-error` event.\n\t */\n\tprivate _emitCharError(code: string, message: string): void {\n\t\tconst detail: CharErrorDetail = { code, message }\n\t\tthis._emitCharEventWithDetail('char-error', detail)\n\t}\n\n\t/**\n\t * Unmount lifecycle: requests iframe teardown and releases all host resources.\n\t */\n\tdisconnectedCallback() {\n\t\tif (this._isDisconnecting) return\n\t\tthis._isDisconnecting = true\n\n\t\ttry {\n\t\t\tconst iframeOrigin = this._getIframeOrigin()\n\t\t\tif (this._iframeReady && iframeOrigin) {\n\t\t\t\tthis._requestTeardown(iframeOrigin, 'unmount')\n\t\t\t\t\t.catch((err) => console.error('[Char] Teardown failed:', err))\n\t\t\t\t\t.finally(() => {\n\t\t\t\t\t\tif (this.isConnected) {\n\t\t\t\t\t\t\tthis._isDisconnecting = false\n\t\t\t\t\t\t\treturn\n\t\t\t\t\t\t}\n\t\t\t\t\t\tthis._finalizeDisconnect()\n\t\t\t\t\t})\n\t\t\t\treturn\n\t\t\t}\n\t\t} catch (err) {\n\t\t\tconsole.error('[Char] Error during disconnect:', err)\n\t\t}\n\n\t\tthis._finalizeDisconnect()\n\t}\n\n\t/**\n\t * Releases iframe, observers, listeners, and pending teardown state.\n\t */\n\tprivate _finalizeDisconnect(): void {\n\t\tif (this._proxy) {\n\t\t\tthis._proxy.destroy()\n\t\t\tthis._proxy = null\n\t\t}\n\n\t\tif (this._messageListener) {\n\t\t\twindow.removeEventListener('message', this._messageListener)\n\t\t\tthis._messageListener = null\n\t\t}\n\n\t\tif (this._darkModeObserver) {\n\t\t\tthis._darkModeObserver.disconnect()\n\t\t\tthis._darkModeObserver = null\n\t\t}\n\t\tif (this._darkModeMediaQuery && this._darkModeMediaHandler) {\n\t\t\tthis._darkModeMediaQuery.removeEventListener('change', this._darkModeMediaHandler)\n\t\t\tthis._darkModeMediaQuery = null\n\t\t\tthis._darkModeMediaHandler = null\n\t\t}\n\n\t\tif (this._styleObserver) {\n\t\t\tthis._styleObserver.disconnect()\n\t\t\tthis._styleObserver = null\n\t\t}\n\n\t\tif (this._containerResizeObserver) {\n\t\t\tthis._containerResizeObserver.disconnect()\n\t\t\tthis._containerResizeObserver = null\n\t\t}\n\n\t\tif (this._iframe) {\n\t\t\tthis._iframe.remove()\n\t\t\tthis._iframe = null\n\t\t}\n\n\t\tif (this._teardownPending) {\n\t\t\twindow.clearTimeout(this._teardownPending.timeoutId)\n\t\t\tthis._teardownPending.resolve()\n\t\t\tthis._teardownPending = null\n\t\t}\n\n\t\tthis._iframeReady = false\n\t\tthis._pendingAuth = null\n\t\tthis._hostContext = {}\n\t\tthis._teardownSequence = 0\n\t\tthis._isDisconnecting = false\n\t}\n\n\t/**\n\t * Reacts to observed attribute updates after iframe readiness.\n\t * Attributes that affect iframe boot configuration are intentionally ignored\n\t * after mount (`dev-mode`, `enable-debug-tools`, `api-base`).\n\t */\n\tattributeChangedCallback(name: string, _oldValue: string | null, newValue: string | null) {\n\t\tif (!this._iframe || !this._iframeReady) return\n\n\t\tswitch (name) {\n\t\t\tcase 'display-mode':\n\t\t\t\tthis.setHostContext({\n\t\t\t\t\tdisplayMode: resolveDisplayModeFromAttribute(newValue),\n\t\t\t\t})\n\t\t\t\tbreak\n\t\t\tcase 'dev-mode':\n\t\t\tcase 'enable-debug-tools':\n\t\t\tcase 'api-base':\n\t\t\t\tbreak\n\t\t}\n\t}\n\n\t/**\n\t * Connect to the Char agent with authentication.\n\t *\n\t * The token is stored as a JavaScript property (not as a DOM attribute),\n\t * preventing exposure to DOM inspection and session replay tools.\n\t *\n\t * @param options - Authentication payload for either ID token or ticket auth.\n\t * @returns `true` when the payload is accepted; `false` when validation fails.\n\t */\n\tconnect(options: ConnectOptions): boolean {\n\t\tif (!options?.idToken && !options?.ticketAuth) {\n\t\t\tthis._emitCharError(\n\t\t\t\t'MISSING_TOKEN',\n\t\t\t\t'connect() requires either idToken or ticketAuth parameter',\n\t\t\t)\n\t\t\treturn false\n\t\t}\n\n\t\tif (options?.idToken && !options?.ticketAuth && !options?.clientId) {\n\t\t\tthis._emitCharError(\n\t\t\t\t'MISSING_CLIENT_ID',\n\t\t\t\t'connect() requires clientId when using idToken authentication',\n\t\t\t)\n\t\t\treturn false\n\t\t}\n\n\t\tthis._pendingAuth = {\n\t\t\tidToken: options.ticketAuth ? undefined : options.idToken,\n\t\t\tclientId: options.ticketAuth ? undefined : options.clientId,\n\t\t\torganizationId: options.ticketAuth ? undefined : options.organizationId,\n\t\t\tticketAuth: options.ticketAuth,\n\t\t}\n\n\t\tconst iframeOrigin = this._getIframeOrigin()\n\t\tif (this._iframeReady && iframeOrigin) {\n\t\t\tthis._postAuth(iframeOrigin)\n\t\t}\n\n\t\treturn true\n\t}\n\n\t/**\n\t * Convenience method for declarative wrappers.\n\t * Applies auth when provided, otherwise clears auth.\n\t *\n\t * @param options - Auth payload to connect, or `null` to disconnect.\n\t * @returns Result from `connect()` or `disconnect()`.\n\t */\n\tsetAuth(options: ConnectOptions | null): boolean {\n\t\tif (!options) {\n\t\t\treturn this.disconnect()\n\t\t}\n\t\treturn this.connect(options)\n\t}\n\n\t/**\n\t * Disconnect from the Char agent.\n\t * Clears the authentication token.\n\t *\n\t * @returns `true` when the disconnect message was sent; `false` when the iframe was not ready.\n\t */\n\tdisconnect(): boolean {\n\t\tthis._pendingAuth = null\n\t\tconst iframeOrigin = this._getIframeOrigin()\n\t\tif (this._iframeReady && this._iframe && iframeOrigin) {\n\t\t\tthis._iframe.contentWindow?.postMessage(\n\t\t\t\t{ type: 'char-disconnect' } satisfies CharHostToIframeMessage,\n\t\t\t\tiframeOrigin,\n\t\t\t)\n\t\t\treturn true\n\t\t}\n\t\treturn false\n\t}\n\n\t/**\n\t * Resolves the current iframe origin when mounted.\n\t */\n\tprivate _getIframeOrigin(): string | null {\n\t\tif (!this._iframe?.src) return null\n\t\ttry {\n\t\t\treturn new URL(this._iframe.src).origin\n\t\t} catch {\n\t\t\tconsole.error('[Char] Failed to parse iframe origin from src:', this._iframe.src)\n\t\t\treturn null\n\t\t}\n\t}\n\n\t/**\n\t * Update the host context sent to the iframe.\n\t * Only changed fields are transmitted (diffing pattern).\n\t *\n\t * @param hostContext - Partial host context patch to merge and emit.\n\t */\n\tsetHostContext(hostContext: CharHostContext): void {\n\t\tconst changes: CharHostContext = {}\n\t\tconst nextContext = mergeHostContext(this._hostContext, hostContext)\n\n\t\tlet hasChanges = false\n\t\tfor (const key of Object.keys(hostContext) as Array<keyof CharHostContext>) {\n\t\t\tconst oldValue = this._hostContext[key]\n\t\t\tconst newValue = nextContext[key]\n\t\t\tif (deepEqual(oldValue, newValue)) {\n\t\t\t\tcontinue\n\t\t\t}\n\t\t\t;(changes as Record<string, unknown>)[key] = hostContext[key]\n\t\t\thasChanges = true\n\t\t}\n\t\tif (hasChanges) {\n\t\t\tthis._hostContext = nextContext\n\t\t\tthis._sendContextDelta(changes)\n\t\t}\n\t}\n\n\t/**\n\t * Build and send the full initial context after iframe signals char-ready.\n\t *\n\t * @param iframeOrigin - Trusted origin used for postMessage target filtering.\n\t */\n\tprivate _sendInitialContext(iframeOrigin: string): void {\n\t\tconst vars = extractCharVariables(this)\n\t\tconst theme = isDarkMode() ? 'dark' as const : 'light' as const\n\t\tconst displayModeAttr = this.getAttribute('display-mode')\n\t\tconst locale = typeof navigator !== 'undefined' ? navigator.language : undefined\n\t\tconst timeZone = Intl.DateTimeFormat().resolvedOptions().timeZone\n\t\tconst containerDimensions: CharContainerDimensions = {\n\t\t\twidth: this.clientWidth,\n\t\t\theight: this.clientHeight,\n\t\t}\n\t\tconst safeAreaInsets = getSafeAreaInsets()\n\n\t\tconst displayMode = resolveDisplayModeFromAttribute(displayModeAttr) ?? 'inline'\n\n\t\tconst hostCapabilities: CharHostCapabilities = {\n\t\t\tsupportedDisplayModes: [...DISPLAY_MODES],\n\t\t\tsupportsOpenLink: true,\n\t\t\tsupportsTeardown: true,\n\t\t}\n\n\t\tconst context: CharHostContext = {\n\t\t\ttheme,\n\t\t\tstyles: { variables: vars },\n\t\t\tdisplayMode,\n\t\t\tavailableDisplayModes: [...DISPLAY_MODES],\n\t\t\tcontainerDimensions,\n\t\t\tlocale,\n\t\t\ttimeZone,\n\t\t\tplatform: detectPlatform(),\n\t\t\tdeviceCapabilities: getDeviceCapabilities(),\n\t\t\tsafeAreaInsets,\n\t\t\tuserAgent: typeof navigator !== 'undefined' ? navigator.userAgent : undefined,\n\t\t\thostCapabilities,\n\t\t}\n\n\t\tthis._hostContext = context\n\t\tif (!this._iframe?.contentWindow) {\n\t\t\tconsole.error('[Char] iframe signaled char-ready but contentWindow is null')\n\t\t\tthis._iframeReady = false\n\t\t\tthis._emitCharError('IFRAME_CONTENT_WINDOW_NULL', 'iframe signaled char-ready but contentWindow is null')\n\t\t\treturn\n\t\t}\n\t\tthis._iframe.contentWindow.postMessage(\n\t\t\t{ type: 'char-context', ...context } satisfies CharHostToIframeMessage,\n\t\t\tiframeOrigin\n\t\t)\n\n\t\tconst debugAttr = this.getAttribute('enable-debug-tools')\n\t\tif (parseBooleanAttribute(debugAttr)) {\n\t\t\tthis._iframe?.contentWindow?.postMessage(\n\t\t\t\t{ type: 'char-debug-tools', enabled: true } satisfies CharHostToIframeMessage,\n\t\t\t\tiframeOrigin\n\t\t\t)\n\t\t}\n\n\t\tif (this._pendingAuth) {\n\t\t\tthis._postAuth(iframeOrigin)\n\t\t}\n\t}\n\n\t/**\n\t * Send a context delta to the iframe.\n\t *\n\t * @param changes - Minimal context patch derived from diffing.\n\t */\n\tprivate _sendContextDelta(changes: CharHostContext): void {\n\t\tif (!this._iframeReady || !this._iframe?.contentWindow) return\n\t\tconst iframeOrigin = this._getIframeOrigin()\n\t\tif (!iframeOrigin) {\n\t\t\tconsole.warn('[Char] Cannot send context delta: iframe origin is null')\n\t\t\treturn\n\t\t}\n\t\tthis._iframe.contentWindow.postMessage(\n\t\t\t{ type: 'char-context', ...changes } satisfies CharHostToIframeMessage,\n\t\t\tiframeOrigin\n\t\t)\n\t}\n\n\t/**\n\t * Post authentication credentials to the iframe.\n\t *\n\t * @param iframeOrigin - Trusted origin used for postMessage target filtering.\n\t */\n\tprivate _postAuth(iframeOrigin: string): void {\n\t\tif (!this._pendingAuth || !this._iframe?.contentWindow) return\n\n\t\tconst message: CharHostToIframeMessage = this._pendingAuth.ticketAuth\n\t\t\t? { type: 'char-auth', ticketAuth: this._pendingAuth.ticketAuth }\n\t\t\t: this._pendingAuth.anthropicApiKey\n\t\t\t\t? { type: 'char-auth', anthropicApiKey: this._pendingAuth.anthropicApiKey }\n\t\t\t\t: {\n\t\t\t\t\t\ttype: 'char-auth',\n\t\t\t\t\t\tidToken: this._pendingAuth.idToken!,\n\t\t\t\t\t\tclientId: this._pendingAuth.clientId,\n\t\t\t\t\t\torganizationId: this._pendingAuth.organizationId,\n\t\t\t\t\t}\n\n\t\tthis._iframe.contentWindow.postMessage(message, iframeOrigin)\n\t}\n\n\t/**\n\t * Requests graceful iframe teardown and resolves once acknowledged or timed out.\n\t *\n\t * @param iframeOrigin - Trusted origin used for postMessage target filtering.\n\t * @param reason - Lifecycle reason for teardown.\n\t * @returns Promise resolved when teardown flow completes.\n\t */\n\tprivate _requestTeardown(\n\t\tiframeOrigin: string,\n\t\treason: 'disconnect' | 'unmount' | 'reload',\n\t): Promise<void> {\n\t\tif (!this._iframeReady || !this._iframe?.contentWindow) {\n\t\t\treturn Promise.resolve()\n\t\t}\n\n\t\tif (this._teardownPending) {\n\t\t\twindow.clearTimeout(this._teardownPending.timeoutId)\n\t\t\tthis._teardownPending.resolve()\n\t\t\tthis._teardownPending = null\n\t\t}\n\n\t\tconst requestId = `teardown-${Date.now()}-${++this._teardownSequence}`\n\t\treturn new Promise((resolve) => {\n\t\t\tconst timeoutId = window.setTimeout(() => {\n\t\t\t\tif (this._teardownPending?.requestId === requestId) {\n\t\t\t\t\tconsole.debug('[Char] Teardown timed out after 1000ms, requestId:', requestId)\n\t\t\t\t\tthis._teardownPending = null\n\t\t\t\t\tresolve()\n\t\t\t\t}\n\t\t\t}, 1000)\n\n\t\t\tthis._teardownPending = {\n\t\t\t\trequestId,\n\t\t\t\ttimeoutId,\n\t\t\t\tresolve: () => {\n\t\t\t\t\twindow.clearTimeout(timeoutId)\n\t\t\t\t\tresolve()\n\t\t\t\t},\n\t\t\t}\n\n\t\t\tthis._iframe?.contentWindow?.postMessage(\n\t\t\t\t{\n\t\t\t\t\ttype: 'char-teardown',\n\t\t\t\t\trequestId,\n\t\t\t\t\treason,\n\t\t\t\t} satisfies CharHostToIframeMessage,\n\t\t\t\tiframeOrigin\n\t\t\t)\n\t\t})\n\t}\n\n\t/**\n\t * Completes an in-flight teardown promise when the iframe acknowledges it.\n\t *\n\t * @param requestId - Optional request identifier to match against pending teardown.\n\t */\n\tprivate _resolvePendingTeardown(requestId?: string): void {\n\t\tconst pending = this._teardownPending\n\t\tif (!pending) return\n\t\tif (requestId && requestId !== pending.requestId) return\n\t\tthis._teardownPending = null\n\t\tpending.resolve()\n\t}\n\n\t/**\n\t * Handles open-link requests from the iframe with protocol allowlisting.\n\t * This allowlist intentionally mirrors the iframe-side pre-filter.\n\t *\n\t * @param requestId - Link request identifier supplied by the iframe.\n\t * @param url - Requested URL string.\n\t * @param iframeOrigin - Trusted origin used for postMessage target filtering.\n\t */\n\tprivate _handleOpenLinkRequest(requestId: string, url: string, iframeOrigin: string): void {\n\t\t// TODO(security#4): trusted-host assumption currently defers destination domain allowlisting.\n\t\tlet ok = false\n\t\tlet error: string | undefined\n\t\ttry {\n\t\t\tconst parsed = new URL(url, window.location.href)\n\t\t\tconst protocol = parsed.protocol.toLowerCase()\n\t\t\tconst isAllowedProtocol =\n\t\t\t\tprotocol === 'http:' ||\n\t\t\t\tprotocol === 'https:' ||\n\t\t\t\tprotocol === 'mailto:' ||\n\t\t\t\tprotocol === 'tel:'\n\t\t\tif (!isAllowedProtocol) {\n\t\t\t\tthrow new Error(`Unsupported URL protocol: ${protocol}`)\n\t\t\t}\n\n\t\t\tconst popup = window.open(parsed.toString(), '_blank', 'noopener,noreferrer')\n\t\t\tok = popup !== null || protocol === 'mailto:' || protocol === 'tel:'\n\t\t\tif (!ok) {\n\t\t\t\terror = 'Popup blocked by browser'\n\t\t\t}\n\n\t\t\tthis.dispatchEvent(\n\t\t\t\tnew CustomEvent('char-open-link', {\n\t\t\t\t\t...CHAR_CUSTOM_EVENT_OPTIONS,\n\t\t\t\t\tdetail: {\n\t\t\t\t\t\trequestId,\n\t\t\t\t\t\turl: parsed.toString(),\n\t\t\t\t\t\tok,\n\t\t\t\t\t} satisfies CharOpenLinkDetail,\n\t\t\t\t})\n\t\t\t)\n\t\t} catch (err) {\n\t\t\terror = err instanceof Error ? err.message : String(err)\n\t\t\tconsole.error('[Char] open-link request failed:', err)\n\t\t}\n\n\t\tthis._iframe?.contentWindow?.postMessage(\n\t\t\t{ type: 'char-open-link-result', requestId, ok, error } satisfies CharHostToIframeMessage,\n\t\t\tiframeOrigin\n\t\t)\n\t}\n\n\t/**\n\t * Observe dark mode changes and forward to iframe via unified context.\n\t */\n\tprivate _observeDarkMode(): void {\n\t\tconst syncThemeAndStyles = () => {\n\t\t\tif (!this._iframeReady) return\n\t\t\tthis.setHostContext({\n\t\t\t\ttheme: isDarkMode() ? 'dark' : 'light',\n\t\t\t\tstyles: { variables: extractCharVariables(this) },\n\t\t\t})\n\t\t}\n\n\t\tthis._darkModeObserver = new MutationObserver(syncThemeAndStyles)\n\t\tthis._darkModeObserver.observe(document.documentElement, {\n\t\t\tattributes: true,\n\t\t\tattributeFilter: ['class', 'style', 'data-theme'],\n\t\t})\n\n\t\tthis._darkModeMediaQuery = window.matchMedia('(prefers-color-scheme: dark)')\n\t\tthis._darkModeMediaHandler = syncThemeAndStyles\n\t\tthis._darkModeMediaQuery.addEventListener('change', this._darkModeMediaHandler)\n\t}\n\n\t/**\n\t * Observe style attribute changes on <char-agent> to detect CSS variable updates.\n\t * Re-extracts variables and sends delta via unified context.\n\t */\n\tprivate _observeStyleChanges(): void {\n\t\tthis._styleObserver = new MutationObserver(() => {\n\t\t\tif (!this._iframeReady) return\n\t\t\tthis.setHostContext({ styles: { variables: extractCharVariables(this) } })\n\t\t})\n\t\tthis._styleObserver.observe(this, {\n\t\t\tattributes: true,\n\t\t\tattributeFilter: ['style', 'class'],\n\t\t})\n\t}\n\n\t/**\n\t * Observe host element dimensions to provide containerDimensions context.\n\t */\n\tprivate _observeContainerDimensions(): void {\n\t\tthis._containerResizeObserver = new ResizeObserver((entries) => {\n\t\t\tif (!this._iframeReady) return\n\t\t\tconst entry = entries[0]\n\t\t\tif (!entry) return\n\t\t\tthis.setHostContext({\n\t\t\t\tcontainerDimensions: {\n\t\t\t\t\twidth: Math.round(entry.contentRect.width),\n\t\t\t\t\theight: Math.round(entry.contentRect.height),\n\t\t\t\t},\n\t\t\t\tsafeAreaInsets: getSafeAreaInsets(),\n\t\t\t})\n\t\t})\n\t\tthis._containerResizeObserver.observe(this)\n\t}\n\n\t/**\n\t * Resolve devMode from property (React 19) or attribute.\n\t *\n\t * @returns Parsed dev-mode configuration when valid.\n\t */\n\tprivate _resolveDevMode(): DevModeConfig | undefined {\n\t\tif (this.devMode && typeof this.devMode === 'object') {\n\t\t\treturn this.devMode\n\t\t}\n\n\t\tconst devModeAttr = this.getAttribute('dev-mode')\n\t\tif (!devModeAttr) return undefined\n\n\t\ttry {\n\t\t\tconst parsed: unknown = JSON.parse(devModeAttr)\n\t\t\tif (!parsed || typeof parsed !== 'object' || Array.isArray(parsed)) {\n\t\t\t\tconst actual = Array.isArray(parsed) ? 'array' : typeof parsed\n\t\t\t\tconst msg = `dev-mode attribute must be a JSON object, got: ${actual}`\n\t\t\t\tconsole.warn(`[Char] ${msg}`)\n\t\t\t\tthis._emitCharError('INVALID_DEV_MODE', msg)\n\t\t\t\treturn undefined\n\t\t\t}\n\t\t\tconst obj = parsed as Record<string, unknown>\n\t\t\treturn {\n\t\t\t\tanthropicApiKey: typeof obj.anthropicApiKey === 'string' ? obj.anthropicApiKey : undefined,\n\t\t\t\topenaiApiKey: typeof obj.openaiApiKey === 'string' ? obj.openaiApiKey : undefined,\n\t\t\t\tuseLocalApi: typeof obj.useLocalApi === 'boolean' ? obj.useLocalApi : undefined,\n\t\t\t}\n\t\t} catch (e) {\n\t\t\tconst msg = `Failed to parse dev-mode attribute as JSON: ${e instanceof Error ? e.message : String(e)}`\n\t\t\tconsole.warn(`[Char] ${msg}`)\n\t\t\tthis._emitCharError('INVALID_DEV_MODE', msg)\n\t\t\treturn undefined\n\t\t}\n\t}\n\n\t/**\n\t * Resolve apiBase override from property (React 19) or attribute.\n\t *\n\t * @returns Sanitized API base URL without trailing slash.\n\t */\n\tprivate _resolveApiBaseOverride(): string | undefined {\n\t\tconst raw =\n\t\t\t(typeof this.apiBase === 'string' ? this.apiBase : this.getAttribute('api-base')) ??\n\t\t\tundefined\n\t\tconst trimmed = raw?.trim()\n\t\tif (!trimmed) return undefined\n\t\treturn trimmed.replace(/\\/+$/, '')\n\t}\n}\n\n/**\n * Type declaration for the Char custom element.\n * Useful for TypeScript consumers using refs.\n *\n * @public\n */\nexport type { CharAgentElement }\n\n/**\n * Legacy alias for `CharAgentElement`.\n *\n * @public\n */\nexport type CharElement = CharAgentElement\n\n/**\n * Backward-compatible alias for `CharAgentElement`.\n *\n * @public\n */\nexport type WebMCPAgentElement = CharAgentElement\n\n/**\n * Register the <char-agent> custom element.\n * Called automatically when importing this module.\n *\n * @param tagName - Optional custom tag name override.\n *\n * @public\n */\nexport function registerChar(tagName = 'char-agent'): void {\n\tif (typeof window === 'undefined') return\n\n\tif (!customElements.get(tagName)) {\n\t\tcustomElements.define(tagName, CharAgentElement)\n\t}\n}\n\nregisterChar()\n\ndeclare global {\n\tinterface HTMLElementTagNameMap {\n\t\t'char-agent': CharAgentElement\n\t}\n\n\t/* biome-ignore lint/style/noNamespace: JSX intrinsic-elements augmentation uses namespace syntax by TypeScript design. */\n\tnamespace JSX {\n\t\tinterface IntrinsicElements {\n\t\t\t'char-agent': {\n\t\t\t\t/** Development mode configuration (JSON string for HTML attributes). */\n\t\t\t\t'dev-mode'?: string\n\t\t\t\t/** Property form of dev mode config. */\n\t\t\t\tdevMode?: DevModeConfig\n\t\t\t\t/** Explicit API base override, e.g. `https://staging-app.usechar.ai`. */\n\t\t\t\t'api-base'?: string\n\t\t\t\t/** Property form of API base override. */\n\t\t\t\tapiBase?: string\n\t\t\t\t/** Enable debug tools for inspecting embedded agent internal state. */\n\t\t\t\t'enable-debug-tools'?: boolean\n\t\t\t\t/** Display mode for host orchestration: `pip` is collapsed launcher mode. */\n\t\t\t\t'display-mode'?: CharDisplayMode\n\t\t\t\t/** Callback when the close button inside the agent panel is clicked. */\n\t\t\t\tonClose?: () => void\n\t\t\t\t/** Imperative ref to the custom element instance. */\n\t\t\t\tref?: unknown\n\t\t\t\t[key: string]: unknown\n\t\t\t}\n\t\t}\n\t}\n}\n\ndeclare module 'react/jsx-runtime' {\n\t/* biome-ignore lint/style/noNamespace: JSX intrinsic-elements augmentation uses namespace syntax by TypeScript design. */\n\tnamespace JSX {\n\t\tinterface IntrinsicElements {\n\t\t\t'char-agent': {\n\t\t\t\t'dev-mode'?: string\n\t\t\t\tdevMode?: DevModeConfig\n\t\t\t\t'api-base'?: string\n\t\t\t\tapiBase?: string\n\t\t\t\t'enable-debug-tools'?: boolean\n\t\t\t\t'display-mode'?: CharDisplayMode\n\t\t\t\tonClose?: () => void\n\t\t\t\tref?: unknown\n\t\t\t\t[key: string]: unknown\n\t\t\t}\n\t\t}\n\t}\n}\n"],"mappings":"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ACA;CAGA;CACA;CACA;CACA;CACA;CACA;CAGA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CAGA;CACA;CACA;CAGA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CAGA;CACA;CACA;CACA;CACA;CACA;CAGA;CACA;CACA;CACA;CACA;CAGA;CACA;CACA;CACA;CAGA;CACA;CACA;CAGA;CACA;CACA;CACA;AAED,MAAM,8BAA8B;CAEnC;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CAGA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CAGA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CAGA;CACA;CACA;CACA;CACA;CACA;CACA;CAGA;CACA;CAGA;CACA;CACA;CACA;CAGA;CACA;CACA;CACA;CAGA;CACA;CACA;CACA;CACA;CACA;CACA;CAGA;CACA;CACA;CACA;CAGA;CACA;CACA;CACA;CACA;CACA;CACA;CAGA;CACA;CACA;CACA;CACA;CACA;CAGA;CAGA;CACA;CACA;CACA;CACA;AAED,MAAa,0BAA0B,CACtC,GAAG,4BACH,GAAG,4BACH;;;;;;;;;;;;ACpLD,SAAgB,cACf,OAC2B;AAC3B,QAAO,UAAU,YAAY,UAAU,gBAAgB,UAAU;;;;;ACvClE,SAAgB,gCACf,iBAC8B;AAC9B,KAAI,CAAC,cAAc,gBAAgB,CAClC;AAGD,QAAO;;;;;ACDR,SAAgB,YACf,SACA,OACwC;AACxC,KAAI,CAAC,MAAO,QAAO;AACnB,QAAO;EACN,GAAG;EACH,GAAG;EAEH,WAAW,MAAM,aAAa,SAAS;EACvC,OAAO,MAAM,SAAS,SAAS;EAC/B;;AAGF,SAAgB,aACf,SACA,OACgB;AAChB,KAAI,CAAC,MAAO,QAAO;AACnB,QAAO;EAAE,GAAG;EAAS,GAAG;EAAO;;AAGhC,SAAgB,iBACf,SACA,OACkB;CAClB,MAAM,WAAW,WAAW,EAAE;AAC9B,QAAO;EACN,GAAG;EACH,GAAG;EACH,QAAQ,YAAY,SAAS,QAAQ,MAAM,OAAO;EAClD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"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@mcp-b/char",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.2",
|
|
4
4
|
"description": "Char host shell. Framework-agnostic custom element that bridges to the iframe embed runtime.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"license": "UNLICENSED",
|
|
@@ -50,16 +50,6 @@
|
|
|
50
50
|
"publishConfig": {
|
|
51
51
|
"access": "public"
|
|
52
52
|
},
|
|
53
|
-
"dependencies": {},
|
|
54
|
-
"devDependencies": {
|
|
55
|
-
"@types/node": "^25.2.1",
|
|
56
|
-
"@types/react": "^19.2.10",
|
|
57
|
-
"happy-dom": "^20.5.0",
|
|
58
|
-
"tsdown": "^0.20.3",
|
|
59
|
-
"typescript": "^5.9.3",
|
|
60
|
-
"vitest": "^4.0.18",
|
|
61
|
-
"@mcp-b/shared-types": "0.0.1"
|
|
62
|
-
},
|
|
63
53
|
"scripts": {
|
|
64
54
|
"build": "rm -f dist/*.js dist/*.d.ts dist/*.map && NODE_ENV=production tsdown && pnpm docs:cem",
|
|
65
55
|
"dev": "tsdown --watch",
|
|
@@ -67,5 +57,15 @@
|
|
|
67
57
|
"test": "vitest run",
|
|
68
58
|
"check:types": "tsc --noEmit",
|
|
69
59
|
"check:lint": "biome lint src"
|
|
60
|
+
},
|
|
61
|
+
"dependencies": {},
|
|
62
|
+
"devDependencies": {
|
|
63
|
+
"@mcp-b/shared-types": "workspace:*",
|
|
64
|
+
"@types/node": "^25.2.1",
|
|
65
|
+
"@types/react": "^19.2.10",
|
|
66
|
+
"happy-dom": "^20.5.0",
|
|
67
|
+
"tsdown": "^0.20.3",
|
|
68
|
+
"typescript": "^5.9.3",
|
|
69
|
+
"vitest": "^4.0.18"
|
|
70
70
|
}
|
|
71
|
-
}
|
|
71
|
+
}
|
package/LICENSE
DELETED
|
@@ -1,23 +0,0 @@
|
|
|
1
|
-
Kukumis Inc. Proprietary License
|
|
2
|
-
|
|
3
|
-
Copyright (c) 2025 Kukumis Inc. All rights reserved.
|
|
4
|
-
|
|
5
|
-
This software, including all source code, object code, documentation, and any
|
|
6
|
-
related materials (collectively, the "Software"), is proprietary to Kukumis
|
|
7
|
-
Inc. and is protected by applicable intellectual property laws.
|
|
8
|
-
|
|
9
|
-
No part of the Software may be used, copied, modified, merged, published,
|
|
10
|
-
distributed, sublicensed, and/or sold, in whole or in part, except as expressly
|
|
11
|
-
authorized in a written license agreement signed by Kukumis Inc.
|
|
12
|
-
|
|
13
|
-
No license or other rights are granted by Kukumis Inc. under any patents,
|
|
14
|
-
copyrights, trade secrets, trademarks, or other intellectual property rights,
|
|
15
|
-
whether by implication, estoppel, or otherwise, except as expressly set forth
|
|
16
|
-
in such written license agreement.
|
|
17
|
-
|
|
18
|
-
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
19
|
-
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
20
|
-
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
|
|
21
|
-
KUKUMIS INC. BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
|
|
22
|
-
ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
|
23
|
-
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|