@matterfact/embed 0.13.0 → 0.16.0
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/README.md +37 -2
- package/dist/react.cjs +275 -28
- package/dist/react.cjs.map +1 -1
- package/dist/react.d.cts +164 -11
- package/dist/react.d.ts +164 -11
- package/dist/react.js +264 -19
- package/dist/react.js.map +1 -1
- package/examples/embed-demo/.env.example +9 -0
- package/examples/embed-demo/README.md +66 -12
- package/examples/embed-demo/src/App.tsx +56 -12
- package/examples/embed-demo/src/config.ts +8 -0
- package/package.json +1 -1
package/dist/react.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/react.tsx","../src/protocol.ts","../src/geometry.ts","../src/loader.ts"],"sourcesContent":["/**\n * `@matterfact/embed/react` — the React front door.\n *\n * A thin wrapper over the SAME `EmbedHost` the `<script>` loader uses: it drives the\n * widget from props and tears it down on unmount, so React hosts don't hand-write a\n * script tag, vendor the loader, or juggle globals. The widget itself is a\n * fixed-position floating bubble (a cross-origin iframe the host page can't style), so\n * the component renders nothing into the React tree.\n */\n'use client';\n\nimport { useEffect, useRef } from 'react';\n\nimport { mount, type EmbedHost, type LoaderConfig } from './loader';\nimport type {\n DeclaredArtifact,\n MatterfactEvent,\n PageContext,\n SiteMapEntry,\n ToolEvent,\n} from './protocol';\n// Type-only, same rationale as loader.ts's identical import: the adapter's actual\n// code stays inside the lazy ./context chunk, this component only ever carries the\n// plain config shape.\nimport type { HoistActionsConfig } from './adapters/hoist';\nimport type { HostResolvers, HostToolDef } from './adapters/registry';\n// Re-exported for hosts: typing a `tools`/`resolve` prop needs these shapes, and the\n// `adapters/*` path is internal. (MatterfactEvent/ToolEvent ride `@matterfact/embed`.)\nexport type { HostResolvers, HostToolDef } from './adapters/registry';\n\nconst DEFAULT_ORIGIN = 'https://app.matterfact.com';\n\nexport interface MatterfactAgentProps {\n /** Publishable key (`pk_…`) identifying this embed app. Public, origin-scoped. */\n publishableKey: string;\n /** Origin serving the widget (the matterfact app). Defaults to production. */\n widgetOrigin?: string;\n /** Label for THIS embedding, for per-surface usage/history attribution. */\n surface?: string;\n theme?: 'light' | 'dark' | 'auto';\n /**\n * HOST-AUTH PASSTHROUGH (trusted first-party embeds only). Return a token this\n * matterfact deployment already trusts — the host app's Firebase idToken, an Entra\n * token, whatever its auth provider verifies — and the widget signs in SILENTLY with\n * it, no separate sign-in. Called on demand so the token can be fresh. Omit for the\n * standard popup/inline sign-in (third-party hosts).\n */\n getAuthToken?: () => string | null | Promise<string | null>;\n /**\n * PULL-BASED PAGE CONTEXT. Return what this page is right now — its route, a one-line\n * description, the entities it's about — and the widget pulls it FRESH at the start of\n * every turn (symmetric with `getAuthToken`). Use this when your context changes without\n * a URL navigation — a virtualized SPA where a team switch, filter edit or selection\n * changes what the agent should know, but the path doesn't. Called on demand; may be\n * async. Omit to keep the static `window.matterfact.context` behaviour.\n */\n getPageContext?: () =>\n | PageContext\n | null\n | undefined\n | Promise<PageContext | null | undefined>;\n /**\n * Render the agent HERE — in your own side panel, drawer or tab — instead of as a\n * floating bubble in the corner.\n *\n * The component then renders a real element and the chat fills it, sized by your CSS.\n * No launcher is drawn and the widget never resizes itself: inside your panel, the box\n * and the chrome are yours. Showing and hiding that panel is the open/close control.\n */\n inline?: boolean;\n /** Only with `inline` — applied to the element the chat fills. */\n className?: string;\n /** Only with `inline`. Merged over the default 100%×100% fill. */\n style?: React.CSSProperties;\n /**\n * Turn off ALL host-page observation — no context, no focus, no snapshot, no\n * activity, no artifact grants; the loader never even installs the observers.\n * Default `true`. Pass `false` for a host that wants agent chat with nothing\n * about the page ever reaching it.\n */\n pageContext?: boolean | 'full' | 'declared' | 'off';\n /**\n * Force dev mode programmatically. The URL trigger (`?mfdev=1`, see `loader.ts`)\n * still works unchanged — this is the React-prop equivalent, and the two are\n * OR'd together.\n */\n dev?: boolean;\n /**\n * Turn on agent-driven host tools (today: `hoist.navigate`) at the given confirm\n * tier, e.g. `{ navigate: 'confirm' }`. Default is every action tool OFF; this is\n * opt-in only.\n *\n * This is just sugar over setting `window.matterfact.hoist.actions` yourself — the\n * LAZY chunk (adapters/hoist-runtime.ts's `readActionsConfig`) reads that global\n * directly at advertise/execute time, so there is nothing for the eager loader to\n * carry. See `writeActionsGlobal` below.\n */\n actions?: HoistActionsConfig;\n /**\n * The app's site map, annotated with each route's content class — so the agent knows\n * which routes carry matterfact content (a co-embedded artifact, a document/report) or\n * the host's own data, and can navigate toward them.\n *\n * Classification only: give the route pattern + label + a `content` tag; NEVER a\n * capability token (the concrete artifact/document on the current page arrives via page\n * context / the live iframe). Sugar over `window.matterfact.sitemap`, read by the loader\n * on load and on navigation.\n */\n sitemap?: SiteMapEntry[];\n /**\n * Declared artifact grants — make a board reachable from ANY route, even ones where\n * its `<MatterfactArtifact>` iframe isn't mounted. Replaces the pattern of mounting\n * hidden zero-size iframes purely to smuggle a read token into the DOM. The token\n * rides the grant channel, never message text. Sugar over `window.matterfact.artifacts`.\n */\n artifacts?: DeclaredArtifact[];\n /**\n * matterfact-defined resolver contracts the HOST implements. matterfact fixes the tool\n * name, schema and agent semantics (so an integration spends no instruction budget on\n * them); you supply the function.\n *\n * - `document({doctype, key})` → `{id, label, href?}` — resolve an off-page matterfact\n * document (dossier/report) to its id, e.g. via your own content-location lookup.\n * - `artifact({slug})` → `{id, owner, token, label}` — resolve an artifact to a read grant.\n *\n * The agent calls these when the site map shows the content lives on another route, and\n * renders the result as an `<MFRef>` chip. Sugar over `window.matterfact.resolve`.\n */\n resolve?: HostResolvers;\n /**\n * Host-defined tools (`app.*`): your own functions, offered to the agent WebMCP-style.\n * Each is `{name, description, inputSchema?, confirm?, handler}`; the name is normalised\n * into the `app.` namespace. A tool is only actually exposed if this app's server-side\n * policy allows it. Sugar over `window.matterfact.tools`.\n */\n tools?: HostToolDef[];\n /**\n * Host telemetry hook: fired on every host-tool advertise/call/result so you can pipe\n * calls into your own observability. Never blocks or breaks the call path — a throw here\n * is swallowed. Sugar over `window.matterfact.onToolEvent`.\n *\n * NOTE: this is now a SUBSET of `onEvent` (the tool events only), kept for back-compat.\n * Prefer `onEvent` for new integrations — it delivers these same tool events (tagged\n * `type:'tool'`) alongside the widget's lifecycle and chat events.\n */\n onToolEvent?: (e: ToolEvent) => void;\n /**\n * UNIFIED host telemetry hook: fired on every embed lifecycle moment — `ready`,\n * `open`/`close`, `navigate`, `auth`, `error`, `chat`, and `tool` — so you can pipe the\n * whole widget into your own observability from one place. Never blocks or breaks the\n * widget; a throw here is swallowed. Sugar over `window.matterfact.onEvent`.\n */\n onEvent?: (e: MatterfactEvent) => void;\n}\n\n/**\n * Mirror the `actions` prop onto `window.matterfact.hoist.actions` — the one place\n * the lazy chunk actually reads it (see the `actions` prop's doc comment above).\n * Merged shallowly so this never clobbers `hoist.rows`/`excludeModels` (readHoistConfig)\n * or unrelated `window.matterfact.*` keys (`getEmbedAuthToken`, `context`, …).\n *\n * Written unconditionally — including `{navigate:'off'}` when the prop is absent —\n * so a host that stops passing `actions` (or never did) reliably gets the documented\n * default rather than whatever a PRIOR render (or a stray external script) left\n * behind. A no-op outside the browser.\n */\nfunction writeActionsGlobal(actions: HoistActionsConfig | undefined): void {\n if (typeof window === 'undefined') return;\n const w = window as { matterfact?: { hoist?: Record<string, unknown> } };\n const mf = (w.matterfact ??= {});\n mf.hoist = { ...mf.hoist, actions: actions ?? { navigate: 'off' } };\n}\n\n/**\n * Mirror the `sitemap` prop onto `window.matterfact.sitemap`, where the lazy context\n * chunk reads it (`readSitemap` in context.ts) on load and on navigation. Written only\n * when the prop is supplied, so a host that never sets a site map leaves the global\n * untouched (and unrelated `window.matterfact.*` keys alone). A no-op outside the browser.\n */\nfunction writeSitemapGlobal(sitemap: SiteMapEntry[] | undefined): void {\n if (typeof window === 'undefined' || sitemap === undefined) return;\n const w = window as { matterfact?: Record<string, unknown> };\n (w.matterfact ??= {}).sitemap = sitemap;\n}\n\n/**\n * Mirror the tool-surface props onto the globals the lazy context chunk reads\n * (`readDeclaredGrants`, and the registry's `resolve`/`tools`/`onToolEvent`/`onEvent`). Written\n * every render — including when a prop is absent, clearing a stale global — but ONLY\n * the values that change identity freely (functions) need care: they ride here, NOT the\n * mount effect's deps, so a host passing an inline `resolve={{document: …}}` or a new\n * `onToolEvent` closure each render never remounts the iframe. See the mount effect's\n * `toolsKey`, which keys remounts on the DESCRIPTORS only. A no-op outside the browser.\n */\nfunction writeToolGlobals(\n artifacts: DeclaredArtifact[] | undefined,\n resolve: HostResolvers | undefined,\n tools: HostToolDef[] | undefined,\n onToolEvent: ((e: ToolEvent) => void) | undefined,\n onEvent: ((e: MatterfactEvent) => void) | undefined,\n): void {\n if (typeof window === 'undefined') return;\n const mf = ((window as { matterfact?: Record<string, unknown> }).matterfact ??= {});\n mf.artifacts = artifacts ?? [];\n mf.resolve = resolve ?? {};\n mf.tools = tools ?? [];\n mf.onToolEvent = onToolEvent;\n mf.onEvent = onEvent;\n}\n\n/** The advertise-shaping fields of the tool props — everything that changes what the\n * agent is TOLD exists, and so must force a remount + re-advertise. Deliberately EXCLUDES\n * handler/callback identity (bodies ride the global, read fresh at call time), so an\n * inline handler literal doesn't remount the widget on every render. */\nfunction toolDescriptorKey(\n artifacts: DeclaredArtifact[] | undefined,\n resolve: HostResolvers | undefined,\n tools: HostToolDef[] | undefined,\n): string {\n return JSON.stringify({\n // Artifact identity is data (slug/owner/label), not a function.\n artifacts: (artifacts ?? []).map((a) => ({\n slug: a.slug,\n owner: a.owner,\n label: a.label,\n })),\n // For resolvers, only WHICH contracts are present changes the advertised set.\n resolve: {\n document: typeof resolve?.document === 'function',\n artifact: typeof resolve?.artifact === 'function',\n },\n // For host tools, the advertised descriptor — never the handler.\n tools: (tools ?? []).map((t) => ({\n name: t.name,\n description: t.description,\n inputSchema: t.inputSchema,\n confirm: t.confirm,\n })),\n });\n}\n\nexport function MatterfactAgent({\n publishableKey,\n widgetOrigin,\n surface = '',\n theme = 'auto',\n getAuthToken,\n getPageContext,\n inline = false,\n className,\n style,\n pageContext,\n dev,\n actions,\n sitemap,\n artifacts,\n resolve,\n tools,\n onToolEvent,\n onEvent,\n}: MatterfactAgentProps) {\n // The live callbacks ride refs so a new getAuthToken/getPageContext identity never\n // remounts the iframe — only the identity-defining config below does.\n const authRef = useRef(getAuthToken);\n authRef.current = getAuthToken;\n const pageContextRef = useRef(getPageContext);\n pageContextRef.current = getPageContext;\n // `actions` rides a ref + a VALUE key for the same reason, and it matters more\n // than it looks: the natural way to write this prop is an inline literal,\n // `actions={{ navigate: 'confirm' }}`, which is a brand-new object on every\n // render. Depending on its identity meant the widget tore itself down and\n // remounted on ANY re-render of the host page — losing the open chat, the\n // selected conversation, and any host-tool result still in flight (the run then\n // waits out its whole budget and reports that the page never confirmed).\n // Observed live: navigating re-rendered the host, which remounted the iframe.\n const actionsRef = useRef(actions);\n actionsRef.current = actions;\n const actionsKey = JSON.stringify(actions ?? null);\n const slot = useRef<HTMLDivElement>(null);\n\n // Publish the site map to its global whenever its CONTENT changes (an inline\n // `sitemap={[…]}` is a new array each render, so key on the serialization, not\n // identity — same trick as `actionsKey`). Declared BEFORE the mount effect so on\n // first mount it runs first and the global is set before start() reads it; a later\n // change re-writes the global without remounting (the loader re-reads it on nav).\n const sitemapKey = JSON.stringify(sitemap ?? null);\n useEffect(() => {\n writeSitemapGlobal(sitemap);\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [sitemapKey]);\n\n // The tool-surface globals are refreshed EVERY render (not keyed), because the\n // handler/callback bodies must always be the latest — the loader reads them fresh at\n // call time. Only the DESCRIPTORS (toolsKey below) gate a remount. `resolve`/`tools`/\n // `onToolEvent` are intentionally NOT mount-effect deps for exactly this reason.\n useEffect(() => {\n writeToolGlobals(artifacts, resolve, tools, onToolEvent, onEvent);\n });\n const toolsKey = toolDescriptorKey(artifacts, resolve, tools);\n\n useEffect(() => {\n if (typeof window === 'undefined') return;\n // Inline mounts into the div we rendered, so it has to exist first. It always does\n // by effect time; the guard is for the impossible case, not a race.\n if (inline && !slot.current) return;\n // Before mounting: publish `actions` to the global the lazy chunk actually reads\n // (see writeActionsGlobal's doc comment) so the remount below's start() call\n // advertises under the CURRENT prop value, not a stale one.\n writeActionsGlobal(actionsRef.current);\n // Same reason as writeActionsGlobal: publish the tool surface before mount so the\n // remount's start() advertises the CURRENT props, not a stale global.\n writeToolGlobals(artifacts, resolve, tools, onToolEvent, onEvent);\n const config: LoaderConfig = {\n publishableKey,\n origin: widgetOrigin || DEFAULT_ORIGIN,\n theme,\n surface,\n // Always present; a null return (no getAuthToken supplied) makes the core fall\n // back to the popup/inline sign-in, so this is safe either way.\n authTokenProvider: async () => (await authRef.current?.()) ?? null,\n // Always present; a null return (no getPageContext supplied) makes the core fall\n // back to window.matterfact.getPageContext / window.matterfact.context.\n pageContextProvider: async () =>\n (await pageContextRef.current?.()) ?? null,\n container: inline ? slot.current : null,\n pageContext,\n dev,\n };\n let host: EmbedHost | null = null;\n try {\n host = mount(config);\n } catch (e) {\n console.error('[matterfact] failed to mount the embed widget', e);\n // Surface the failure to host telemetry (read fresh off the global, the same\n // channel writeToolGlobals feeds; a throw in the host's handler is swallowed).\n try {\n (\n window as { matterfact?: { onEvent?: (ev: MatterfactEvent) => void } }\n ).matterfact?.onEvent?.({\n type: 'error',\n message: e instanceof Error ? e.message : String(e),\n });\n } catch {\n /* host telemetry must never mask the original mount failure */\n }\n }\n return () => host?.destroy();\n // A genuine CHANGE to `actions` still has to remount, so the fresh start()\n // re-advertises under the new policy — hence actionsKey (the serialized\n // value) rather than `actions` (the object identity). See the ref above.\n }, [\n publishableKey,\n widgetOrigin,\n surface,\n theme,\n inline,\n pageContext,\n dev,\n actionsKey,\n // A change to the advertised tool SHAPE remounts so start() re-advertises under it;\n // a change to only a handler body or callback identity does not (see toolDescriptorKey).\n toolsKey,\n // eslint-disable-next-line react-hooks/exhaustive-deps\n ]);\n\n // Corner mode renders nothing into the tree — it's a fixed-position iframe and where\n // you put the component is irrelevant. Inline is the opposite, and that's the point:\n // the agent appears exactly where you place this, sized by your own CSS, so it can sit\n // in a side panel or a tab like any other component you own.\n if (!inline) return null;\n return (\n <div\n ref={slot}\n className={className}\n style={{ width: '100%', height: '100%', ...style }}\n />\n );\n}\n\nconst DEFAULT_ARTIFACT_ORIGIN = 'https://app.matterfact.com';\n\nexport interface MatterfactArtifactProps {\n /** The artifact's slug/name. */\n slug: string;\n /** The artifact owner's email — same as the Share dialog's `?owner=` param. */\n owner: string;\n /** The artifact's read-only capability token — same as the Share dialog's `?t=` param. */\n token: string;\n /** Origin serving the widget (the matterfact app). Defaults to production. */\n widgetOrigin?: string;\n theme?: 'auto' | 'light' | 'dark';\n className?: string;\n style?: React.CSSProperties;\n}\n\n/**\n * Render a matterfact artifact inline in a host page. Chrome-free, cross-origin. When a\n * MatterfactAgent is embedded on the same page, the loader sees this iframe and both\n * makes the agent aware of it and (via its token) lets the agent read its data.\n */\nexport function MatterfactArtifact({\n slug,\n owner,\n token,\n widgetOrigin,\n theme = 'auto',\n className,\n style,\n}: MatterfactArtifactProps) {\n const origin = widgetOrigin || DEFAULT_ARTIFACT_ORIGIN;\n const src =\n `${origin}/embed/artifacts/${encodeURIComponent(slug)}` +\n `?owner=${encodeURIComponent(owner)}&t=${encodeURIComponent(token)}&theme=${theme}`;\n return (\n <iframe\n src={src}\n title={`matterfact artifact ${slug}`}\n className={className}\n style={{ width: '100%', height: 600, border: 0, ...style }}\n // `allow-downloads` so an in-place `<a download>`.click() inside the\n // artifact (e.g. a deck's \"Export to PowerPoint\", a CSV/file export)\n // actually saves. Without it a sandboxed frame blocks the download and the\n // browser falls back to opening the file in a new tab. The sibling artifact\n // iframes (embed loader, co-embedded side panel) already carry this token;\n // this standalone embed component was the last one missing it.\n sandbox=\"allow-scripts allow-same-origin allow-forms allow-popups allow-popups-to-escape-sandbox allow-downloads\"\n />\n );\n}\n","/**\n * The matterfact embed bridge protocol.\n *\n * This is the contract between the HOST PAGE (a customer's site, running our\n * loader) and the WIDGET (our chat, running in a cross-origin iframe on\n * app.matterfact.com).\n *\n * Two things to understand before changing anything here:\n *\n * 1. THE IFRAME CANNOT SEE THE PAGE. It is cross-origin — that is the entire\n * point of it. So every fact about the host page (its URL, its DOM, what the\n * user just did) reaches the agent only because the LOADER, which runs as\n * ordinary page JS and *can* see it, chose to send it. The loader is the eye;\n * the widget is the mouth.\n *\n * 2. THIS CONTRACT OUTLIVES THE IFRAME. The next tier is an in-page React SDK\n * with no iframe at all — and it will speak exactly this. If the message\n * shapes are right, \"deeper integration\" is a transport swap (postMessage ->\n * direct call), not a rewrite. So model the DOMAIN here, not the plumbing.\n */\n\n// v3 (2026-07): adds `host.sitemap` (a structured, content-annotated site map),\n// `widget.navigate` (deeplink the host to one of its own routes), and `widget.chat`\n// (content-free chat-turn telemetry forwarded to the host's `onEvent`). All additive —\n// a peer that doesn't know a message simply never sends/handles it — so this is a\n// capability signal, not a breaking change. No code branches on the version number.\nexport const PROTOCOL_VERSION = 3;\n\n/** Every message is namespaced so we never collide with the host page's own postMessage traffic. */\nexport const CHANNEL = 'mf-embed';\n\n/* ────────────────────────────── refs ──────────────────────────────────── */\n\n/**\n * A handle on a real element in the host page.\n *\n * Format and semantics are lifted from Playwright's ARIA snapshot (`ref=e12`),\n * for a specific reason: it is the element-handle syntax the frontier models have\n * seen most, via playwright-mcp. We are not being original here on purpose.\n *\n * The ref is an in-page expando plus a snapshot-scoped Map — never an attribute,\n * so it is invisible to the host page's CSS, its serialization, and its own\n * mutation observers. It is resolved back to an element by the LOADER; the agent\n * never touches the DOM, and never learns a selector.\n */\nexport type ElementRef = string; // 'e12'\n\n/* ──────────────────────────── host → widget ───────────────────────────── */\n\n/** Ambient facts about the page. Cheap, sent on every navigation. */\nexport interface PageContext {\n url?: string;\n /** A route PATTERN ('/orders/:id') beats a raw path — it tells the agent what KIND of page this is. */\n route?: string;\n path?: string;\n title?: string;\n locale?: string;\n /** 1–2 sentences, written the way you'd brief a colleague. The highest-value field by far. */\n description?: string;\n /** Things the page is about. These also become @-mentionable in the composer, for free. */\n entities?: PageEntity[];\n /** Allowlisted only. Never a dump of app state. */\n data?: Record<string, unknown>;\n}\n\nexport interface PageEntity {\n /** Host kinds are namespaced (`x:ticker`) so they can never collide with matterfact's own. */\n kind: string;\n id: string;\n label: string;\n /** Pre-rendered, agent-readable. Keep it short. */\n text?: string;\n data?: Record<string, unknown>;\n}\n\n/**\n * How a route's content is classified, for the annotated site map.\n *\n * The host DECORATES the site map we auto-derive (see `SiteMapEntry`) so the agent\n * knows which routes carry MATTERFACT content and of what kind — app-wide awareness\n * it can navigate toward. This is CLASSIFICATION only: no per-instance ids, and never\n * a capability token. The concrete artifact/document on the CURRENT page still arrives\n * via `host.context` entities / `host.artifactGrants`, resolved per page.\n *\n * - `mf-artifact` — a co-embedded matterfact artifact (its slug); the token stays on\n * the live iframe / grant, never here.\n * - `mf-document` — a matterfact document (dossier/report), resolved to an MF_DOC_ID\n * host-side per page.\n * - `host-data` — the host's own data; not a matterfact entity.\n */\nexport type ContentClass =\n | { kind: 'mf-artifact'; slug: string }\n | { kind: 'mf-document'; doctype: string }\n | { kind: 'host-data' };\n\n/**\n * One route in the annotated site map: the path pattern + label we derived, plus the\n * host's content classification. `current` marks the route the user is on.\n */\nexport interface SiteMapEntry {\n /** Route pattern or path, e.g. '/app/company/:ticker/dossier'. */\n path: string;\n /** Human label for the route ('Company Dossier'). */\n label?: string;\n /** The host's classification of this route's content, when it annotated it. */\n content?: ContentClass;\n current?: boolean;\n}\n\n/** A co-embedded artifact's read-only capability, for the backend to materialize its\n * data. Backend-only — never rendered. `token` is the artifact's own embed token. */\nexport interface ArtifactGrant {\n id: string;\n owner: string;\n token: string;\n}\n\n/**\n * A DECLARED artifact grant — the `<MatterfactAgent artifacts>` prop (or\n * `window.matterfact.artifacts`). Same wire shape as a grant discovered by scanning\n * co-embedded `<MatterfactArtifact>` iframes, plus an optional `label`; it feeds the\n * SAME `host.artifactGrants` channel. This is how a host makes a board reachable from\n * a route where its iframe ISN'T mounted, without hidden zero-size iframes smuggling\n * the token into the DOM. The token stays in the grant channel, never in message text.\n */\nexport interface DeclaredArtifact {\n slug: string;\n owner: string;\n token: string;\n label?: string;\n}\n\n/**\n * One host-tool lifecycle event, surfaced to the host via `onToolEvent` (and the dev\n * panel) — the host's own telemetry hook. Fired loader-side; a throwing or slow\n * callback here NEVER blocks or breaks the call path.\n *\n * - `advertise` — the merged tool list changed (fired with the new set's names).\n * - `call` — a tool call is about to run (`args` present).\n * - `result` — the call settled (`ok`/`error`/`ms`).\n */\nexport interface ToolEvent {\n phase: 'advertise' | 'call' | 'result';\n name: string;\n toolClass: 'hoist' | 'mf' | 'app';\n args?: unknown;\n ok?: boolean;\n error?: string;\n ms?: number;\n}\n\n/**\n * One embed lifecycle event, surfaced to the host via `onEvent` — the host's single\n * telemetry hook. Fired loader-side (lifecycle) and widget-side (chat); a throwing or\n * slow callback NEVER blocks or breaks the mount/call/auth/run path.\n *\n * - `ready` — the widget connected (protocol handshake done).\n * - `open`/`close` — the corner widget was opened/closed. (Inline has no open event —\n * the host's own panel is the open/close control.)\n * - `navigate` — the agent deeplinked the host to one of its own routes.\n * - `auth` — host-auth passthrough: `required` (widget asked), `granted` (a\n * token was handed over), `failed` (no provider result / the provider threw).\n * - `error` — the widget failed to mount / load.\n * - `chat` — a chat-turn moment. Content-free: phase + opaque chatId only.\n * - `tool` — a host-tool lifecycle event (the `ToolEvent` shape, tagged). Also\n * still delivered untagged to the legacy `onToolEvent` hook for back-compat.\n * - `artifactParams` — an artifact param changed in the widget.\n */\n/**\n * A value an artifact param can carry. Mirrors the app's `ParamValue`\n * (`enum` / `enum[]` / `entity` / `date` / `daterange` / `boolean` / `number`)\n * DELIBERATELY BY COPY, not by import: this is a wire contract between two\n * independently-deployed artifacts, and a shared type would let a rename\n * compile on both sides while still breaking the wire. Bump PROTOCOL_VERSION\n * instead. See this file's header.\n */\nexport type ArtifactParamValue =\n | string\n | string[]\n | number\n | boolean\n /** `daterange`: a [from, to] pair of YYYY-MM-DD strings. */\n | [string, string];\n\n/**\n * Artifact params on the wire, keyed by declared param name.\n *\n * Values are typed but NOT verified against the artifact's declared spec —\n * a host can send anything, and the widget's param store coerces each value\n * to its declared type (or drops it) on arrival. This type says what a\n * well-behaved host means to send, not what the widget trusts it to have sent.\n */\nexport type ArtifactParams = { [name: string]: ArtifactParamValue };\n\nexport type MatterfactEvent =\n | { type: 'ready' }\n | { type: 'open' }\n | { type: 'close' }\n | { type: 'navigate'; href: string }\n | { type: 'auth'; phase: 'required' | 'granted' | 'failed' }\n | { type: 'error'; message: string }\n | {\n type: 'chat';\n phase: 'message' | 'response-start' | 'response-end';\n chatId?: string;\n }\n | ({ type: 'tool' } & ToolEvent)\n | {\n type: 'artifactParams';\n params: ArtifactParams;\n source: 'user' | 'context';\n };\n\n/**\n * A snapshot of the page as the agent sees it.\n *\n * `yaml` is Playwright's ARIA-snapshot rendering — role, accessible name, state,\n * and a ref on anything interactable:\n *\n * - heading \"Orders\" [level=1]\n * - textbox \"Search orders\" [ref=e5]\n * - button \"Export CSV\" [ref=e9]\n *\n * NOT raw HTML. A real page is tens of thousands of tokens of div soup, it gives\n * the model no handle to act with, and it is a prompt-injection firehose. The\n * a11y tree is a tenth the size and is the only signal that survives in-page —\n * `getEventListeners()` is DevTools-only, so listener-sniffing (the browser-use\n * approach) cannot see a single React handler on a real customer's app.\n */\nexport interface DomSnapshot {\n yaml: string;\n /** Monotonic. A ref is only valid within the snapshot that minted it. */\n seq: number;\n /** True when the walk hit its node budget and stopped. The agent must be told it is not seeing everything. */\n truncated: boolean;\n}\n\n/**\n * Where the user's attention is RIGHT NOW.\n *\n * This is the difference between an assistant that can read your page and one that\n * knows what you're doing. A full DOM snapshot answers \"what is on this screen\"; this\n * answers \"what are you looking at and working in\" — which is what a colleague\n * leaning over reads first. It is small, so it rides on every turn, where the full\n * snapshot does not.\n *\n * Every field here is a LABEL or a REF, never a value. `selection` is the sharpest\n * intent signal there is (\"this is the thing I'm asking about\"), and for exactly that\n * reason it is also where a user might have highlighted their own email — so it is\n * redacted and honours data-mf-private like everything else.\n */\nexport interface FocusContext {\n /** Text the user has selected. The single strongest \"I'm asking about this\" signal. Redacted. */\n selection?: string;\n /** The control the user is working IN — by label and ref, never its contents. */\n focused?: { ref?: ElementRef; label: string; role: string };\n /** The refs currently in the viewport: the handful of things actually on screen, out of the whole tree. */\n visibleRefs?: ElementRef[];\n /** How far down the page they are, 0–1. A cheap proxy for \"which part am I reading\". */\n scroll?: number;\n}\n\n/** Something the user did. Shaped after PostHog's autocapture — one readable line, not a replay frame. */\nexport interface ActivityEvent {\n seq: number;\n ts: number;\n type: 'nav' | 'click' | 'input' | 'submit' | 'selection' | 'custom';\n /** Human-readable: 'clicked button \"Export CSV\"'. Semantic, not coordinates. */\n summary: string;\n ref?: ElementRef;\n data?: Record<string, unknown>;\n}\n\n/**\n * A tool the HOST page exposes to the agent.\n *\n * Deliberately shaped as a superset of WebMCP (`navigator.modelContext`, W3C\n * WebML CG draft / Chrome origin trial). A customer who adopts WebMCP should be\n * able to hand us their existing tool declarations unchanged.\n */\nexport interface HostTool {\n name: string;\n description: string;\n /** JSON Schema. */\n inputSchema: Record<string, unknown>;\n /**\n * A read-only tool may run without asking. Anything that MUTATES the page\n * requires the user to confirm, every time — see the note on ToolCall.\n */\n readOnly?: boolean;\n /**\n * The tiered confirm policy, set by the host (e.g. `window.matterfact.hoist.actions`):\n * `'required'` ⇒ the widget must show a confirm card and get the user's approval\n * before every call; `'auto'` ⇒ it may call without asking. Absent for a tool that\n * predates this policy (treat as `'required'` — the safer default).\n */\n confirm?: 'auto' | 'required';\n}\n\nexport type HostToWidget =\n | { type: 'host.ready'; protocol: number; origin: string }\n | { type: 'host.context'; context: PageContext }\n | { type: 'host.focus'; focus: FocusContext }\n | { type: 'host.snapshot'; snapshot: DomSnapshot }\n | { type: 'host.region'; ref: ElementRef; yaml: string }\n | { type: 'host.activity'; events: ActivityEvent[] }\n | { type: 'host.artifactGrants'; grants: ArtifactGrant[] }\n /**\n * Set artifact param values. A PARTIAL patch — only the named params change, and\n * each value is validated against its declared spec before it applies (unknown or\n * invalid entries are dropped). Explicit and deliberate, so it always applies, even\n * to a param whose page-context binding the viewer has overridden.\n */\n | { type: 'host.artifactParams'; params: ArtifactParams }\n /**\n * The annotated site map — the app's routes with the host's content\n * classification. Sent on mount and re-sent on navigation (a virtualized SPA can\n * change what's routable). Distinct from the per-page `host.context`: this is the\n * whole app's structure, so the agent can reason about and navigate toward MF\n * content the user isn't currently looking at.\n */\n | { type: 'host.sitemap'; sitemap: SiteMapEntry[] }\n | { type: 'host.tools'; tools: HostTool[] }\n | {\n type: 'host.toolResult';\n callId: string;\n ok: boolean;\n result?: unknown;\n error?: string;\n }\n | { type: 'host.auth'; token: string; expiresAt: number }\n | { type: 'host.theme'; mode: 'light' | 'dark' }\n /**\n * Where the widget currently IS. Sent on mount and after anything that moves it\n * (drag, snap, mode change, viewport resize).\n *\n * The widget cannot work this out for itself — it can't see the viewport, and the\n * loader owns the box. Without it the widget renders its launcher against a stale\n * guess: a launcher dragged to the left edge kept drawing its circle bottom-RIGHT\n * inside a left-anchored box, so the circle jumped as soon as the box grew for the\n * hover pill. `growth` is the direction the pill/menu/panel must open (toward the\n * viewport centre); `mode` decides whether the launcher is a circle or an edge tab.\n */\n | {\n type: 'host.geometry';\n mode: 'float' | 'dock';\n dockSide: 'left' | 'right';\n growth: 'tl' | 'tr' | 'bl' | 'br';\n };\n\n/* ──────────────────────────── widget → host ───────────────────────────── */\n\n/**\n * The agent asking the host to do something.\n *\n * THE SECURITY MODEL, stated once: the page's content is UNTRUSTED. Text on the\n * page can try to instruct the agent, and the moment the agent can act, that stops\n * being a bad answer and becomes a bad ACTION. So:\n *\n * - v1 is READ-ONLY. `snapshot` and `activity` flow; no tool mutates the page.\n * - A tool that is not `readOnly` must be confirmed by the USER, every call. Not\n * \"unless the host opts out\" — every call. The trigger may have been written by\n * the page.\n * - The host is the enforcement point, not the widget. The widget can be lied to;\n * the loader runs in the page and decides what actually happens.\n */\nexport interface ToolCall {\n callId: string;\n name: string;\n args: Record<string, unknown>;\n}\n\nexport type WidgetToHost =\n | { type: 'widget.ready'; protocol: number }\n /**\n * Ask for the FULL page snapshot. This is the agent PULLING detail, not us pushing\n * it: the focus context rides on every turn, but the whole tree is fetched only\n * when the agent decides it needs to look — cheaper, and it reads what's relevant\n * to the question instead of drowning in the footer nav every turn.\n */\n | { type: 'widget.requestSnapshot' }\n /**\n * Pull the DECLARED page context fresh, right now. The host answers with `host.context`.\n *\n * Declared context (`window.matterfact.context`, or a `getPageContext()` callback) is\n * otherwise only published on load and on URL navigation, so in a virtualized SPA it goes\n * stale on any non-navigation state change (a team switch, a filter edit, a selection).\n * The widget pulls it at turn-assembly time — symmetric with `widget.requestSnapshot` —\n * so every turn carries the current context, not the context as of the last route change.\n */\n | { type: 'widget.requestContext' }\n /**\n * Admin max page-context mode from bootstrap — host clamps observation to this.\n */\n | { type: 'widget.pageContextMax'; mode: 'full' | 'declared' | 'off' }\n /** Zoom in: the a11y sub-tree under one ref (a table, a form, a card the agent cares about). */\n | { type: 'widget.readRegion'; ref: ElementRef }\n | { type: 'widget.callTool'; call: ToolCall }\n /** Chat opened/closed — the loader resizes the iframe and starts/stops observing. */\n | { type: 'widget.setOpen'; open: boolean }\n | { type: 'widget.resize'; height: number }\n /**\n * Dock / undock the open panel.\n *\n * Docked is a FULL-HEIGHT SIDE PANEL on the launcher's own side — left corners dock\n * to the left edge, right corners to the right, unless `setDockSide` overrides it.\n * This replaces the old `setExpanded`, which was hardcoded to the right edge and so\n * threw the panel across the screen whenever the launcher sat on the left.\n *\n * The loader owns the box, so only it can do this — `widget.resize` (height-only)\n * can neither widen the panel nor move it to an edge.\n */\n | { type: 'widget.setMode'; mode: 'float' | 'dock' }\n /** Which edge the tab and its panel live on. */\n | { type: 'widget.setDockSide'; side: 'left' | 'right' }\n /** Place the FLOATING launcher at a specific corner (the menu's snap picker). */\n | { type: 'widget.snapCorner'; corner: 'tl' | 'tr' | 'bl' | 'br' }\n /**\n * Resize the COLLAPSED launcher's host box so the widget can paint outside the 56px\n * circle — the hover pill (`{w:180,h:56}`) or the context menu (its own box). The\n * iframe clips to the host box, so anything beside the circle is invisible until the\n * host makes room. `{w:56,h:56}` returns to the bare circle. Ignored while open.\n */\n | { type: 'widget.setLauncherRegion'; w: number; h: number }\n /**\n * User resize of the floating panel, via the grip on its inner corner.\n *\n * Same contract as the launcher drag below, and for the same reason: the press is\n * captured by the iframe, so the host cannot see the moves. The widget tracks the\n * gesture and sends SCREEN-coordinate deltas; the loader owns the sizing, the\n * per-corner sign of the delta, and the clamp.\n */\n | { type: 'widget.resizeStart' }\n | { type: 'widget.resizeMove'; dx: number; dy: number }\n | { type: 'widget.resizeEnd' }\n /**\n * Launcher drag. The widget tracks the gesture; the host only moves the element.\n *\n * It has to be this way round, and the reason is easy to get wrong: a press that starts\n * inside an iframe is implicitly captured by that iframe, so every pointermove and the\n * pointerup belong to the widget's document until the button comes up. The host cannot\n * see them at all — not even through an overlay of its own, because capture beats hit\n * testing. So the host cannot take over a drag mid-gesture; it can only be told.\n *\n * `dx`/`dy` are deltas in SCREEN coordinates, which is the other half of the trick:\n * client coordinates are measured against the iframe's own viewport, and the iframe is\n * being moved under the cursor as the drag proceeds — so the pointer would appear to\n * stop moving and the launcher would stall after one step. Screen coordinates are\n * absolute and immune to that feedback loop.\n */\n | { type: 'widget.dragStart' }\n | { type: 'widget.dragMove'; dx: number; dy: number }\n | { type: 'widget.dragEnd' }\n /** Put the launcher back in the default corner and forget the saved size/dock. */\n | { type: 'widget.resetPos' }\n /** Get out of the way until the page reloads (a menu action, not a setting). */\n | { type: 'widget.hide' }\n /** The widget has no session; the host must run the hosted-login popup. */\n | { type: 'widget.needsAuth' }\n /**\n * Deeplink the host to one of its OWN routes — the \"open on page\" control on a\n * co-embedded document's side-panel viewer. The widget is a cross-origin iframe and\n * can't navigate the top window itself, so it asks the loader, which resolves the\n * href against the host location and refuses anything off the host's own origin.\n */\n | { type: 'widget.navigate'; href: string }\n /**\n * A chat-turn lifecycle moment, forwarded by the loader to the host's `onEvent`.\n * Content-free: `phase` + an opaque `chatId`, never message text. Additive.\n */\n | {\n type: 'widget.chat';\n phase: 'message' | 'response-start' | 'response-end';\n chatId?: string;\n }\n /**\n * An artifact param changed. Emitted ONLY for user- and context-originated changes,\n * never for a change the host itself pushed. The type excludes `'host'` so a caller\n * cannot mislabel an echo as legitimate; the actual no-echo guarantee comes from the\n * param store filtering by `source` before this callback is invoked.\n */\n | {\n type: 'widget.artifactParams';\n params: ArtifactParams;\n source: 'user' | 'context';\n };\n\n/* ───────────────────────────── envelope ───────────────────────────────── */\n\n/**\n * Everything on the wire is wrapped.\n *\n * `channel` keeps us out of the host page's own postMessage traffic (and out of\n * every other widget's). The receiver ALSO checks `event.origin` against an\n * allowlist and `event.source` against the expected window, on every single\n * message — a namespace is not a security boundary, it is a politeness.\n */\nexport interface Envelope<T> {\n channel: typeof CHANNEL;\n protocol: number;\n /** Correlates a response to its request; absent for notifications. */\n id?: string;\n payload: T;\n}\n\nexport function envelope<T>(payload: T, id?: string): Envelope<T> {\n return {\n channel: CHANNEL,\n protocol: PROTOCOL_VERSION,\n ...(id ? { id } : {}),\n payload,\n };\n}\n\nexport function isEnvelope(data: unknown): data is Envelope<unknown> {\n return (\n typeof data === 'object' &&\n data !== null &&\n (data as { channel?: unknown }).channel === CHANNEL\n );\n}\n","/**\n * Pure geometry for the embed launcher/panel.\n *\n * No DOM, no React — every function here is a total map from numbers/strings to\n * numbers/strings. That is deliberate: the loader attaches a CLOSED shadow root, so\n * the iframe inside it is unreachable from a test. Keeping the whole positioning\n * model in pure functions is what makes it verifiable at all; `loader.ts` is then a\n * thin applier that sets the returned box on the light-DOM host element.\n *\n * There are two MODES, and the mode decides both how the widget rests and how it opens:\n *\n * dock (default) — the launcher is a TAB flush against a side edge, and opening\n * gives a full-height side panel on that edge. This is the shape\n * most hosts want: it reads as part of the app, not as a bubble\n * floating over it.\n * float — the launcher is a CIRCLE the user can put anywhere, and opening\n * gives a panel anchored near it.\n *\n * In float the position is FREE. A corner is only recorded when the user drops the\n * launcher near one (see `snapOrFree`), because a snap that always fires isn't a\n * convenience, it's a constraint — you could never place the thing mid-edge. Recording\n * the corner when it IS snapped is what lets it stay in that corner across a viewport\n * resize, where a raw x/y would drift.\n */\n\nexport type Corner = 'tl' | 'tr' | 'bl' | 'br';\nexport type DockSide = 'left' | 'right';\nexport type Mode = 'float' | 'dock';\n\nexport interface EmbedGeometry {\n mode: Mode;\n /** float: the corner it snapped to, or null when freely placed. */\n corner: Corner | null;\n /** float: free launcher top-left, used only when `corner` is null. */\n x: number;\n y: number;\n floatW: number;\n floatH: number;\n dockSide: DockSide;\n /** dock: the tab's top offset along its edge. */\n tabY: number;\n dockW: number;\n}\n\n/**\n * A set of fixed-position box properties to apply to the host `<div>`.\n * Exactly two of top/right/bottom/left carry a px value; the other two are 'auto',\n * so the box stays pinned to the edges it grows away from.\n */\nexport interface BoxStyle {\n top: string;\n right: string;\n bottom: string;\n left: string;\n width: string;\n height: string;\n}\n\nexport const MARGIN = 20;\nexport const LAUNCHER = 56;\nexport const PILL_W = 180;\n/** How close to a corner a drop must land before it snaps. Generous enough to feel\n * magnetic, small enough that the middle of an edge is still reachable. */\nexport const SNAP_RADIUS = 96;\n/**\n * The docked launcher tab.\n *\n * Wide enough to carry the logo mark, and clearly TALLER than it is wide so the shape\n * still reads as a tab on the page edge. The ratio is the thing that matters: at a\n * near-square size any inner radius rounds it into a lozenge, which is what \"squished\n * circle\" looks like.\n */\nexport const TAB_W = 44;\nexport const TAB_H = 132;\n/**\n * How close to a SIDE edge a drop must land to become a docked tab. Dragging the tab\n * away from the edge is therefore how you float it, and dragging the circle back to an\n * edge is how you dock it — the placement follows where you put the thing, so there is\n * no mode switch to go hunting for.\n */\nexport const EDGE_DOCK = 64;\nexport const DEFAULT_FLOAT_W = 420;\nexport const DEFAULT_FLOAT_H = 640;\nexport const MIN_FLOAT_W = 320;\nexport const MIN_FLOAT_H = 420;\nexport const DEFAULT_DOCK_W = 600;\nexport const MIN_DOCK_W = 320;\n\nconst CORNERS: readonly Corner[] = ['tl', 'tr', 'bl', 'br'];\n\nexport function defaultGeometry(): EmbedGeometry {\n return {\n // Dock is the default: a side panel reads as part of the host app.\n mode: 'dock',\n corner: 'br',\n x: 0,\n y: 0,\n floatW: DEFAULT_FLOAT_W,\n floatH: DEFAULT_FLOAT_H,\n dockSide: 'right',\n tabY: 0, // 0 => centre it (see tabTop)\n dockW: DEFAULT_DOCK_W,\n };\n}\n\nfunction clamp(n: number, lo: number, hi: number): number {\n return Math.min(Math.max(n, lo), hi);\n}\n\n/** Where a snapped corner puts the launcher's top-left. */\nfunction cornerOrigin(\n corner: Corner,\n w: number,\n h: number,\n vw: number,\n vh: number,\n): { x: number; y: number } {\n return {\n x: corner[1] === 'l' ? MARGIN : vw - MARGIN - w,\n y: corner[0] === 't' ? MARGIN : vh - MARGIN - h,\n };\n}\n\n/** The launcher's top-left in float mode: its snapped corner, else its free spot. */\nexport function launcherOrigin(\n g: EmbedGeometry,\n vw: number,\n vh: number,\n): { x: number; y: number } {\n if (g.corner) return cornerOrigin(g.corner, LAUNCHER, LAUNCHER, vw, vh);\n return {\n x: clamp(g.x, MARGIN, Math.max(MARGIN, vw - MARGIN - LAUNCHER)),\n y: clamp(g.y, MARGIN, Math.max(MARGIN, vh - MARGIN - LAUNCHER)),\n };\n}\n\n/**\n * Which way things grow from the launcher: toward the viewport centre.\n *\n * Distinct from `geometry.corner` on purpose — a FREELY placed launcher has no corner,\n * but its pill, menu and panel still have to open inward or they'd run off the page.\n */\nexport function growthCorner(g: EmbedGeometry, vw: number, vh: number): Corner {\n if (g.corner) return g.corner;\n const o = launcherOrigin(g, vw, vh);\n return nearestCorner(o.x + LAUNCHER / 2, o.y + LAUNCHER / 2, vw, vh);\n}\n\n/** Which quadrant a point is in. */\nexport function nearestCorner(\n cx: number,\n cy: number,\n vw: number,\n vh: number,\n): Corner {\n return `${cy < vh / 2 ? 't' : 'b'}${cx < vw / 2 ? 'l' : 'r'}` as Corner;\n}\n\n/**\n * Resolve a drop: snap to a corner only if it landed NEAR one, else keep it where the\n * user put it (clamped on-screen). Returns the fields to merge into the geometry.\n */\nexport function snapOrFree(\n x: number,\n y: number,\n vw: number,\n vh: number,\n): { corner: Corner | null; x: number; y: number } {\n const cx = clamp(x, MARGIN, Math.max(MARGIN, vw - MARGIN - LAUNCHER));\n const cy = clamp(y, MARGIN, Math.max(MARGIN, vh - MARGIN - LAUNCHER));\n for (const corner of CORNERS) {\n const o = cornerOrigin(corner, LAUNCHER, LAUNCHER, vw, vh);\n if (Math.hypot(cx - o.x, cy - o.y) <= SNAP_RADIUS) {\n return { corner, x: cx, y: cy };\n }\n }\n return { corner: null, x: cx, y: cy };\n}\n\n/** Is this box close enough to a side edge to be docked? The live half of the drag. */\nexport function edgeDock(x: number, w: number, vw: number): DockSide | null {\n if (x <= EDGE_DOCK) return 'left';\n if (x + w >= vw - EDGE_DOCK) return 'right';\n return null;\n}\n\n/**\n * The point a drag MOVES — the widget's logical anchor, not its painted box.\n *\n * They differ once the panel is open: the box is the whole panel, but what the geometry\n * stores (and what a corner drop resolves) is the launcher's own origin. Dragging by the\n * box's top-left would shift the widget by the panel's size the moment you grabbed it.\n */\nexport function dragAnchor(\n g: EmbedGeometry,\n vw: number,\n vh: number,\n): { x: number; y: number } {\n if (g.mode === 'dock') {\n return { x: g.dockSide === 'left' ? 0 : vw - TAB_W, y: tabTop(g, vh) };\n }\n return launcherOrigin(g, vw, vh);\n}\n\n/**\n * The widget's painted horizontal extent for the state it is in.\n *\n * Derived, not measured: `getBoundingClientRect` is unavailable before layout (and is\n * all-zeros under jsdom), and the loader already knows every input. This is what the\n * edge test reads — \"dragged to the side\" is about the box you can see touching the\n * edge, which for an open panel is nowhere near its anchor.\n */\nexport function visibleBox(\n g: EmbedGeometry,\n open: boolean,\n vw: number,\n vh: number,\n): { x: number; w: number } {\n if (g.mode === 'dock') {\n const w = open\n ? Math.min(Math.max(g.dockW, MIN_DOCK_W), Math.max(0, vw - 2 * MARGIN))\n : TAB_W;\n return { x: g.dockSide === 'left' ? 0 : vw - w, w };\n }\n const o = launcherOrigin(g, vw, vh);\n if (!open) return { x: o.x, w: LAUNCHER };\n const { w } = clampSize(g.floatW, g.floatH, vw, vh, MIN_FLOAT_W, MIN_FLOAT_H);\n // A right-growing panel hangs LEFT off the launcher, so its left edge is the\n // launcher's right edge minus the panel width.\n return {\n x: growthCorner(g, vw, vh)[1] === 'r' ? o.x + LAUNCHER - w : o.x,\n w,\n };\n}\n\n/**\n * Resolve a drop into a PLACEMENT — which mode the widget lands in, and where.\n *\n * Two different measurements, deliberately:\n * - the EDGE test uses the widget's visible box, because \"dragged to the side\" is\n * about what you can see touching the edge. Testing the anchor instead means a\n * 420px-wide panel whose right edge is already against the screen refuses to dock,\n * since its anchor is still 400px away.\n * - the CORNER test uses the anchor, because that is what a corner placement stores.\n *\n * Priority matters, and corner beats edge on purpose: every corner is also within reach\n * of a side edge, so checking the edge first would make corner-snapping unreachable and\n * quietly turn the floating circle into a tab whenever you aimed for a corner.\n */\nexport function placeDrop(\n anchorX: number,\n anchorY: number,\n boxLeft: number,\n boxW: number,\n vw: number,\n vh: number,\n):\n | { mode: 'float'; corner: Corner | null; x: number; y: number }\n | { mode: 'dock'; dockSide: DockSide; tabY: number } {\n const snapped = snapOrFree(anchorX, anchorY, vw, vh);\n if (snapped.corner) return { mode: 'float', ...snapped };\n const side = edgeDock(boxLeft, boxW, vw);\n if (side) {\n return {\n mode: 'dock',\n dockSide: side,\n tabY: clamp(anchorY, 0, Math.max(0, vh - TAB_H)),\n };\n }\n return { mode: 'float', ...snapped };\n}\n\nexport function dockSideForCorner(corner: Corner): DockSide {\n return corner[1] === 'l' ? 'left' : 'right';\n}\n\nexport function effectiveDockSide(g: EmbedGeometry): DockSide {\n return g.dockSide;\n}\n\n/**\n * Fit a size inside the viewport.\n *\n * Order matters: the minimum is applied FIRST and the viewport cap LAST, so on a\n * viewport smaller than the minimum the VIEWPORT wins. That is the whole point — a\n * panel that honoured its minimum past the viewport edge would hang off the page.\n */\nexport function clampSize(\n w: number,\n h: number,\n vw: number,\n vh: number,\n minW: number,\n minH: number,\n): { w: number; h: number } {\n return {\n w: Math.min(Math.max(w, minW), Math.max(0, vw - 2 * MARGIN)),\n h: Math.min(Math.max(h, minH), Math.max(0, vh - 2 * MARGIN)),\n };\n}\n\n/**\n * Apply a resize drag to the float size. The grip sits on the panel's INNER corner\n * (the one facing viewport centre), so dragging it away from the anchored corner\n * grows the panel — which flips the sign of the delta per corner. Unclamped.\n */\nexport function applyResizeDelta(\n corner: Corner,\n startW: number,\n startH: number,\n dx: number,\n dy: number,\n): { w: number; h: number } {\n const wSign = corner[1] === 'r' ? -1 : 1; // right-anchored grows on -dx\n const hSign = corner[0] === 'b' ? -1 : 1; // bottom-anchored grows on -dy\n return { w: startW + wSign * dx, h: startH + hSign * dy };\n}\n\n/** Widen/narrow a docked panel by dragging its inner edge. */\nexport function applyDockResizeDelta(\n side: DockSide,\n startW: number,\n dx: number,\n): number {\n return side === 'right' ? startW - dx : startW + dx;\n}\n\nexport function parseGeometry(raw: string | null): EmbedGeometry | null {\n if (!raw) return null;\n try {\n const p = JSON.parse(raw) as Partial<EmbedGeometry>;\n // Pre-mode values (and the even older {right,bottom}) are dropped rather than\n // guessed at — there is no honest mapping from them onto this model.\n if (!p || (p.mode !== 'float' && p.mode !== 'dock')) return null;\n const d = defaultGeometry();\n const num = (v: unknown, dv: number) => (typeof v === 'number' ? v : dv);\n return {\n mode: p.mode,\n corner: CORNERS.includes(p.corner as Corner)\n ? (p.corner as Corner)\n : null,\n x: num(p.x, d.x),\n y: num(p.y, d.y),\n floatW: num(p.floatW, d.floatW),\n floatH: num(p.floatH, d.floatH),\n dockSide: p.dockSide === 'left' ? 'left' : 'right',\n tabY: num(p.tabY, d.tabY),\n dockW: num(p.dockW, d.dockW),\n };\n } catch {\n return null;\n }\n}\n\nexport function serializeGeometry(g: EmbedGeometry): string {\n return JSON.stringify(g);\n}\n\n/* ─────────────────────────── box builders ─────────────────────────────── */\n\nconst px = (n: number) => `${n}px`;\n\n/**\n * Pin the box by the two edges nearest the growth corner, so it grows inward.\n * Works for a snapped launcher and a freely-placed one alike: the anchor is always\n * the launcher's own top-left plus its size, expressed from whichever edges it grows\n * away from.\n */\nfunction boxAt(\n ox: number,\n oy: number,\n w: number,\n h: number,\n corner: Corner,\n vw: number,\n vh: number,\n): BoxStyle {\n const right = corner[1] === 'r';\n const bottom = corner[0] === 'b';\n // The anchored edges are measured from the LAUNCHER's own far side, so the launcher\n // stays put while the box grows past it...\n const rawX = right ? vw - (ox + LAUNCHER) : ox;\n const rawY = bottom ? vh - (oy + LAUNCHER) : oy;\n // ...but only as far as the viewport allows. Clamping the SIZE is not enough: a\n // launcher parked mid-page opens a 640px panel that grows straight off the top, and\n // the panel's header — the only way to close, dock or drag it — goes with it. So the\n // pinned offset is clamped too, which shifts the panel back into view while keeping\n // it as close to the launcher as it can be.\n const x = clamp(rawX, MARGIN, Math.max(MARGIN, vw - MARGIN - w));\n const y = clamp(rawY, MARGIN, Math.max(MARGIN, vh - MARGIN - h));\n return {\n top: bottom ? 'auto' : px(y),\n bottom: bottom ? px(y) : 'auto',\n left: right ? 'auto' : px(x),\n right: right ? px(x) : 'auto',\n width: px(w),\n height: px(h),\n };\n}\n\n/** The tab's top offset; a stored 0 means \"centre it\". */\nfunction tabTop(g: EmbedGeometry, vh: number): number {\n const max = Math.max(0, vh - TAB_H);\n return clamp(g.tabY || Math.round((vh - TAB_H) / 2), 0, max);\n}\n\n/** The docked launcher: a slim tab flush against its edge (no margin — it hugs). */\nexport function tabBox(g: EmbedGeometry, vw: number, vh: number): BoxStyle {\n const left = g.dockSide === 'left';\n return {\n top: px(tabTop(g, vh)),\n bottom: 'auto',\n left: left ? '0px' : 'auto',\n right: left ? 'auto' : '0px',\n width: px(TAB_W),\n height: px(TAB_H),\n };\n}\n\n/**\n * ANY float-mode box — the circle, the hover pill, the menu region, the open panel.\n *\n * They are all the same shape: a box of size w×h anchored at the launcher and growing\n * inward. One function rather than four wrappers, because this module is bundled into\n * the per-page loader stub and four near-identical bodies is pure weight.\n */\nexport function floatChrome(\n g: EmbedGeometry,\n w: number,\n h: number,\n vw: number,\n vh: number,\n /**\n * Override the growth direction. Used to FREEZE it for the length of a drag: growth\n * is derived from which half of the viewport the launcher is in, so dragging an open\n * panel across the middle would otherwise flip the side it grows from and jump the\n * panel by its own width. Held still, it tracks the cursor.\n */\n growth?: Corner,\n): BoxStyle {\n const o = launcherOrigin(g, vw, vh);\n return boxAt(o.x, o.y, w, h, growth ?? growthCorner(g, vw, vh), vw, vh);\n}\n\n/** The region the docked tab grows into for its context menu — hugging its edge. */\nexport function dockChrome(\n g: EmbedGeometry,\n w: number,\n h: number,\n vw: number,\n vh: number,\n): BoxStyle {\n const left = g.dockSide === 'left';\n return {\n top: px(tabTop(g, vh)),\n bottom: 'auto',\n left: left ? '0px' : 'auto',\n right: left ? 'auto' : '0px',\n width: px(w),\n height: px(h),\n };\n}\n\n/** The collapsed launcher for the current mode: dock → tab, float → circle. */\nexport function launcherBox(\n g: EmbedGeometry,\n vw: number,\n vh: number,\n): BoxStyle {\n return g.mode === 'dock'\n ? tabBox(g, vw, vh)\n : floatChrome(g, LAUNCHER, LAUNCHER, vw, vh);\n}\n\n/** The floating open panel, anchored at the launcher and clamped on-screen. */\nexport function floatBox(\n g: EmbedGeometry,\n vw: number,\n vh: number,\n growth?: Corner,\n): BoxStyle {\n const f = clampSize(g.floatW, g.floatH, vw, vh, MIN_FLOAT_W, MIN_FLOAT_H);\n return floatChrome(g, f.w, f.h, vw, vh, growth);\n}\n\n/** Full-height side panel flush against its edge. */\nexport function dockBox(g: EmbedGeometry, vw: number, _vh: number): BoxStyle {\n const w = Math.min(\n Math.max(g.dockW, MIN_DOCK_W),\n Math.max(0, vw - 2 * MARGIN),\n );\n const left = g.dockSide === 'left';\n return {\n top: '0px',\n bottom: '0px',\n height: 'auto',\n left: left ? '0px' : 'auto',\n right: left ? 'auto' : '0px',\n width: px(w),\n };\n}\n","import {\n CHANNEL,\n PROTOCOL_VERSION,\n envelope,\n isEnvelope,\n type ArtifactParams,\n type HostToWidget,\n type MatterfactEvent,\n type WidgetToHost,\n} from './protocol';\n// Type-only: erased at build, so it does NOT pull the lazy ./context chunk eagerly.\nimport type { PageContextProvider } from './context';\n\ndeclare global {\n interface Window {\n /**\n * The loader's public host API. Declared here so the members this file\n * installs are checked on both the read and the write; hosts also see a\n * typed surface instead of an opaque bag.\n */\n matterfact?: {\n setArtifactParams?: (params: ArtifactParams) => void;\n getPageContext?: unknown;\n [key: string]: unknown;\n };\n }\n}\nimport {\n applyDockResizeDelta,\n applyResizeDelta,\n clampSize,\n defaultGeometry,\n dockBox,\n floatBox,\n growthCorner,\n tabBox,\n dockChrome,\n floatChrome,\n parseGeometry,\n serializeGeometry,\n dragAnchor,\n visibleBox,\n edgeDock,\n placeDrop,\n LAUNCHER,\n MARGIN,\n MIN_DOCK_W,\n MIN_FLOAT_H,\n MIN_FLOAT_W,\n DEFAULT_DOCK_W,\n type BoxStyle,\n type Corner,\n type EmbedGeometry,\n} from './geometry';\n\n/**\n * The matterfact embed loader.\n *\n * <script type=\"module\" src=\"https://app.matterfact.com/embed/embed.js\"\n * data-key=\"pk_live_acme_...\"\n * data-origin=\"https://app.matterfact.com\"></script>\n *\n * This file runs on EVERY page load of a customer's site, so it is deliberately\n * tiny and deliberately dumb:\n *\n * - zero dependencies\n * - it does NOT read the DOM. The snapshot/activity code is a separate chunk,\n * fetched only when the user actually opens the chat. A customer should pay\n * nothing for a widget nobody clicked.\n * - it does NOT bundle React, or a CSS framework, or an RPC library.\n *\n * `iframe-resizer` is the thing you will be tempted to reach for here. It is\n * GPL-3.0 and cannot ship in this file. The ResizeObserver bridge below is the\n * fifteen lines it would have cost you anyway.\n */\n\n/** Returns a token this matterfact deployment's auth provider trusts (Firebase idToken,\n * Entra token, …). May be async; return null when there's no signed-in user. */\nexport type AuthTokenProvider = () => string | null | Promise<string | null>;\n\nexport interface LoaderConfig {\n publishableKey: string;\n /** Origin of the widget. Overridable for dev/self-hosted; defaults to production. */\n origin: string;\n theme: 'light' | 'dark' | 'auto';\n /** Optional label identifying THIS embedding, when one app embeds the agent in more\n * than one place (e.g. \"orders\" vs \"support\"). Stamped on runs/threads so usage and\n * history can be told apart per surface. Free-form; the app_id (from the session)\n * is the trusted half. */\n surface: string;\n /**\n * OPTIONAL host-auth passthrough, for TRUSTED first-party embeds (the admin portal,\n * EMC). When set, the host page already holds a token this deployment's auth provider\n * trusts (its Firebase idToken, an Entra token, …); the loader hands it to the widget\n * on `widget.needsAuth` instead of opening a sign-in popup. The backend still verifies\n * the token, so identity can't be forged — this only skips a redundant second sign-in.\n * Called on demand (so tokens can refresh). Absent ⇒ the popup/inline sign-in flow.\n * The `<script>` loader has no config here; it reads window.matterfact.getEmbedAuthToken\n * instead (see provideAuth).\n */\n authTokenProvider?: AuthTokenProvider;\n /**\n * OPTIONAL pull callback for DECLARED page context, for a host whose context changes\n * without a URL navigation (a virtualized SPA: team switch, filter edit, selection). The\n * widget calls it at turn-assembly time (via `widget.requestContext`), so every turn\n * carries current context instead of context as of the last route change. Symmetric with\n * `authTokenProvider`. The `<script>` loader has no config here; it reads\n * `window.matterfact.getPageContext` instead (see resolveDeclaredContext in context.ts).\n * Absent ⇒ falls back to the static `window.matterfact.context`.\n */\n pageContextProvider?: PageContextProvider;\n /**\n * Render INTO this element instead of floating in the corner — for a host that has its\n * own side panel, tool drawer or tab and wants the agent to live there.\n *\n * This inverts who owns the chrome. In corner mode the widget owns everything: it draws\n * its own launcher and it tells us how big to be (setOpen/setDocked/resize). Inside\n * someone else's panel, both are theirs — a widget that resized its host's drawer, or\n * drew a second floating bubble inside it, is just broken. So here we fill the\n * container, ignore the size messages, and the widget skips the launcher and renders\n * the chat directly.\n *\n * The host also owns visibility: showing and hiding their own panel IS the open/close\n * control, so there is nothing for us to add.\n */\n container?: HTMLElement | null;\n /**\n * Page observation mode: `full` (default / `true`), `declared` (getPageContext +\n * host sitemap only), or `off` (`false`). Auto DOM/focus/activity require `full`.\n */\n pageContext?: boolean | 'full' | 'declared' | 'off';\n /**\n * Force dev mode (see `devRequested()` below) without needing `?mfdev=1` on the host\n * URL. This is the programmatic equivalent of that trigger, not a replacement for it —\n * the URL param still works untouched; `mount()` ORs the two together.\n */\n dev?: boolean;\n}\n\nconst DEFAULT_ORIGIN = 'https://app.matterfact.com';\n\n/**\n * Has someone asked to see what the widget is doing? `?mfdev=1` on the HOST page, or\n * `localStorage.mfdev = '1'` on the host's own origin.\n *\n * Driven by the host's own URL rather than a `data-` attribute on purpose: an engineer\n * debugging a page they didn't write can add a query param, but they cannot redeploy the\n * customer's site to change a script tag.\n *\n * The localStorage twin exists because the URL trigger assumes the host LEAVES the query\n * string alone, and plenty of app shells don't — a client-side router that rewrites the\n * URL on boot drops `?mfdev=1` before we ever read it, which is unfixable from our side\n * and leaves the panel unreachable on exactly the deployments most in need of it. From\n * the host page's console:\n *\n * localStorage.mfdev = '1'; location.reload(); // on (survives navigation)\n * delete localStorage.mfdev; location.reload(); // off\n *\n * Read at mount, so it needs that reload to take effect. It trades the URL trigger's\n * ephemerality for surviving a rewrite — deliberately, and it is per-origin,\n * per-browser, and cleared by the same devtools pane that set it.\n *\n * This only surfaces what the widget already collects FROM THE PAGE IT'S ON, to the\n * person whose page it is. It grants no access: the session, the origin allowlist and\n * the route scope are all enforced server-side and none of them consult this.\n */\nfunction devRequested(): boolean {\n try {\n if (new URLSearchParams(location.search).get('mfdev') === '1') return true;\n // Separate try: Safari/incognito and blocked third-party storage THROW on access,\n // and that must not take the URL trigger down with it.\n try {\n return localStorage.getItem('mfdev') === '1';\n } catch {\n return false;\n }\n } catch {\n return false;\n }\n}\n\n/**\n * Host-auth diagnostics, printed only when dev mode is on (`devRequested`).\n *\n * Gated rather than always-on because \"no host-auth provider\" is a perfectly normal\n * configuration for a third-party site that uses the widget's own sign-in — warning\n * about it on every page load would be noise on the majority of installs. It is only\n * interesting when someone is asking why a FIRST-PARTY passthrough didn't happen, and\n * that someone can turn it on from the console. Shares the `[embed auth]` prefix with\n * the widget-side logs so one filter shows the whole flow across both frames.\n *\n * Messages are terse ON PURPOSE — every byte here is paid by every customer page (see\n * the stub budget in build.mjs). The words that explain them live at the call sites.\n */\nfunction alog(m: string, x?: unknown) {\n if (devRequested()) console.info('[embed auth] host: ' + m, x ?? '');\n}\n\nfunction readConfig(): LoaderConfig | null {\n // `document.currentScript` is null inside a module or after async execution, so\n // fall back to finding our own tag by src.\n const el =\n (document.currentScript as HTMLScriptElement | null) ??\n document.querySelector<HTMLScriptElement>('script[data-key][src*=\"embed\"]');\n\n const publishableKey = el?.dataset.key;\n if (!publishableKey) {\n console.error('[matterfact] missing data-key on the embed script tag');\n return null;\n }\n // `data-container=\"#panel\"` renders into the host's own element instead of the\n // corner. A selector rather than an element, because a script tag can't hand us a\n // reference — and if it doesn't resolve we fall back to the corner rather than\n // vanish: a widget nobody can find looks identical to a broken install.\n const sel = el?.dataset.container;\n const container = sel ? document.querySelector<HTMLElement>(sel) : null;\n if (sel && !container) {\n console.error(\n `[matterfact] data-container=\"${sel}\" matched nothing; falling back to the corner`,\n );\n }\n\n // `data-page-context`: full|declared|off (aliases: true/on/1 → full; false/off/0 → off).\n // Absent → full. Declared keeps getPageContext/host sitemap; skips auto DOM/focus.\n const pageContextAttr = el?.dataset.pageContext?.toLowerCase();\n let pageContext: LoaderConfig['pageContext'] = true;\n if (\n pageContextAttr === 'off' ||\n pageContextAttr === 'false' ||\n pageContextAttr === '0'\n ) {\n pageContext = false;\n } else if (pageContextAttr === 'declared') {\n pageContext = 'declared';\n } else if (\n pageContextAttr === 'full' ||\n pageContextAttr === 'on' ||\n pageContextAttr === 'true' ||\n pageContextAttr === '1'\n ) {\n pageContext = true;\n }\n\n return {\n publishableKey,\n origin: el?.dataset.origin || DEFAULT_ORIGIN,\n theme: (el?.dataset.theme as LoaderConfig['theme']) || 'auto',\n surface: el?.dataset.surface || '',\n container,\n pageContext,\n // No `data-dev` — see the `dev` field's doc comment: the URL trigger is the\n // point for the script-tag path, so there is deliberately no script-tag knob here.\n // No `data-actions` either: the action policy lives entirely in the lazy chunk,\n // read straight off `window.matterfact.hoist.actions` by hoist-runtime.ts's\n // readActionsConfig() — see LoaderConfig's class doc and MatterfactAgentProps.actions\n // (react.tsx) for the one remaining way to SET that global programmatically.\n };\n}\n\nconst POS_KEY = 'mf.embed.pos';\n\nclass EmbedHost {\n private iframe: HTMLIFrameElement | null = null;\n private shadow: ShadowRoot | null = null;\n /** Buffered until the widget says it's listening — postMessage before load is dropped silently. */\n private queue: HostToWidget[] = [];\n private ready = false;\n private open = false;\n /** The persisted box: launcher corner, float size, dock state/side/width. */\n private geo: EmbedGeometry = defaultGeometry();\n /** Float size at resizeStart; resizeMove deltas apply against it (they're cumulative). */\n private resizeBase: { w: number; h: number } | null = null;\n /** Docked width at resizeStart — the docked panel resizes in one axis only. */\n private dockBase = DEFAULT_DOCK_W;\n /** The widget's logical anchor when the drag began, and where it is now. Deltas arrive\n * cumulative from the press, so the current spot is simply origin + delta — no need to\n * read the box back, which stops being the thing we're moving the moment it docks. */\n private dragFrom: { x: number; y: number } | null = null;\n private dragAt = { x: 0, y: 0 };\n /** The painted box when the drag began — the edge test measures what you can SEE\n * touching the screen edge, which is not the anchor once the panel is open. */\n private dragBox = { x: 0, w: 0 };\n /** Growth direction frozen for the drag — see floatChrome's `growth` param. */\n private dragGrowth: Corner | null = null;\n /**\n * Has this drag been clear of the dock band yet?\n *\n * The resting position is INSIDE the band — a corner-anchored widget sits MARGIN\n * (20px) from the edge, well within EDGE_DOCK (64px) — so without this, picking one\n * up docked it instantly, before it had moved anywhere. Docking now requires\n * ENTERING the band rather than merely starting in it.\n */\n private dragLeftBand = false;\n /** Loaded on first open. Holds everything that touches the customer's DOM. */\n private context: Promise<typeof import('./context')> | null = null;\n /** The host element; kept so `destroy()` can remove it (React lifecycle). */\n private hostEl: HTMLDivElement | null = null;\n /** Rendering into the host's own element: they own the box, the chrome and visibility. */\n private readonly inline: boolean;\n /** Host-auth storm guard (see provideAuth). Counts getAuthToken calls; past a cap we stop\n * calling the provider, rather than joining the storm. This host instance OUTLIVES iframe\n * reloads, so — unlike anything in the widget — the counter survives the very reload loop\n * that drives the storm. (Kept tiny on purpose: this file has a hard per-page size budget;\n * the widget side carries the windowed/retry-after-settle half.) Terse name = fewer bytes\n * in the stub; it is the host-auth request count. */\n private ac = 0;\n\n constructor(private config: LoaderConfig) {\n this.inline = !!config.container;\n }\n\n mount() {\n // A CLOSED shadow root, on a container that resets every inherited property.\n //\n // We are a guest on someone else's page. Their CSS must not reach into us, our\n // CSS must not leak out, and their JS should not be able to walk into our tree\n // by accident (closed mode: `host.shadowRoot` is null). `contain` stops our\n // layout/paint from invalidating theirs.\n const host = document.createElement('div');\n this.hostEl = host;\n host.id = 'matterfact-embed';\n\n if (this.inline) {\n // The container's box IS the widget's box. No fixed positioning, no z-index\n // scramble, no drag: where this sits is the host's layout problem, which is the\n // entire point of asking for it.\n host.style.cssText = [\n 'all: initial',\n 'position: relative',\n 'display: block',\n 'width: 100%',\n 'height: 100%',\n 'contain: layout style',\n ].join(';');\n this.config.container!.appendChild(host);\n } else {\n this.geo = this.readGeometry();\n host.style.cssText = [\n 'all: initial',\n 'position: fixed',\n // Below the max so a host that genuinely needs to cover us (a modal, a cookie\n // banner they are legally obliged to show) still can.\n 'z-index: 2147483000',\n 'contain: layout style',\n // The host element owns the BOX now (position AND size); the iframe fills it.\n // Transitioning the box is what makes the corner snap and the open/dock read as\n // motion rather than a jump. Suppressed during drag/resize (see those handlers).\n 'transition: width .18s ease, height .18s ease, top .18s ease,' +\n ' right .18s ease, bottom .18s ease, left .18s ease',\n ].join(';');\n document.body.appendChild(host);\n }\n\n this.shadow = host.attachShadow({ mode: 'closed' });\n\n const style = document.createElement('style');\n // Shared prefix, then only what differs. This ships on every page load and the stub\n // has ~no headroom, so the two modes must not each carry a whole stylesheet.\n //\n // Inline fills the host's box and adds nothing: no radius, no shadow. Their panel\n // already has whatever chrome they chose; ours would sit inside it looking like a\n // mistake.\n // The iframe simply FILLS the host element, in both modes. The host owns every\n // dimension, so all the geometry lives in one place (and stays observable in the\n // light DOM — this shadow root is closed, so nothing in here can be inspected).\n // Only the corner treatment differs, switched off the host's data-mode: a circle\n // while it's the launcher, a card once it's a pill, menu or panel.\n //\n // Hairline ring + soft ambient: the widget must read as a crisp shape on ANY host\n // background, including pure white where a shadow alone bleeds.\n // Only the OPEN panel is a surface of ours. While collapsed the widget draws its\n // own shapes — the tab, the circle, the menu — onto the host's page, so the iframe\n // must not paint a card behind them: the host box grows to make room for the menu,\n // and a background there shows up as a slab hanging off the launcher.\n style.textContent =\n ':host{all:initial}' +\n 'iframe{border:0;display:block;width:100%;height:100%;background:transparent}' +\n (this.inline\n ? ''\n : // color-scheme only on the OPEN panel — on a collapsed launcher it makes the\n // UA paint an opaque canvas base behind our shapes.\n ':host([data-mode=\"float\"]) iframe,:host([data-mode=\"dock\"]) iframe' +\n '{color-scheme:light dark}' +\n ':host([data-mode=\"float\"]) iframe{border-radius:12px;' +\n 'box-shadow:0 0 0 1px #00000014,0 8px 40px #00000029}' +\n ':host([data-mode=\"dock\"]) iframe{box-shadow:0 0 0 1px #00000014,' +\n '0 8px 40px #00000029}' +\n ':host([data-mode=\"dock\"][data-flush=\"right\"]) iframe' +\n '{border-radius:12px 0 0 12px}' +\n ':host([data-mode=\"dock\"][data-flush=\"left\"]) iframe' +\n '{border-radius:0 12px 12px 0}');\n this.shadow.appendChild(style);\n\n const iframe = document.createElement('iframe');\n iframe.title = 'matterfact assistant';\n //\n // `allow-same-origin` is REQUIRED, and leaving it out is the trap.\n //\n // Without it the framed document gets an OPAQUE origin: its postMessage arrives\n // as `event.origin === \"null\"`, so our own origin check rejects the handshake and\n // the bridge silently never connects. It also denies the widget any storage at\n // all — no session, no auth, ever.\n //\n // The familiar warning — \"allow-scripts + allow-same-origin lets the frame escape\n // its sandbox\" — is about a frame that is SAME-ORIGIN WITH THE FRAMER, which can\n // then reach up and delete its own sandbox attribute. Ours is cross-origin to the\n // host by construction (that is the entire security model), so it cannot touch\n // this element. What the sandbox still buys us is real: no top-level navigation,\n // no downloads, no pointer lock.\n // `allow-downloads` lets the chat save a file in place. Every download path\n // is an in-place `<a download>`.click(); WITHOUT this flag a sandboxed frame\n // blocks that and the browser falls back to opening the file in a new tab.\n // It is a sandbox flag, not a permissions-policy `allow` token.\n iframe.setAttribute(\n 'sandbox',\n 'allow-scripts allow-same-origin allow-forms allow-popups allow-popups-to-escape-sandbox allow-downloads',\n );\n // Powerful features need an explicit permissions-policy grant: a cross-origin\n // frame is granted NONE by default, so without `allow` they are hard-blocked\n // no matter what the user approves.\n // - microphone: dictation's getUserMedia.\n // - clipboard-write: the \"Copy\" button on a chat message calls\n // navigator.clipboard.writeText, which throws NotAllowedError without it.\n // The bare tokens scope each grant to the iframe's own origin (the app), not\n // the host page. A host page can still deny either via its own\n // Permissions-Policy response header — outside our control, which is why the\n // in-app copy path also degrades gracefully rather than throwing.\n iframe.setAttribute('allow', 'microphone; clipboard-write');\n iframe.src =\n `${this.config.origin}/embed/chat?k=${encodeURIComponent(\n this.config.publishableKey,\n )}&o=${encodeURIComponent(location.origin)}` +\n (this.config.surface\n ? `&s=${encodeURIComponent(this.config.surface)}`\n : '') +\n // Either trigger works: the URL param (no redeploy needed) OR the config's\n // programmatic `dev` (the React prop). See both doc comments above.\n (devRequested() || this.config.dev ? '&dev=1' : '') +\n (this.inline ? '&inline=1' : '');\n this.iframe = iframe;\n this.shadow.appendChild(iframe);\n\n window.addEventListener('message', this.onMessage);\n if (!this.inline) {\n // A viewport change can strand a saved box off-page (a laptop undocked from an\n // external monitor is the common one), so re-clamp and re-anchor on resize.\n window.addEventListener('resize', this.onViewportResize);\n this.place();\n }\n }\n\n /**\n * Every message is checked twice, on every single message — not once at setup.\n *\n * `channel` is a namespace, not a boundary. The origin and source checks are the\n * boundary: any frame on the page can postMessage us, and a page with an ad iframe\n * on it has plenty of frames.\n */\n private onMessage = (event: MessageEvent) => {\n if (event.origin !== this.config.origin) return;\n if (event.source !== this.iframe?.contentWindow) return;\n if (!isEnvelope(event.data)) return;\n\n this.handle(event.data.payload as WidgetToHost);\n };\n\n /**\n * Test seam. The shadow root is CLOSED, so a test cannot reach the iframe to forge a\n * source-valid MessageEvent — this routes a message through the identical logic\n * without weakening the origin/source checks above, which stay the only real door.\n */\n __testHandle(msg: WidgetToHost) {\n this.handle(msg);\n }\n\n /**\n * The eager stub's own emit-and-swallow for the host's `onEvent` telemetry hook.\n * Deliberately does NOT import the registry's `emitEvent`: pulling the lazy ./context\n * chunk (where the registry lives) into the size-budgeted stub would blow it, so this\n * ~5-line duplication across the bundle boundary is intentional. Host telemetry is\n * host code — a throw here must never break the widget.\n */\n private emit(e: MatterfactEvent) {\n const cb = (\n globalThis as { matterfact?: { onEvent?: (e: MatterfactEvent) => void } }\n ).matterfact?.onEvent;\n if (typeof cb !== 'function') return;\n try {\n cb(e);\n } catch {\n /* host telemetry must never break the widget */\n }\n }\n\n private handle(msg: WidgetToHost) {\n switch (msg.type) {\n case 'widget.ready':\n this.ready = true;\n this.send({\n type: 'host.ready',\n protocol: PROTOCOL_VERSION,\n origin: location.origin,\n });\n // Theme NOW, not on first open. It used to ride in with the lazy context\n // chunk, which is only fetched when the chat is opened — so a collapsed\n // launcher sat in the wrong colours until you interacted with it.\n this.send({ type: 'host.theme', mode: this.themeMode() });\n this.flush();\n // Inline has no \"open\" event to hang this off — the chat is visible the moment\n // the host renders their panel, so the eye has to load now or the agent is\n // blind to the page for the whole session. The corner keeps its lazy load: a\n // page whose visitors never click still pays nothing.\n if (this.inline) void this.loadContext();\n this.emit({ type: 'ready' });\n break;\n\n case 'widget.setOpen':\n // Host telemetry sees the open/close moment regardless of placement (the inline\n // guard below only skips the popup SIZING, which is meaningless inline).\n this.emit({ type: msg.open ? 'open' : 'close' });\n // Inline: the host's panel IS the open/close control. There's no popup to size.\n if (this.inline) break;\n this.open = msg.open;\n this.place();\n // The DOM code is fetched HERE — the first time someone actually opens the\n // chat — and never on a page where they don't.\n if (msg.open) void this.loadContext();\n break;\n\n case 'widget.setMode':\n // Inline: the host owns the box; float/dock is not ours to choose.\n if (this.inline) break;\n this.geo.mode = msg.mode;\n this.writeGeometry(this.geo);\n this.place();\n break;\n\n case 'widget.setDockSide':\n if (this.inline) break;\n this.geo.dockSide = msg.side;\n this.writeGeometry(this.geo);\n this.place();\n break;\n\n case 'widget.snapCorner':\n if (this.inline) break;\n // Snapping is a FLOAT affordance; the picker only shows in float mode.\n this.geo.corner = msg.corner;\n this.writeGeometry(this.geo);\n this.place();\n break;\n\n case 'widget.setLauncherRegion': {\n // Only meaningful while collapsed — open, the panel is already bigger than any\n // pill or menu, and growing the box would fight the panel's own size.\n if (this.inline || this.open) break;\n const vw = window.innerWidth;\n const vh = window.innerHeight;\n if (msg.w <= LAUNCHER && msg.h <= LAUNCHER) {\n // Back to the bare launcher — place() already knows how to draw it.\n this.place();\n } else {\n const region =\n this.geo.mode === 'dock'\n ? dockChrome(this.geo, msg.w, msg.h, vw, vh)\n : floatChrome(this.geo, msg.w, msg.h, vw, vh);\n this.applyBox(region, msg.h > LAUNCHER ? 'menu' : 'pill');\n }\n break;\n }\n\n case 'widget.resize':\n // Inline: the container's height is the host's business, and an auto-grow here\n // would fight their layout every time the transcript got longer.\n if (this.inline) break;\n // Content height auto-grow, for the FLOATING panel only. Docked, the edges own\n // the height; and a manual resize has already written floatH, which place()\n // would restore on the next call anyway.\n if (this.hostEl && this.open && this.geo.mode === 'float') {\n this.hostEl.style.height = `${Math.min(\n msg.height,\n Math.max(0, window.innerHeight - 2 * MARGIN),\n )}px`;\n }\n break;\n\n case 'widget.resizeStart':\n if (this.inline) break;\n this.resizeBase = { w: this.geo.floatW, h: this.geo.floatH };\n this.dockBase = this.geo.dockW;\n // The box must track the pointer exactly; an eased transition lags behind it.\n if (this.hostEl) this.hostEl.style.transition = 'none';\n break;\n\n case 'widget.resizeMove': {\n if (this.inline || !this.resizeBase) break;\n if (this.geo.mode === 'dock') {\n // Docked, only the WIDTH is the user's — the edges own the height.\n const w = applyDockResizeDelta(\n this.geo.dockSide,\n this.dockBase,\n msg.dx,\n );\n this.geo.dockW = Math.min(\n Math.max(w, MIN_DOCK_W),\n Math.max(0, window.innerWidth - 2 * MARGIN),\n );\n } else {\n const next = applyResizeDelta(\n growthCorner(this.geo, window.innerWidth, window.innerHeight),\n this.resizeBase.w,\n this.resizeBase.h,\n msg.dx,\n msg.dy,\n );\n const fit = clampSize(\n next.w,\n next.h,\n window.innerWidth,\n window.innerHeight,\n MIN_FLOAT_W,\n MIN_FLOAT_H,\n );\n this.geo.floatW = fit.w;\n this.geo.floatH = fit.h;\n }\n this.place();\n break;\n }\n\n case 'widget.resizeEnd':\n if (this.inline) break;\n this.resizeBase = null;\n if (this.hostEl) this.hostEl.style.transition = '';\n this.writeGeometry(this.geo);\n break;\n\n case 'widget.requestContext':\n // The pull path for declared context — re-read it now (via getPageContext if the\n // host supplied one) and re-publish `host.context`. No-op if pageContext is off.\n void this.loadContext().then((m) => m.provideContext());\n break;\n\n case 'widget.pageContextMax':\n // Admin max from bootstrap — clamp host observation without remounting.\n void this.loadContext().then((m) => {\n m.setPageContextMax(msg.mode);\n });\n break;\n\n case 'widget.requestSnapshot':\n // pageContext: false ⇒ `sendSnapshot` itself no-ops (see context.ts) rather\n // than gating here — this file is the size-budgeted stub, and the check\n // costs nothing extra in the lazy chunk that already owns the DOM read.\n void this.loadContext().then((m) => m.sendSnapshot(this.send));\n break;\n\n case 'widget.readRegion':\n void this.loadContext().then((m) => m.sendRegion(msg.ref, this.send));\n break;\n\n case 'widget.callTool':\n void this.loadContext().then((m) => m.callTool(msg.call, this.send));\n break;\n\n // Everything below is LAUNCHER chrome: there is no launcher inline (the widget\n // doesn't draw one), and moving or hiding the host's own panel from inside it\n // would be us redecorating their app. An older cached loader could still be told\n // any of these by a newer widget, so they're guarded rather than assumed absent.\n case 'widget.dragStart':\n if (this.inline) break;\n this.dragFrom = dragAnchor(\n this.geo,\n window.innerWidth,\n window.innerHeight,\n );\n this.dragAt = { ...this.dragFrom };\n this.dragLeftBand = false;\n this.dragGrowth = growthCorner(\n this.geo,\n window.innerWidth,\n window.innerHeight,\n );\n this.dragBox = visibleBox(\n this.geo,\n this.open,\n window.innerWidth,\n window.innerHeight,\n );\n // Follow the pointer 1:1 while dragging; the ease is for the snap on release.\n if (this.hostEl) this.hostEl.style.transition = 'none';\n break;\n\n case 'widget.dragMove': {\n if (this.inline || !this.dragFrom) break;\n const vw = window.innerWidth;\n const vh = window.innerHeight;\n this.dragAt = {\n x: this.dragFrom.x + msg.dx,\n y: this.dragFrom.y + msg.dy,\n };\n // Switch placement DURING the drag, not on release: drag toward a side and it\n // becomes the tab under your cursor, drag away and it pops back to the circle.\n // Deliberately only the EDGE test here — running the full drop resolver would\n // also snap corners mid-gesture, yanking the widget away while you're still\n // holding it. Corners are resolved on release, where a jump reads as a snap.\n const side = edgeDock(this.dragBox.x + msg.dx, this.dragBox.w, vw);\n if (!side) this.dragLeftBand = true;\n // A tab is already docked, so it may stay docked without having left first;\n // anything else must have been clear of the band at some point in this drag.\n if (side && (this.dragLeftBand || this.geo.mode === 'dock')) {\n this.geo.mode = 'dock';\n this.geo.dockSide = side;\n this.geo.tabY = Math.max(0, this.dragAt.y);\n } else {\n this.geo.mode = 'float';\n this.geo.corner = null;\n this.geo.x = this.dragAt.x;\n this.geo.y = this.dragAt.y;\n }\n this.place();\n break;\n }\n\n case 'widget.dragEnd': {\n if (this.inline || !this.dragFrom) break;\n // The drop resolves the FULL placement — including the corner snap the live\n // pass deliberately skipped. Restoring the transition first is what makes that\n // final snap animate rather than teleport.\n const at = placeDrop(\n this.dragAt.x,\n this.dragAt.y,\n this.dragBox.x + (this.dragAt.x - this.dragFrom.x),\n this.dragBox.w,\n window.innerWidth,\n window.innerHeight,\n );\n this.geo.mode = at.mode;\n if (at.mode === 'dock') {\n this.geo.dockSide = at.dockSide;\n this.geo.tabY = at.tabY;\n } else {\n this.geo.corner = at.corner;\n this.geo.x = at.x;\n this.geo.y = at.y;\n }\n this.dragFrom = null;\n this.dragGrowth = null;\n if (this.hostEl) this.hostEl.style.transition = '';\n this.place();\n this.writeGeometry(this.geo);\n break;\n }\n\n case 'widget.resetPos':\n if (this.inline) break;\n this.geo = defaultGeometry();\n this.writeGeometry(this.geo);\n this.place();\n break;\n\n case 'widget.hide':\n // Inline: hiding is the host's panel to hide. Blanking their drawer from in\n // here would leave them with an empty box and no way to get it back.\n if (this.inline) break;\n if (this.hostEl) this.hostEl.style.display = 'none';\n break;\n\n case 'widget.needsAuth':\n this.emit({ type: 'auth', phase: 'required' });\n void this.provideAuth();\n break;\n\n case 'widget.navigate':\n this.emit({ type: 'navigate', href: msg.href });\n // Delegated to the lazy context chunk (kept out of the every-page stub —\n // the eager budget is tight). By the time a document deeplink is clicked the\n // chat is open, so context is already loaded.\n void this.loadContext().then((m) => m.navigateHost(msg.href));\n break;\n\n case 'widget.chat':\n // Content-free chat-turn telemetry (phase + opaque chatId), relayed straight\n // to the host's onEvent. Never carries message text.\n this.emit({ type: 'chat', phase: msg.phase, chatId: msg.chatId });\n break;\n\n case 'widget.artifactParams':\n // An artifact param changed in the widget (user edit or context binding),\n // relayed straight to the host's onEvent. params/source pass through unchanged.\n this.emit({\n type: 'artifactParams',\n params: msg.params,\n source: msg.source,\n });\n break;\n }\n }\n\n /**\n * Own a drag for its lifetime.\n *\n * The widget reports the press and then goes quiet: a cross-origin iframe only gets\n * pointer events while the pointer is over it, and a drag leaves that box immediately.\n * So we lay a transparent layer over the whole viewport and track the gesture in the\n * host document, where it can't be lost. The layer also stops the pointer landing on\n * the customer's own UI mid-drag (text selection, hover states, stray clicks).\n *\n * We are the dumb half on purpose — see `widget.dragMove` in the protocol for why the\n * widget has to own the gesture. All we do is take a delta and place the element.\n */\n /** Write a box onto the host element and stamp the mode the shadow CSS keys off. */\n private applyBox(\n box: BoxStyle,\n mode: 'launcher' | 'tab' | 'pill' | 'menu' | 'float' | 'dock',\n ) {\n if (!this.hostEl || this.inline) return;\n const s = this.hostEl.style;\n s.top = box.top;\n s.right = box.right;\n s.bottom = box.bottom;\n s.left = box.left;\n s.width = box.width;\n s.height = box.height;\n this.hostEl.dataset.mode = mode;\n // The flush shapes (tab, docked panel) round only on the side facing the page.\n if (mode === 'tab' || mode === 'dock')\n this.hostEl.dataset.flush = this.geo.dockSide;\n else delete this.hostEl.dataset.flush;\n }\n\n /** Re-apply the box for whatever state we're in: collapsed launcher, float, or dock. */\n /**\n * The growth direction to use RIGHT NOW — frozen while a drag is in flight.\n *\n * Every consumer has to agree on this, which is the bug that made the widget\n * flip-flop under small movements near the viewport centre: the box was frozen but\n * `host.geometry` still published a freshly-derived growth, so the WIDGET kept\n * swapping which corner it drew against (and it animates that), twitching back and\n * forth while the loader held perfectly still.\n */\n private growthNow(): Corner {\n return (\n this.dragGrowth ??\n growthCorner(this.geo, window.innerWidth, window.innerHeight)\n );\n }\n\n private place() {\n const vw = window.innerWidth;\n const vh = window.innerHeight;\n if (!this.open) {\n const dock = this.geo.mode === 'dock';\n this.applyBox(\n dock\n ? tabBox(this.geo, vw, vh)\n : floatChrome(this.geo, LAUNCHER, LAUNCHER, vw, vh, this.growthNow()),\n dock ? 'tab' : 'launcher',\n );\n } else if (this.geo.mode === 'dock') {\n this.applyBox(dockBox(this.geo, vw, vh), 'dock');\n } else {\n this.applyBox(floatBox(this.geo, vw, vh, this.growthNow()), 'float');\n }\n this.publishGeometry();\n }\n\n /**\n * Tell the widget where it is. It can't see the viewport and doesn't own the box, so\n * without this it draws its launcher against a stale guess — which is what made the\n * circle jump when the launcher sat on the left (see `host.geometry` in the protocol).\n */\n private publishGeometry() {\n this.send({\n type: 'host.geometry',\n mode: this.geo.mode,\n dockSide: this.geo.dockSide,\n growth: this.growthNow(),\n });\n }\n\n /** The theme to publish: an explicit data-theme wins, else the OS preference. */\n private themeMode(): 'light' | 'dark' {\n return this.config.theme === 'auto'\n ? matchMedia('(prefers-color-scheme: dark)').matches\n ? 'dark'\n : 'light'\n : this.config.theme;\n }\n\n private onViewportResize = () => {\n // Re-clamp and re-anchor: a saved box must never be left hanging off a smaller page.\n this.place();\n };\n\n private readGeometry(): EmbedGeometry {\n // First-party storage on the CUSTOMER's origin, so each site remembers its own spot.\n // Never throws: storage is unavailable in some embedding contexts, and a widget that\n // can't remember where it was put is fine — one that fails to mount is not.\n try {\n return parseGeometry(localStorage.getItem(POS_KEY)) ?? defaultGeometry();\n } catch {\n return defaultGeometry();\n }\n }\n\n private writeGeometry(g: EmbedGeometry) {\n try {\n localStorage.setItem(POS_KEY, serializeGeometry(g));\n } catch {\n /* storage denied — the box just won't survive the reload */\n }\n }\n\n /**\n * Answer `widget.needsAuth`. If a trusted first-party host has an auth-token provider,\n * call it and hand the token straight to the widget via `host.auth` — no popup. The\n * provider comes from EITHER the programmatic config (the React `<MatterfactAgent\n * getAuthToken>` prop) OR a global the host page sets for the `<script>` loader:\n *\n * window.matterfact = { getEmbedAuthToken: () => getIdToken(user) };\n *\n * Read fresh at call time (not at readConfig), so a global set after the loader booted\n * — e.g. once the host's auth is ready — is still picked up. `expiresAt: 0`: the widget\n * doesn't cache it, it exchanges the token for a rotating embed session anyway.\n *\n * No provider ⇒ this does nothing. The loader never opens a sign-in popup — the widget\n * owns interactive sign-in (inline Firebase/email, and Microsoft's own MSAL login popup\n * for Entra, then the PKCE token exchange), so the loader must not also fire its stale\n * `/embed/authorize?k=&o=` popup, which the deployed authorize page no longer accepts.\n */\n private async provideAuth() {\n const provider =\n this.config.authTokenProvider ??\n (globalThis as { matterfact?: { getEmbedAuthToken?: AuthTokenProvider } })\n .matterfact?.getEmbedAuthToken;\n if (!provider) {\n // The silent case, and the one that looks identical to every other failure from\n // the outside: a host that registers getEmbedAuthToken LATE (after its own login\n // resolves) isn't asked again — the widget requests host-auth once per session.\n // So \"the page is signed in but the widget shows a login form\" and \"this host\n // never wired host-auth\" are the same picture unless we say which one happened.\n alog('no provider');\n return; // the widget owns interactive sign-in; the loader never opens it\n }\n\n // Storm guard. A healthy host-auth flow asks ONCE — the widget exchanges the token for a\n // rotating session and never asks again. A flood of `widget.needsAuth` means the token\n // keeps being rejected (wrong audience/scope) and something is remounting the widget in a\n // loop; calling getAuthToken on every one hammers the host's identity provider (MSAL →\n // login.microsoftonline.com), which can get the app RATE-LIMITED. Past the cap we simply\n // stop calling the provider. Minimal by necessity (hard per-page size budget) and\n // silent to the USER — the widget-side guard is what surfaces the reason to them and\n // logs the rejection; `alog` only says the cap is why we stopped asking.\n if (++this.ac > 5) {\n alog('storm cap');\n return;\n }\n try {\n const token = await provider();\n if (token) this.send({ type: 'host.auth', token, expiresAt: 0 });\n // One emit either way — a null token is a failure to provide, same as a throw.\n this.emit({ type: 'auth', phase: token ? 'granted' : 'failed' });\n // 'empty' is the interesting one: the host HAS a provider and it resolved\n // nothing — its own login usually hasn't finished yet. Distinct from 'no\n // provider' (never wired, or wired after we asked) and from a throw.\n alog(token ? 'token' : 'empty');\n } catch (e) {\n this.emit({ type: 'auth', phase: 'failed' });\n alog('threw', e);\n /* no token this time; the widget falls back to its own inline / popup sign-in */\n }\n }\n\n private loadContext() {\n // One chunk, one fetch, cached. Everything that can see the customer's page\n // lives behind this boundary.\n this.context ??= import('./context').then((m) => {\n // The single place the eye actually starts: when pageContext is off, `start()`\n // skips installing every DOM/nav/focus/activity observer — not a filter over\n // what gets sent afterward, nothing is collected in the first place. Passed\n // through raw: `start`'s default parameter is what turns `undefined` (the\n // option omitted) into \"on\".\n m.start(\n this.send,\n this.config.origin,\n this.config.pageContext,\n this.config.pageContextProvider,\n );\n // start() emits the OS-preference theme. An explicit data-theme on the\n // script tag is the host's choice — resend it so it wins over auto-detect\n // (a light-branded site must be able to force a light widget on a dark-OS\n // visitor, and vice versa).\n if (this.config.theme !== 'auto') {\n this.send({ type: 'host.theme', mode: this.config.theme });\n }\n return m;\n });\n return this.context;\n }\n\n private send = (msg: HostToWidget) => {\n if (!this.ready) {\n this.queue.push(msg);\n return;\n }\n // targetOrigin is the widget's exact origin — never '*'. A '*' here would\n // broadcast the customer's page content to whatever happens to be listening.\n this.iframe?.contentWindow?.postMessage(envelope(msg), this.config.origin);\n };\n\n private flush() {\n const pending = this.queue;\n this.queue = [];\n for (const m of pending) this.send(m);\n }\n\n /**\n * Push artifact param values into the widget — a PARTIAL patch; only the named\n * params change. Buffered like every other host→widget message until the widget\n * says it's listening.\n *\n * Explicit and deliberate, so it applies even to a param whose page-context binding\n * the viewer has overridden (spec §7.3).\n */\n setArtifactParams(params: ArtifactParams): void {\n // Runtime check despite the type: this is a PUBLIC entry point reachable from\n // untyped host JS (`window.matterfact.setArtifactParams(...)`), where the\n // signature guarantees nothing.\n if (params == null || typeof params !== 'object' || Array.isArray(params)) {\n console.error(\n '[matterfact] setArtifactParams expects an object of param values',\n );\n return;\n }\n this.send({ type: 'host.artifactParams', params });\n }\n\n /** Tear down: stop listening and remove the host element. For the React wrapper's\n * unmount — the vanilla `<script>` loader lives for the page's lifetime and never\n * calls this. */\n destroy() {\n window.removeEventListener('message', this.onMessage);\n window.removeEventListener('resize', this.onViewportResize);\n this.hostEl?.remove();\n this.hostEl = null;\n this.iframe = null;\n this.shadow = null;\n this.ready = false;\n // context.ts's listeners/history-patch/ref-registry are module-global and\n // outlive this instance — stop() undoes exactly what start() installed. A no-op\n // if the eye was never loaded (chat never opened, so nothing was ever started).\n // This is the unmount-with-no-remount case; start()'s own stop()-first call\n // (see context.ts) is what protects a REMOUNT, which is why both exist.\n void this.context?.then((m) => m.stop());\n }\n}\n\n/**\n * Programmatic entry — mount the widget from a config object instead of the DOM\n * `<script data-*>`. This is what the React wrapper (`@matterfact/embed/react`) drives;\n * the vanilla loader below reads the script tag and calls this same class.\n */\nexport function mount(config: LoaderConfig): EmbedHost {\n const host = new EmbedHost(config);\n host.mount();\n // The <script> loader has no config object, so the host reaches this method the\n // same way it provides getPageContext — through window.matterfact. Declared on\n // Window (above) rather than reached through `window as unknown as {...}`: the\n // double assertion silenced the checker on BOTH the read and the write, so a\n // typo in either key was invisible, and it published the API as an untyped\n // `Record` to anyone reading this file for the host contract.\n window.matterfact = {\n ...(window.matterfact ?? {}),\n setArtifactParams: (params: ArtifactParams) =>\n host.setArtifactParams(params),\n };\n return host;\n}\n\nexport { EmbedHost, CHANNEL, readConfig };\n"],"mappings":"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","names":["w"]}
|
|
1
|
+
{"version":3,"sources":["../src/react.tsx","../src/protocol.ts","../src/geometry.ts","../src/loader.ts","../src/react-context.tsx"],"sourcesContent":["/**\n * `@matterfact/embed/react` — the React front door.\n *\n * A thin wrapper over the SAME `EmbedHost` the `<script>` loader uses: it drives the\n * widget from props and tears it down on unmount, so React hosts don't hand-write a\n * script tag, vendor the loader, or juggle globals. The widget itself is a\n * fixed-position floating bubble (a cross-origin iframe the host page can't style), so\n * the component renders nothing into the React tree.\n */\n'use client';\n\nimport { useCallback, useEffect, useRef } from 'react';\n\nimport { mount, type EmbedHost, type LoaderConfig } from './loader';\nimport { useMatterfactConfig } from './react-context';\n// The provider (and the host-auth broker it runs) lives in ./react-context, but is\n// re-exported here so a host gets the ENTIRE React surface — provider, components, and\n// their prop types — from the one `@matterfact/embed/react` entry.\nexport { MatterfactAuthProvider } from './react-context';\nexport type { MatterfactAuthProviderProps } from './react-context';\nimport type {\n DeclaredArtifact,\n MatterfactEvent,\n PageContext,\n SiteMapEntry,\n ToolEvent,\n} from './protocol';\n// Type-only, same rationale as loader.ts's identical import: the adapter's actual\n// code stays inside the lazy ./context chunk, this component only ever carries the\n// plain config shape.\nimport type { HoistActionsConfig } from './adapters/hoist';\nimport type { HostResolvers, HostToolDef } from './adapters/registry';\n// Re-exported for hosts: typing a `tools`/`resolve` prop needs these shapes, and the\n// `adapters/*` path is internal. (MatterfactEvent/ToolEvent ride `@matterfact/embed`.)\nexport type { HostResolvers, HostToolDef } from './adapters/registry';\n// Same rationale: the `actions` prop is typed `HoistActionsConfig`, so a host typing\n// that prop needs the shape, and the `adapters/*` path is internal.\nexport type { HoistActionsConfig } from './adapters/hoist';\n\nconst DEFAULT_ORIGIN = 'https://app.matterfact.com';\n\nexport interface MatterfactAgentProps {\n /**\n * Publishable key (`pk_…`) identifying this embed app. Public, origin-scoped.\n *\n * Optional here ONLY because it can instead come from an ancestor\n * `MatterfactAuthProvider` (`prop ?? context`) — one of the two must supply it,\n * or the component throws at mount.\n */\n publishableKey?: string;\n /**\n * Origin serving the widget (the matterfact app). Defaults to production.\n *\n * @deprecated Prefer setting this once on `<MatterfactAuthProvider>` and letting\n * every `<MatterfactAgent>`/`<MatterfactArtifact>` underneath inherit it. Still\n * fully supported standalone (no provider) — resolved as `prop ?? context ?? default`.\n */\n widgetOrigin?: string;\n /** Label for THIS embedding, for per-surface usage/history attribution. */\n surface?: string;\n /** Resolved as `prop ?? MatterfactAuthProvider's theme ?? 'auto'`. */\n theme?: 'light' | 'dark' | 'auto';\n /**\n * HOST-AUTH PASSTHROUGH (trusted first-party embeds only). Return a token this\n * matterfact deployment already trusts — the host app's Firebase idToken, an Entra\n * token, whatever its auth provider verifies — and the widget signs in SILENTLY with\n * it, no separate sign-in. Called on demand so the token can be fresh. Omit for the\n * standard popup/inline sign-in (third-party hosts).\n *\n * Resolved as `prop ?? MatterfactAuthProvider's getAuthToken`.\n */\n getAuthToken?: () => string | null | Promise<string | null>;\n /**\n * PULL-BASED PAGE CONTEXT. Return what this page is right now — its route, a one-line\n * description, the entities it's about — and the widget pulls it FRESH at the start of\n * every turn (symmetric with `getAuthToken`). Use this when your context changes without\n * a URL navigation — a virtualized SPA where a team switch, filter edit or selection\n * changes what the agent should know, but the path doesn't. Called on demand; may be\n * async. Omit to keep the static `window.matterfact.context` behaviour.\n */\n getPageContext?: () =>\n | PageContext\n | null\n | undefined\n | Promise<PageContext | null | undefined>;\n /**\n * Render the agent HERE — in your own side panel, drawer or tab — instead of as a\n * floating bubble in the corner.\n *\n * The component then renders a real element and the chat fills it, sized by your CSS.\n * No launcher is drawn and the widget never resizes itself: inside your panel, the box\n * and the chrome are yours. Showing and hiding that panel is the open/close control.\n */\n inline?: boolean;\n /** Only with `inline` — applied to the element the chat fills. */\n className?: string;\n /** Only with `inline`. Merged over the default 100%×100% fill. */\n style?: React.CSSProperties;\n /**\n * Turn off ALL host-page observation — no context, no focus, no snapshot, no\n * activity, no artifact grants; the loader never even installs the observers.\n * Default `true`. Pass `false` for a host that wants agent chat with nothing\n * about the page ever reaching it.\n */\n pageContext?: boolean | 'full' | 'declared' | 'off';\n /**\n * Force dev mode programmatically. The URL trigger (`?mfdev=1`, see `loader.ts`)\n * still works unchanged — this is the React-prop equivalent, and the two are\n * OR'd together.\n */\n dev?: boolean;\n /**\n * Turn on agent-driven host tools (today: `hoist.navigate`) at the given confirm\n * tier, e.g. `{ navigate: 'confirm' }`. Default is every action tool OFF; this is\n * opt-in only.\n *\n * This is just sugar over setting `window.matterfact.hoist.actions` yourself — the\n * LAZY chunk (adapters/hoist-runtime.ts's `readActionsConfig`) reads that global\n * directly at advertise/execute time, so there is nothing for the eager loader to\n * carry. See `writeActionsGlobal` below.\n */\n actions?: HoistActionsConfig;\n /**\n * The app's site map, annotated with each route's content class — so the agent knows\n * which routes carry matterfact content (a co-embedded artifact, a document/report) or\n * the host's own data, and can navigate toward them.\n *\n * Classification only: give the route pattern + label + a `content` tag; NEVER a\n * capability token (the concrete artifact/document on the current page arrives via page\n * context / the live iframe). Sugar over `window.matterfact.sitemap`, read by the loader\n * on load and on navigation.\n */\n sitemap?: SiteMapEntry[];\n /**\n * Declared artifact grants — make a board reachable from ANY route, even ones where\n * its `<MatterfactArtifact>` iframe isn't mounted. Replaces the pattern of mounting\n * hidden zero-size iframes purely to smuggle a read token into the DOM. The token\n * rides the grant channel, never message text. Sugar over `window.matterfact.artifacts`.\n */\n artifacts?: DeclaredArtifact[];\n /**\n * matterfact-defined resolver contracts the HOST implements. matterfact fixes the tool\n * name, schema and agent semantics (so an integration spends no instruction budget on\n * them); you supply the function.\n *\n * - `document({doctype, key})` → `{id, label, href?}` — resolve an off-page matterfact\n * document (dossier/report) to its id, e.g. via your own content-location lookup.\n * - `artifact({slug})` → `{id, owner, token, label}` — resolve an artifact to a read grant.\n *\n * The agent calls these when the site map shows the content lives on another route, and\n * renders the result as an `<MFRef>` chip. Sugar over `window.matterfact.resolve`.\n */\n resolve?: HostResolvers;\n /**\n * Host-defined tools (`app.*`): your own functions, offered to the agent WebMCP-style.\n * Each is `{name, description, inputSchema?, confirm?, handler}`; the name is normalised\n * into the `app.` namespace. A tool is only actually exposed if this app's server-side\n * policy allows it. Sugar over `window.matterfact.tools`.\n */\n tools?: HostToolDef[];\n /**\n * Host telemetry hook: fired on every host-tool advertise/call/result so you can pipe\n * calls into your own observability. Never blocks or breaks the call path — a throw here\n * is swallowed. Sugar over `window.matterfact.onToolEvent`.\n *\n * NOTE: this is now a SUBSET of `onEvent` (the tool events only), kept for back-compat.\n * Prefer `onEvent` for new integrations — it delivers these same tool events (tagged\n * `type:'tool'`) alongside the widget's lifecycle and chat events.\n */\n onToolEvent?: (e: ToolEvent) => void;\n /**\n * UNIFIED host telemetry hook: fired on every embed lifecycle moment — `ready`,\n * `open`/`close`, `navigate`, `auth`, `error`, `chat`, and `tool` — so you can pipe the\n * whole widget into your own observability from one place. Never blocks or breaks the\n * widget; a throw here is swallowed. Sugar over `window.matterfact.onEvent`.\n */\n onEvent?: (e: MatterfactEvent) => void;\n}\n\n/**\n * Mirror the `actions` prop onto `window.matterfact.hoist.actions` — the one place\n * the lazy chunk actually reads it (see the `actions` prop's doc comment above).\n * Merged shallowly so this never clobbers `hoist.rows`/`excludeModels` (readHoistConfig)\n * or unrelated `window.matterfact.*` keys (`getEmbedAuthToken`, `context`, …).\n *\n * Written unconditionally — including `{navigate:'off'}` when the prop is absent —\n * so a host that stops passing `actions` (or never did) reliably gets the documented\n * default rather than whatever a PRIOR render (or a stray external script) left\n * behind. A no-op outside the browser.\n */\nfunction writeActionsGlobal(actions: HoistActionsConfig | undefined): void {\n if (typeof window === 'undefined') return;\n const w = window as { matterfact?: { hoist?: Record<string, unknown> } };\n const mf = (w.matterfact ??= {});\n mf.hoist = { ...mf.hoist, actions: actions ?? { navigate: 'off' } };\n}\n\n/**\n * Mirror the `sitemap` prop onto `window.matterfact.sitemap`, where the lazy context\n * chunk reads it (`readSitemap` in context.ts) on load and on navigation. Written only\n * when the prop is supplied, so a host that never sets a site map leaves the global\n * untouched (and unrelated `window.matterfact.*` keys alone). A no-op outside the browser.\n */\nfunction writeSitemapGlobal(sitemap: SiteMapEntry[] | undefined): void {\n if (typeof window === 'undefined' || sitemap === undefined) return;\n const w = window as { matterfact?: Record<string, unknown> };\n (w.matterfact ??= {}).sitemap = sitemap;\n}\n\n/**\n * Mirror the tool-surface props onto the globals the lazy context chunk reads\n * (`readDeclaredGrants`, and the registry's `resolve`/`tools`/`onToolEvent`/`onEvent`). Written\n * every render — including when a prop is absent, clearing a stale global — but ONLY\n * the values that change identity freely (functions) need care: they ride here, NOT the\n * mount effect's deps, so a host passing an inline `resolve={{document: …}}` or a new\n * `onToolEvent` closure each render never remounts the iframe. See the mount effect's\n * `toolsKey`, which keys remounts on the DESCRIPTORS only. A no-op outside the browser.\n */\nfunction writeToolGlobals(\n artifacts: DeclaredArtifact[] | undefined,\n resolve: HostResolvers | undefined,\n tools: HostToolDef[] | undefined,\n onToolEvent: ((e: ToolEvent) => void) | undefined,\n onEvent: ((e: MatterfactEvent) => void) | undefined,\n): void {\n if (typeof window === 'undefined') return;\n const mf = ((window as { matterfact?: Record<string, unknown> }).matterfact ??= {});\n mf.artifacts = artifacts ?? [];\n mf.resolve = resolve ?? {};\n mf.tools = tools ?? [];\n mf.onToolEvent = onToolEvent;\n mf.onEvent = onEvent;\n}\n\n/** The advertise-shaping fields of the tool props — everything that changes what the\n * agent is TOLD exists, and so must force a remount + re-advertise. Deliberately EXCLUDES\n * handler/callback identity (bodies ride the global, read fresh at call time), so an\n * inline handler literal doesn't remount the widget on every render. */\nfunction toolDescriptorKey(\n artifacts: DeclaredArtifact[] | undefined,\n resolve: HostResolvers | undefined,\n tools: HostToolDef[] | undefined,\n): string {\n return JSON.stringify({\n // Artifact identity is data (slug/owner/label), not a function.\n artifacts: (artifacts ?? []).map((a) => ({\n slug: a.slug,\n owner: a.owner,\n label: a.label,\n })),\n // For resolvers, only WHICH contracts are present changes the advertised set.\n resolve: {\n document: typeof resolve?.document === 'function',\n artifact: typeof resolve?.artifact === 'function',\n },\n // For host tools, the advertised descriptor — never the handler.\n tools: (tools ?? []).map((t) => ({\n name: t.name,\n description: t.description,\n inputSchema: t.inputSchema,\n confirm: t.confirm,\n })),\n });\n}\n\nexport function MatterfactAgent({\n publishableKey: publishableKeyProp,\n widgetOrigin: widgetOriginProp,\n surface = '',\n theme: themeProp,\n getAuthToken: getAuthTokenProp,\n getPageContext,\n inline = false,\n className,\n style,\n pageContext,\n dev,\n actions,\n sitemap,\n artifacts,\n resolve,\n tools,\n onToolEvent,\n onEvent,\n}: MatterfactAgentProps) {\n // Inherit from an ancestor MatterfactAuthProvider, prop wins when both are given.\n // Reading the hook unconditionally (before the throw below) keeps hook order\n // consistent across renders — see the doc comment on the throw itself.\n const ctx = useMatterfactConfig();\n const publishableKey = publishableKeyProp ?? ctx?.publishableKey;\n if (!publishableKey) {\n // Neither a prop nor a provider supplied it — there is no widget to mount.\n // Thrown (not a console.error + no-op render) so this fails loudly at the\n // integration's very first render, not as a silent missing bubble in prod.\n throw new Error(\n 'MatterfactAgent requires publishableKey (as a prop or via MatterfactAuthProvider)',\n );\n }\n const widgetOrigin = widgetOriginProp ?? ctx?.widgetOrigin ?? DEFAULT_ORIGIN;\n const getAuthToken = getAuthTokenProp ?? ctx?.getAuthToken;\n const theme = themeProp ?? ctx?.theme ?? 'auto';\n\n // The live callbacks ride refs so a new getAuthToken/getPageContext identity never\n // remounts the iframe — only the identity-defining config below does.\n const authRef = useRef(getAuthToken);\n authRef.current = getAuthToken;\n const pageContextRef = useRef(getPageContext);\n pageContextRef.current = getPageContext;\n // `actions` rides a ref + a VALUE key for the same reason, and it matters more\n // than it looks: the natural way to write this prop is an inline literal,\n // `actions={{ navigate: 'confirm' }}`, which is a brand-new object on every\n // render. Depending on its identity meant the widget tore itself down and\n // remounted on ANY re-render of the host page — losing the open chat, the\n // selected conversation, and any host-tool result still in flight (the run then\n // waits out its whole budget and reports that the page never confirmed).\n // Observed live: navigating re-rendered the host, which remounted the iframe.\n const actionsRef = useRef(actions);\n actionsRef.current = actions;\n const actionsKey = JSON.stringify(actions ?? null);\n const slot = useRef<HTMLDivElement>(null);\n\n // Publish the site map to its global whenever its CONTENT changes (an inline\n // `sitemap={[…]}` is a new array each render, so key on the serialization, not\n // identity — same trick as `actionsKey`). Declared BEFORE the mount effect so on\n // first mount it runs first and the global is set before start() reads it; a later\n // change re-writes the global without remounting (the loader re-reads it on nav).\n const sitemapKey = JSON.stringify(sitemap ?? null);\n useEffect(() => {\n writeSitemapGlobal(sitemap);\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [sitemapKey]);\n\n // The tool-surface globals are refreshed EVERY render (not keyed), because the\n // handler/callback bodies must always be the latest — the loader reads them fresh at\n // call time. Only the DESCRIPTORS (toolsKey below) gate a remount. `resolve`/`tools`/\n // `onToolEvent` are intentionally NOT mount-effect deps for exactly this reason.\n useEffect(() => {\n writeToolGlobals(artifacts, resolve, tools, onToolEvent, onEvent);\n });\n const toolsKey = toolDescriptorKey(artifacts, resolve, tools);\n\n useEffect(() => {\n if (typeof window === 'undefined') return;\n // Inline mounts into the div we rendered, so it has to exist first. It always does\n // by effect time; the guard is for the impossible case, not a race.\n if (inline && !slot.current) return;\n // Before mounting: publish `actions` to the global the lazy chunk actually reads\n // (see writeActionsGlobal's doc comment) so the remount below's start() call\n // advertises under the CURRENT prop value, not a stale one.\n writeActionsGlobal(actionsRef.current);\n // Same reason as writeActionsGlobal: publish the tool surface before mount so the\n // remount's start() advertises the CURRENT props, not a stale global.\n writeToolGlobals(artifacts, resolve, tools, onToolEvent, onEvent);\n const config: LoaderConfig = {\n publishableKey,\n // Already fully resolved above (prop ?? context ?? DEFAULT_ORIGIN).\n origin: widgetOrigin,\n theme,\n surface,\n // Always present; a null return (no getAuthToken supplied) makes the core fall\n // back to the popup/inline sign-in, so this is safe either way.\n authTokenProvider: async () => (await authRef.current?.()) ?? null,\n // Always present; a null return (no getPageContext supplied) makes the core fall\n // back to window.matterfact.getPageContext / window.matterfact.context.\n pageContextProvider: async () =>\n (await pageContextRef.current?.()) ?? null,\n container: inline ? slot.current : null,\n pageContext,\n dev,\n };\n let host: EmbedHost | null = null;\n try {\n host = mount(config);\n } catch (e) {\n console.error('[matterfact] failed to mount the embed widget', e);\n // Surface the failure to host telemetry (read fresh off the global, the same\n // channel writeToolGlobals feeds; a throw in the host's handler is swallowed).\n try {\n (\n window as { matterfact?: { onEvent?: (ev: MatterfactEvent) => void } }\n ).matterfact?.onEvent?.({\n type: 'error',\n message: e instanceof Error ? e.message : String(e),\n });\n } catch {\n /* host telemetry must never mask the original mount failure */\n }\n }\n return () => host?.destroy();\n // A genuine CHANGE to `actions` still has to remount, so the fresh start()\n // re-advertises under the new policy — hence actionsKey (the serialized\n // value) rather than `actions` (the object identity). See the ref above.\n }, [\n publishableKey,\n widgetOrigin,\n surface,\n theme,\n inline,\n pageContext,\n dev,\n actionsKey,\n // A change to the advertised tool SHAPE remounts so start() re-advertises under it;\n // a change to only a handler body or callback identity does not (see toolDescriptorKey).\n toolsKey,\n // eslint-disable-next-line react-hooks/exhaustive-deps\n ]);\n\n // Corner mode renders nothing into the tree — it's a fixed-position iframe and where\n // you put the component is irrelevant. Inline is the opposite, and that's the point:\n // the agent appears exactly where you place this, sized by your own CSS, so it can sit\n // in a side panel or a tab like any other component you own.\n if (!inline) return null;\n return (\n <div\n ref={slot}\n className={className}\n style={{ width: '100%', height: '100%', ...style }}\n />\n );\n}\n\nconst DEFAULT_ARTIFACT_ORIGIN = 'https://app.matterfact.com';\n\nexport interface MatterfactArtifactProps {\n /** The artifact's name (its slug). */\n name?: string;\n /**\n * @deprecated Use `name` instead. Still accepted as an alias — `name` wins\n * when both are given.\n */\n slug?: string;\n /** The artifact owner's email — same as the Share dialog's `?owner=` param. */\n owner: string;\n /**\n * The artifact's read-only capability token — same as the Share dialog's `?t=`\n * param. This is the standalone share-link path.\n *\n * @deprecated Prefer an ancestor `MatterfactAuthProvider` instead: with no\n * `token`, the iframe authenticates the same way `MatterfactAgent` does — the\n * provider's publishable key (`?k=`) plus the host page's origin (`?o=`) —\n * instead of a share-link capability token. `token` still wins when both a\n * token and a provider are present.\n */\n token?: string;\n /**\n * Origin serving the widget (the matterfact app). Defaults to production.\n *\n * @deprecated Prefer setting this once on `<MatterfactAuthProvider>` and\n * letting every `<MatterfactAgent>`/`<MatterfactArtifact>` underneath inherit\n * it. Still fully supported standalone (no provider) — resolved as\n * `prop ?? context ?? default`.\n */\n widgetOrigin?: string;\n /** Resolved as `prop ?? MatterfactAuthProvider's theme ?? 'auto'`. */\n theme?: 'auto' | 'light' | 'dark';\n /** Applied to the `<iframe>` element the artifact renders into. */\n className?: string;\n /** Applied to the `<iframe>` element. Merged over the default fill. */\n style?: React.CSSProperties;\n /**\n * Drive the artifact's declared params from your page — the company it's about, the\n * time window, the peer set. View-only: a param change re-renders the artifact from\n * data it already has, and never re-queries or runs the agent.\n *\n * Pass your whole param state; the component works out what actually changed and\n * pushes only that. That matters, because a push is authoritative for every key it\n * names: re-asserting a param the reader has since changed in the artifact's own\n * control bar would silently overwrite their choice. Dropping a key stops driving it\n * and leaves its current value alone — there is no \"unset\".\n *\n * NOTE: `MatterfactAgent`'s `setArtifactParams` steers artifacts rendered INSIDE the\n * chat. This prop steers THIS artifact. They are different iframes; neither reaches\n * the other.\n */\n params?: Record<string, unknown>;\n /**\n * Fired when a param changes inside the artifact — `'user'` when the reader used its\n * control bar, `'context'` when a bound param followed the host page's context.\n *\n * Never fires for your own `params` pushes, so feeding this straight back into the\n * `params` prop cannot loop.\n */\n onParamsChange?: (\n params: Record<string, unknown>,\n source: 'user' | 'context',\n ) => void;\n}\n\n/** Per-key JSON snapshot — the param vocabulary is JSON scalars and arrays of them, so\n * this compares exactly rather than approximately. */\nfunction snapshot(params: Record<string, unknown> | undefined): Map<string, string> {\n const out = new Map<string, string>();\n for (const [key, value] of Object.entries(params ?? {})) {\n out.set(key, JSON.stringify(value ?? null));\n }\n return out;\n}\n\n/**\n * Render a matterfact artifact inline in a host page. Chrome-free, cross-origin. When a\n * MatterfactAgent is embedded on the same page, the loader sees this iframe and both\n * makes the agent aware of it and (via its token) lets the agent read its data.\n *\n * Two auth modes, `token` wins when both are available:\n * - TOKEN mode (`token` given): a standalone share link, `?t=`. This is the LIVE\n * third-party embed path — its `src` format must stay byte-identical.\n * - SESSION mode (no `token`, an ancestor `MatterfactAuthProvider` is present):\n * tokenless, `?k=<publishableKey>&o=<host origin>`, the same authentication\n * `MatterfactAgent` uses.\n * With neither, there is no way for the iframe to authenticate, so no iframe is\n * built at all — matching the \"incomplete link\" placeholder the embed artifact\n * page itself renders for a missing owner/token.\n */\nexport function MatterfactArtifact({\n name: nameProp,\n slug,\n owner,\n token,\n widgetOrigin: widgetOriginProp,\n theme: themeProp,\n className,\n style,\n params,\n onParamsChange,\n}: MatterfactArtifactProps) {\n // Inherit from an ancestor MatterfactAuthProvider, same pattern as\n // MatterfactAgent above. Read unconditionally (before the throw below) to\n // keep hook order consistent across renders.\n const ctx = useMatterfactConfig();\n const artifactName = nameProp ?? slug;\n if (!artifactName) {\n // Thrown (not a silent no-op render) so a misconfigured integration fails\n // loudly at its very first render — same rationale as MatterfactAgent's\n // missing-publishableKey throw above.\n throw new Error(\n 'MatterfactArtifact requires name (or its deprecated alias, slug)',\n );\n }\n const origin = widgetOriginProp ?? ctx?.widgetOrigin ?? DEFAULT_ARTIFACT_ORIGIN;\n const theme = themeProp ?? ctx?.theme ?? 'auto';\n\n // All hooks live above the conditional placeholder return below — hook order\n // must not depend on which auth mode (or none) this render resolves to.\n const frame = useRef<HTMLIFrameElement>(null);\n // The live callback rides a ref so an inline arrow doesn't re-run the listener effect.\n const changeRef = useRef(onParamsChange);\n changeRef.current = onParamsChange;\n // Same reason, plus: the post helpers read the CURRENT params, and they're called from\n // an event handler (load) and a listener (ready) that outlive the render they closed over.\n const paramsRef = useRef(params);\n paramsRef.current = params;\n // What the artifact has already been told, per key. Drives the diff below.\n const posted = useRef<Map<string, string>>(new Map());\n\n const post = useCallback(\n (values: Record<string, unknown>) => {\n if (Object.keys(values).length === 0) return;\n // Targeted at the artifact's own origin rather than '*': we know exactly where we\n // mounted this iframe, so there's no reason to broadcast param values wider.\n frame.current?.contentWindow?.postMessage(\n { type: 'host.artifactParams', params: values },\n origin,\n );\n },\n [origin],\n );\n\n /** Everything, and reset the diff baseline — the frame is fresh and knows nothing. */\n const postAll = useCallback(() => {\n posted.current = snapshot(paramsRef.current);\n post({ ...paramsRef.current });\n }, [post]);\n\n // A fresh frame is told everything, twice over, because neither trigger is sufficient\n // alone and both are harmless (a patch is last-write-wins per key, so re-sending the\n // same values changes nothing):\n //\n // - `load` fires when the document finishes loading, which is NOT ordered against the\n // React hydration inside it that attaches the listener. DOM events aren't replayed\n // to a listener that attached late, so `load` alone can post into the void.\n // - `widget.artifactReady` is posted by the artifact page once its listener is up —\n // exact, but it only exists in newer app deployments. An embedder pins a package\n // version and points `widgetOrigin` at whatever is deployed, so relying on the ping\n // alone would make params silently never apply against an older app.\n const onLoad = useCallback(() => postAll(), [postAll]);\n\n useEffect(() => {\n if (typeof window === 'undefined') return;\n\n const onMessage = (event: MessageEvent) => {\n // We know precisely which window we mounted, so pin the sender to it — a sibling\n // iframe on the same page can't feed the host's callback or fake a ready ping.\n // (The artifact page itself can't make the symmetric check: a bare `<iframe src>`\n // route has no handshake giving it a trustworthy expected origin.)\n if (!frame.current || event.source !== frame.current.contentWindow) return;\n const data = event.data as\n | { type?: string; params?: Record<string, unknown>; source?: string }\n | null;\n if (!data || typeof data !== 'object') return;\n\n if (data.type === 'widget.artifactReady') {\n postAll();\n return;\n }\n if (data.type === 'widget.artifactParams' && data.params) {\n const source = data.source === 'context' ? 'context' : 'user';\n try {\n changeRef.current?.(data.params, source);\n } catch {\n /* host telemetry must never break the artifact */\n }\n }\n };\n\n window.addEventListener('message', onMessage);\n return () => window.removeEventListener('message', onMessage);\n }, [postAll]);\n\n // Push only what changed. A host naturally re-passes its whole param state on every\n // render, and posting that verbatim would re-assert every key — clobbering the\n // reader's control-bar choices whenever the host touched an UNRELATED param.\n // Keyed on the serialization, not the object identity, so an inline `params={{…}}`\n // literal doesn't post on every render.\n const paramsKey = JSON.stringify(params ?? null);\n const settled = useRef(false);\n useEffect(() => {\n const next = snapshot(paramsRef.current);\n // First run only records the baseline. Initial delivery is `load`/`ready`'s job —\n // posting here would aim at a frame that has loaded nothing yet.\n if (!settled.current) {\n settled.current = true;\n posted.current = next;\n return;\n }\n const changed: Record<string, unknown> = {};\n for (const [key, json] of next) {\n if (posted.current.get(key) !== json) {\n changed[key] = (paramsRef.current as Record<string, unknown>)[key];\n }\n }\n // A dropped key means \"stop driving this\", not \"unset it\" — nothing is posted, but\n // it leaves the baseline so re-adding the same value posts it again (which is how a\n // host deliberately forces a value back after the reader changed it).\n posted.current = next;\n post(changed);\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [paramsKey, post]);\n\n let src: string;\n if (token) {\n // TOKEN mode — byte-identical to the pre-context format. Never touch this\n // branch's string shape: it is the live third-party embed path.\n src =\n `${origin}/embed/artifacts/${encodeURIComponent(artifactName)}` +\n `?owner=${encodeURIComponent(owner)}&t=${encodeURIComponent(token)}&theme=${theme}`;\n } else if (ctx) {\n // SESSION mode — no share token, but a provider supplies a publishable key.\n // `o=` mirrors loader.ts's own `/embed/chat?k=&o=` construction. Guarded for\n // `window` because (unlike MatterfactAgent's mount(), which only ever runs\n // inside a client-only effect) this component builds its iframe `src`\n // synchronously in the render body, which can also run during SSR.\n const hostOrigin = typeof window === 'undefined' ? '' : window.location.origin;\n src =\n `${origin}/embed/artifacts/${encodeURIComponent(artifactName)}` +\n `?owner=${encodeURIComponent(owner)}&k=${encodeURIComponent(ctx.publishableKey)}` +\n `&o=${encodeURIComponent(hostOrigin)}&theme=${theme}`;\n } else {\n // Neither a token nor a provider — there is no way for the iframe to\n // authenticate. Don't build a URL that can never load; match the embed\n // artifact page's own guard for a missing owner/token\n // (EmbedArtifactPage.tsx's `EmbedMessage`) instead of inventing new copy.\n return (\n <div\n className={className}\n style={{\n width: '100%',\n height: 600,\n display: 'flex',\n alignItems: 'center',\n justifyContent: 'center',\n textAlign: 'center',\n ...style,\n }}\n >\n <p>This embedded artifact link is incomplete.</p>\n </div>\n );\n }\n\n return (\n <iframe\n ref={frame}\n src={src}\n onLoad={onLoad}\n title={`matterfact artifact ${artifactName}`}\n className={className}\n style={{ width: '100%', height: 600, border: 0, ...style }}\n // `allow-downloads` so an in-place `<a download>`.click() inside the\n // artifact (e.g. a deck's \"Export to PowerPoint\", a CSV/file export)\n // actually saves. Without it a sandboxed frame blocks the download and the\n // browser falls back to opening the file in a new tab. The sibling artifact\n // iframes (embed loader, co-embedded side panel) already carry this token;\n // this standalone embed component was the last one missing it.\n sandbox=\"allow-scripts allow-same-origin allow-forms allow-popups allow-popups-to-escape-sandbox allow-downloads\"\n />\n );\n}\n\nconst DEFAULT_DOC_ORIGIN = 'https://app.matterfact.com';\n\n/** The two live-drivable doc params, mirroring `EmbedDocumentPage`'s `HostDocParams`. */\nexport type MatterfactDocParams = { ticker?: string; version?: string };\n\n/**\n * Diff `params` against a per-key JSON snapshot, returning only the entries whose\n * value actually changed (or that were never sent), and recording those as sent.\n * `undefined` values are skipped — there's nothing to push for a key the caller\n * hasn't set. Deliberately not a closure: it captures nothing from a component, so\n * it's a plain function, not a nested one.\n *\n * Mirrors the per-key push discipline of the `<MatterfactArtifact params>` sender:\n * a push is authoritative for every key it names, so re-asserting an unchanged key\n * would clobber a value the reader has since changed in the doc's own control bar.\n * No artifact-params sender existed on this branch to reuse, so it is reimplemented\n * here from scratch.\n */\nfunction diffDocParams(\n params: MatterfactDocParams,\n sent: Map<string, string>,\n): MatterfactDocParams {\n const changed: MatterfactDocParams = {};\n for (const [key, value] of Object.entries(params) as [\n keyof MatterfactDocParams,\n string | undefined,\n ][]) {\n if (value === undefined) continue;\n const snapshot = JSON.stringify(value);\n if (sent.get(key) === snapshot) continue;\n changed[key] = value;\n sent.set(key, snapshot);\n }\n return changed;\n}\n\nexport interface MatterfactDocProps {\n /** The document catalog key (`EmbedDocumentPage`'s `?key=`). Fixed at mount — not\n * drivable via `params`, unlike `ticker`/`version`. */\n docKey: string;\n /** Initial ticker, seeds the iframe's `?ticker=` query param. */\n ticker?: string;\n /** Initial version. Always sent explicitly on the iframe `src` — defaults to\n * `'latest'` rather than being omitted, so the doc page never has to guess. */\n version?: 'latest' | `${number}-${number}-${number}`;\n /** Resolved as `prop ?? MatterfactAuthProvider's theme ?? 'auto'`. */\n theme?: 'auto' | 'light' | 'dark';\n /**\n * Drive the document's ticker/version from your page after mount. Pass your whole\n * param state; only the keys that actually changed since the last render are pushed\n * to the iframe (as `host.docParams`), and the full set is re-sent once the iframe\n * loads. A push is authoritative for every key it names — dropping a key stops\n * driving it, it does not reset it. Omit this prop entirely for a static document\n * whose ticker/version never change after mount.\n */\n params?: MatterfactDocParams;\n /** Applied to the `<iframe>` element the document renders into. */\n className?: string;\n /** Applied to the `<iframe>` element. Merged over the default fill. */\n style?: React.CSSProperties;\n}\n\n/**\n * Render a matterfact catalog document inline in a host page — the `MatterfactArtifact`\n * counterpart for a published document/report instead of an artifact board.\n *\n * ALWAYS session-authed: there's no standalone share-token mode the way\n * `MatterfactArtifact` has, so an ancestor `MatterfactAuthProvider` is required. With\n * none present (or a provider with no `publishableKey`), there's no way for the\n * iframe to authenticate — matching `MatterfactArtifact`'s own no-token-no-provider\n * decision, no iframe is built at all; the same \"incomplete link\" placeholder shape\n * is rendered instead of throwing.\n */\nexport function MatterfactDoc({\n docKey,\n ticker = '',\n version = 'latest',\n theme: themeProp,\n params,\n className,\n style,\n}: MatterfactDocProps) {\n // Read unconditionally, and declare every hook below unconditionally too — same\n // rationale as MatterfactAgent/MatterfactArtifact: hook order must stay identical\n // across renders even though ctx can flip from null to non-null (a provider\n // mounting after this component's first render).\n const ctx = useMatterfactConfig();\n const iframeRef = useRef<HTMLIFrameElement>(null);\n const sentParamsRef = useRef<Map<string, string>>(new Map());\n\n const origin = ctx?.widgetOrigin ?? DEFAULT_DOC_ORIGIN;\n // Same trick as MatterfactAgent's `actionsKey`: an inline `params={{...}}` literal\n // is a new object every render, so the effect keys on its serialized VALUE, not\n // identity, and reads the live `params` via closure when it actually runs.\n const paramsKey = JSON.stringify(params ?? null);\n\n const pushParams = (force: boolean) => {\n if (!params) return;\n const win = iframeRef.current?.contentWindow;\n if (!win) return;\n // A forced push (the iframe's `load` handler) resyncs the FULL current snapshot\n // — the receiver merges by key, so a duplicate push is harmless, and this is the\n // one moment we know its listener is actually alive to receive it.\n if (force) sentParamsRef.current.clear();\n const changed = diffDocParams(params, sentParamsRef.current);\n if (Object.keys(changed).length === 0) return;\n // Bare, unenveloped message — matches `EmbedDocumentPage`'s receiver exactly\n // (that route has no loader-driven handshake, so there's no protocol envelope\n // to wrap it in). targetOrigin is the widget's exact origin, never '*'.\n win.postMessage({ type: 'host.docParams', params: changed }, origin);\n };\n\n useEffect(() => {\n pushParams(false);\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [paramsKey]);\n\n if (!docKey) {\n // Thrown (not a silent no-op render), same rationale as MatterfactArtifact's\n // missing-name throw — a misconfigured integration fails loudly at first render.\n throw new Error('MatterfactDoc requires docKey');\n }\n\n if (!ctx || !ctx.publishableKey) {\n return (\n <div\n className={className}\n style={{\n width: '100%',\n height: 600,\n display: 'flex',\n alignItems: 'center',\n justifyContent: 'center',\n textAlign: 'center',\n ...style,\n }}\n >\n <p>This embedded document link is incomplete.</p>\n </div>\n );\n }\n\n const theme = themeProp ?? ctx.theme ?? 'auto';\n // Guarded for `window` for the same reason as MatterfactArtifact's session-mode\n // branch: this builds its iframe `src` synchronously in the render body, which can\n // also run during SSR.\n const hostOrigin = typeof window === 'undefined' ? '' : window.location.origin;\n const src =\n `${origin}/embed/document` +\n `?k=${encodeURIComponent(ctx.publishableKey)}&o=${encodeURIComponent(hostOrigin)}` +\n `&key=${encodeURIComponent(docKey)}&ticker=${encodeURIComponent(ticker)}` +\n `&version=${encodeURIComponent(version)}&theme=${theme}`;\n\n return (\n <iframe\n ref={iframeRef}\n src={src}\n title={`matterfact document ${docKey}`}\n className={className}\n style={{ width: '100%', height: 600, border: 0, ...style }}\n // Same sandbox tokens as MatterfactArtifact, incl. `allow-downloads` for an\n // in-place export/download inside the doc view.\n sandbox=\"allow-scripts allow-same-origin allow-forms allow-popups allow-popups-to-escape-sandbox allow-downloads\"\n // The receiver's listener attaches on ITS OWN mount, independent of auth — but\n // that's still after this iframe's `load` fires, so a push sent any earlier can\n // land before anyone's listening. Force a full resync here as a second, later\n // delivery attempt (see `pushParams`'s `force` branch).\n onLoad={() => pushParams(true)}\n />\n );\n}\n","/**\n * The matterfact embed bridge protocol.\n *\n * This is the contract between the HOST PAGE (a customer's site, running our\n * loader) and the WIDGET (our chat, running in a cross-origin iframe on\n * app.matterfact.com).\n *\n * Two things to understand before changing anything here:\n *\n * 1. THE IFRAME CANNOT SEE THE PAGE. It is cross-origin — that is the entire\n * point of it. So every fact about the host page (its URL, its DOM, what the\n * user just did) reaches the agent only because the LOADER, which runs as\n * ordinary page JS and *can* see it, chose to send it. The loader is the eye;\n * the widget is the mouth.\n *\n * 2. THIS CONTRACT OUTLIVES THE IFRAME. The next tier is an in-page React SDK\n * with no iframe at all — and it will speak exactly this. If the message\n * shapes are right, \"deeper integration\" is a transport swap (postMessage ->\n * direct call), not a rewrite. So model the DOMAIN here, not the plumbing.\n */\n\n// v3 (2026-07): adds `host.sitemap` (a structured, content-annotated site map),\n// `widget.navigate` (deeplink the host to one of its own routes), and `widget.chat`\n// (content-free chat-turn telemetry forwarded to the host's `onEvent`). All additive —\n// a peer that doesn't know a message simply never sends/handles it — so this is a\n// capability signal, not a breaking change. No code branches on the version number.\nexport const PROTOCOL_VERSION = 3;\n\n/** Every message is namespaced so we never collide with the host page's own postMessage traffic. */\nexport const CHANNEL = 'mf-embed';\n\n/* ────────────────────────────── refs ──────────────────────────────────── */\n\n/**\n * A handle on a real element in the host page.\n *\n * Format and semantics are lifted from Playwright's ARIA snapshot (`ref=e12`),\n * for a specific reason: it is the element-handle syntax the frontier models have\n * seen most, via playwright-mcp. We are not being original here on purpose.\n *\n * The ref is an in-page expando plus a snapshot-scoped Map — never an attribute,\n * so it is invisible to the host page's CSS, its serialization, and its own\n * mutation observers. It is resolved back to an element by the LOADER; the agent\n * never touches the DOM, and never learns a selector.\n */\nexport type ElementRef = string; // 'e12'\n\n/* ──────────────────────────── host → widget ───────────────────────────── */\n\n/** Ambient facts about the page. Cheap, sent on every navigation. */\nexport interface PageContext {\n url?: string;\n /** A route PATTERN ('/orders/:id') beats a raw path — it tells the agent what KIND of page this is. */\n route?: string;\n path?: string;\n title?: string;\n locale?: string;\n /** 1–2 sentences, written the way you'd brief a colleague. The highest-value field by far. */\n description?: string;\n /** Things the page is about. These also become @-mentionable in the composer, for free. */\n entities?: PageEntity[];\n /** Allowlisted only. Never a dump of app state. */\n data?: Record<string, unknown>;\n}\n\nexport interface PageEntity {\n /** Host kinds are namespaced (`x:ticker`) so they can never collide with matterfact's own. */\n kind: string;\n id: string;\n label: string;\n /** Pre-rendered, agent-readable. Keep it short. */\n text?: string;\n data?: Record<string, unknown>;\n}\n\n/**\n * How a route's content is classified, for the annotated site map.\n *\n * The host DECORATES the site map we auto-derive (see `SiteMapEntry`) so the agent\n * knows which routes carry MATTERFACT content and of what kind — app-wide awareness\n * it can navigate toward. This is CLASSIFICATION only: no per-instance ids, and never\n * a capability token. The concrete artifact/document on the CURRENT page still arrives\n * via `host.context` entities / `host.artifactGrants`, resolved per page.\n *\n * - `mf-artifact` — a co-embedded matterfact artifact (its slug); the token stays on\n * the live iframe / grant, never here.\n * - `mf-document` — a matterfact document (dossier/report), resolved to an MF_DOC_ID\n * host-side per page.\n * - `host-data` — the host's own data; not a matterfact entity.\n */\nexport type ContentClass =\n | { kind: 'mf-artifact'; slug: string }\n | { kind: 'mf-document'; doctype: string }\n | { kind: 'host-data' };\n\n/**\n * One route in the annotated site map: the path pattern + label we derived, plus the\n * host's content classification. `current` marks the route the user is on.\n */\nexport interface SiteMapEntry {\n /** Route pattern or path, e.g. '/app/company/:ticker/dossier'. */\n path: string;\n /** Human label for the route ('Company Dossier'). */\n label?: string;\n /** The host's classification of this route's content, when it annotated it. */\n content?: ContentClass;\n current?: boolean;\n}\n\n/** A co-embedded artifact's read-only capability, for the backend to materialize its\n * data. Backend-only — never rendered. `token` is the artifact's own embed token. */\nexport interface ArtifactGrant {\n id: string;\n owner: string;\n token: string;\n}\n\n/**\n * A DECLARED artifact grant — the `<MatterfactAgent artifacts>` prop (or\n * `window.matterfact.artifacts`). Same wire shape as a grant discovered by scanning\n * co-embedded `<MatterfactArtifact>` iframes, plus an optional `label`; it feeds the\n * SAME `host.artifactGrants` channel. This is how a host makes a board reachable from\n * a route where its iframe ISN'T mounted, without hidden zero-size iframes smuggling\n * the token into the DOM. The token stays in the grant channel, never in message text.\n */\nexport interface DeclaredArtifact {\n slug: string;\n owner: string;\n token: string;\n label?: string;\n}\n\n/**\n * One host-tool lifecycle event, surfaced to the host via `onToolEvent` (and the dev\n * panel) — the host's own telemetry hook. Fired loader-side; a throwing or slow\n * callback here NEVER blocks or breaks the call path.\n *\n * - `advertise` — the merged tool list changed (fired with the new set's names).\n * - `call` — a tool call is about to run (`args` present).\n * - `result` — the call settled (`ok`/`error`/`ms`).\n */\nexport interface ToolEvent {\n phase: 'advertise' | 'call' | 'result';\n name: string;\n toolClass: 'hoist' | 'mf' | 'app';\n args?: unknown;\n ok?: boolean;\n error?: string;\n ms?: number;\n}\n\n/**\n * One embed lifecycle event, surfaced to the host via `onEvent` — the host's single\n * telemetry hook. Fired loader-side (lifecycle) and widget-side (chat); a throwing or\n * slow callback NEVER blocks or breaks the mount/call/auth/run path.\n *\n * - `ready` — the widget connected (protocol handshake done).\n * - `open`/`close` — the corner widget was opened/closed. (Inline has no open event —\n * the host's own panel is the open/close control.)\n * - `navigate` — the agent deeplinked the host to one of its own routes.\n * - `auth` — host-auth passthrough: `required` (widget asked), `granted` (a\n * token was handed over), `failed` (no provider result / the provider threw).\n * - `error` — the widget failed to mount / load.\n * - `chat` — a chat-turn moment. Content-free: phase + opaque chatId only.\n * - `tool` — a host-tool lifecycle event (the `ToolEvent` shape, tagged). Also\n * still delivered untagged to the legacy `onToolEvent` hook for back-compat.\n * - `artifactParams` — an artifact param changed in the widget.\n */\n/**\n * A value an artifact param can carry. Mirrors the app's `ParamValue`\n * (`enum` / `enum[]` / `entity` / `date` / `daterange` / `boolean` / `number`)\n * DELIBERATELY BY COPY, not by import: this is a wire contract between two\n * independently-deployed artifacts, and a shared type would let a rename\n * compile on both sides while still breaking the wire. Bump PROTOCOL_VERSION\n * instead. See this file's header.\n */\nexport type ArtifactParamValue =\n | string\n | string[]\n | number\n | boolean\n /** `daterange`: a [from, to] pair of YYYY-MM-DD strings. */\n | [string, string];\n\n/**\n * Artifact params on the wire, keyed by declared param name.\n *\n * Values are typed but NOT verified against the artifact's declared spec —\n * a host can send anything, and the widget's param store coerces each value\n * to its declared type (or drops it) on arrival. This type says what a\n * well-behaved host means to send, not what the widget trusts it to have sent.\n */\nexport type ArtifactParams = { [name: string]: ArtifactParamValue };\n\nexport type MatterfactEvent =\n | { type: 'ready' }\n | { type: 'open' }\n | { type: 'close' }\n | { type: 'navigate'; href: string }\n | { type: 'auth'; phase: 'required' | 'granted' | 'failed' }\n | { type: 'error'; message: string }\n | {\n type: 'chat';\n phase: 'message' | 'response-start' | 'response-end';\n chatId?: string;\n }\n | ({ type: 'tool' } & ToolEvent)\n | {\n type: 'artifactParams';\n params: ArtifactParams;\n source: 'user' | 'context';\n };\n\n/**\n * A snapshot of the page as the agent sees it.\n *\n * `yaml` is Playwright's ARIA-snapshot rendering — role, accessible name, state,\n * and a ref on anything interactable:\n *\n * - heading \"Orders\" [level=1]\n * - textbox \"Search orders\" [ref=e5]\n * - button \"Export CSV\" [ref=e9]\n *\n * NOT raw HTML. A real page is tens of thousands of tokens of div soup, it gives\n * the model no handle to act with, and it is a prompt-injection firehose. The\n * a11y tree is a tenth the size and is the only signal that survives in-page —\n * `getEventListeners()` is DevTools-only, so listener-sniffing (the browser-use\n * approach) cannot see a single React handler on a real customer's app.\n */\nexport interface DomSnapshot {\n yaml: string;\n /** Monotonic. A ref is only valid within the snapshot that minted it. */\n seq: number;\n /** True when the walk hit its node budget and stopped. The agent must be told it is not seeing everything. */\n truncated: boolean;\n}\n\n/**\n * Where the user's attention is RIGHT NOW.\n *\n * This is the difference between an assistant that can read your page and one that\n * knows what you're doing. A full DOM snapshot answers \"what is on this screen\"; this\n * answers \"what are you looking at and working in\" — which is what a colleague\n * leaning over reads first. It is small, so it rides on every turn, where the full\n * snapshot does not.\n *\n * Every field here is a LABEL or a REF, never a value. `selection` is the sharpest\n * intent signal there is (\"this is the thing I'm asking about\"), and for exactly that\n * reason it is also where a user might have highlighted their own email — so it is\n * redacted and honours data-mf-private like everything else.\n */\nexport interface FocusContext {\n /** Text the user has selected. The single strongest \"I'm asking about this\" signal. Redacted. */\n selection?: string;\n /** The control the user is working IN — by label and ref, never its contents. */\n focused?: { ref?: ElementRef; label: string; role: string };\n /** The refs currently in the viewport: the handful of things actually on screen, out of the whole tree. */\n visibleRefs?: ElementRef[];\n /** How far down the page they are, 0–1. A cheap proxy for \"which part am I reading\". */\n scroll?: number;\n}\n\n/** Something the user did. Shaped after PostHog's autocapture — one readable line, not a replay frame. */\nexport interface ActivityEvent {\n seq: number;\n ts: number;\n type: 'nav' | 'click' | 'input' | 'submit' | 'selection' | 'custom';\n /** Human-readable: 'clicked button \"Export CSV\"'. Semantic, not coordinates. */\n summary: string;\n ref?: ElementRef;\n data?: Record<string, unknown>;\n}\n\n/**\n * A tool the HOST page exposes to the agent.\n *\n * Deliberately shaped as a superset of WebMCP (`navigator.modelContext`, W3C\n * WebML CG draft / Chrome origin trial). A customer who adopts WebMCP should be\n * able to hand us their existing tool declarations unchanged.\n */\nexport interface HostTool {\n name: string;\n description: string;\n /** JSON Schema. */\n inputSchema: Record<string, unknown>;\n /**\n * A read-only tool may run without asking. Anything that MUTATES the page\n * requires the user to confirm, every time — see the note on ToolCall.\n */\n readOnly?: boolean;\n /**\n * The tiered confirm policy, set by the host (e.g. `window.matterfact.hoist.actions`):\n * `'required'` ⇒ the widget must show a confirm card and get the user's approval\n * before every call; `'auto'` ⇒ it may call without asking. Absent for a tool that\n * predates this policy (treat as `'required'` — the safer default).\n */\n confirm?: 'auto' | 'required';\n}\n\nexport type HostToWidget =\n | { type: 'host.ready'; protocol: number; origin: string }\n | { type: 'host.context'; context: PageContext }\n | { type: 'host.focus'; focus: FocusContext }\n | { type: 'host.snapshot'; snapshot: DomSnapshot }\n | { type: 'host.region'; ref: ElementRef; yaml: string }\n | { type: 'host.activity'; events: ActivityEvent[] }\n | { type: 'host.artifactGrants'; grants: ArtifactGrant[] }\n /**\n * Set artifact param values. A PARTIAL patch — only the named params change, and\n * each value is validated against its declared spec before it applies (unknown or\n * invalid entries are dropped). Explicit and deliberate, so it always applies, even\n * to a param whose page-context binding the viewer has overridden.\n */\n | { type: 'host.artifactParams'; params: ArtifactParams }\n /**\n * The annotated site map — the app's routes with the host's content\n * classification. Sent on mount and re-sent on navigation (a virtualized SPA can\n * change what's routable). Distinct from the per-page `host.context`: this is the\n * whole app's structure, so the agent can reason about and navigate toward MF\n * content the user isn't currently looking at.\n */\n | { type: 'host.sitemap'; sitemap: SiteMapEntry[] }\n | { type: 'host.tools'; tools: HostTool[] }\n | {\n type: 'host.toolResult';\n callId: string;\n ok: boolean;\n result?: unknown;\n error?: string;\n }\n | { type: 'host.auth'; token: string; expiresAt: number }\n | { type: 'host.theme'; mode: 'light' | 'dark' }\n /**\n * Where the widget currently IS. Sent on mount and after anything that moves it\n * (drag, snap, mode change, viewport resize).\n *\n * The widget cannot work this out for itself — it can't see the viewport, and the\n * loader owns the box. Without it the widget renders its launcher against a stale\n * guess: a launcher dragged to the left edge kept drawing its circle bottom-RIGHT\n * inside a left-anchored box, so the circle jumped as soon as the box grew for the\n * hover pill. `growth` is the direction the pill/menu/panel must open (toward the\n * viewport centre); `mode` decides whether the launcher is a circle or an edge tab.\n */\n | {\n type: 'host.geometry';\n mode: 'float' | 'dock';\n dockSide: 'left' | 'right';\n growth: 'tl' | 'tr' | 'bl' | 'br';\n };\n\n/* ──────────────────────────── widget → host ───────────────────────────── */\n\n/**\n * The agent asking the host to do something.\n *\n * THE SECURITY MODEL, stated once: the page's content is UNTRUSTED. Text on the\n * page can try to instruct the agent, and the moment the agent can act, that stops\n * being a bad answer and becomes a bad ACTION. So:\n *\n * - v1 is READ-ONLY. `snapshot` and `activity` flow; no tool mutates the page.\n * - A tool that is not `readOnly` must be confirmed by the USER, every call. Not\n * \"unless the host opts out\" — every call. The trigger may have been written by\n * the page.\n * - The host is the enforcement point, not the widget. The widget can be lied to;\n * the loader runs in the page and decides what actually happens.\n */\nexport interface ToolCall {\n callId: string;\n name: string;\n args: Record<string, unknown>;\n}\n\nexport type WidgetToHost =\n | { type: 'widget.ready'; protocol: number }\n /**\n * Ask for the FULL page snapshot. This is the agent PULLING detail, not us pushing\n * it: the focus context rides on every turn, but the whole tree is fetched only\n * when the agent decides it needs to look — cheaper, and it reads what's relevant\n * to the question instead of drowning in the footer nav every turn.\n */\n | { type: 'widget.requestSnapshot' }\n /**\n * Pull the DECLARED page context fresh, right now. The host answers with `host.context`.\n *\n * Declared context (`window.matterfact.context`, or a `getPageContext()` callback) is\n * otherwise only published on load and on URL navigation, so in a virtualized SPA it goes\n * stale on any non-navigation state change (a team switch, a filter edit, a selection).\n * The widget pulls it at turn-assembly time — symmetric with `widget.requestSnapshot` —\n * so every turn carries the current context, not the context as of the last route change.\n */\n | { type: 'widget.requestContext' }\n /**\n * Admin max page-context mode from bootstrap — host clamps observation to this.\n */\n | { type: 'widget.pageContextMax'; mode: 'full' | 'declared' | 'off' }\n /** Zoom in: the a11y sub-tree under one ref (a table, a form, a card the agent cares about). */\n | { type: 'widget.readRegion'; ref: ElementRef }\n | { type: 'widget.callTool'; call: ToolCall }\n /** Chat opened/closed — the loader resizes the iframe and starts/stops observing. */\n | { type: 'widget.setOpen'; open: boolean }\n | { type: 'widget.resize'; height: number }\n /**\n * Dock / undock the open panel.\n *\n * Docked is a FULL-HEIGHT SIDE PANEL on the launcher's own side — left corners dock\n * to the left edge, right corners to the right, unless `setDockSide` overrides it.\n * This replaces the old `setExpanded`, which was hardcoded to the right edge and so\n * threw the panel across the screen whenever the launcher sat on the left.\n *\n * The loader owns the box, so only it can do this — `widget.resize` (height-only)\n * can neither widen the panel nor move it to an edge.\n */\n | { type: 'widget.setMode'; mode: 'float' | 'dock' }\n /** Which edge the tab and its panel live on. */\n | { type: 'widget.setDockSide'; side: 'left' | 'right' }\n /** Place the FLOATING launcher at a specific corner (the menu's snap picker). */\n | { type: 'widget.snapCorner'; corner: 'tl' | 'tr' | 'bl' | 'br' }\n /**\n * Resize the COLLAPSED launcher's host box so the widget can paint outside the 56px\n * circle — the hover pill (`{w:180,h:56}`) or the context menu (its own box). The\n * iframe clips to the host box, so anything beside the circle is invisible until the\n * host makes room. `{w:56,h:56}` returns to the bare circle. Ignored while open.\n */\n | { type: 'widget.setLauncherRegion'; w: number; h: number }\n /**\n * User resize of the floating panel, via the grip on its inner corner.\n *\n * Same contract as the launcher drag below, and for the same reason: the press is\n * captured by the iframe, so the host cannot see the moves. The widget tracks the\n * gesture and sends SCREEN-coordinate deltas; the loader owns the sizing, the\n * per-corner sign of the delta, and the clamp.\n */\n | { type: 'widget.resizeStart' }\n | { type: 'widget.resizeMove'; dx: number; dy: number }\n | { type: 'widget.resizeEnd' }\n /**\n * Launcher drag. The widget tracks the gesture; the host only moves the element.\n *\n * It has to be this way round, and the reason is easy to get wrong: a press that starts\n * inside an iframe is implicitly captured by that iframe, so every pointermove and the\n * pointerup belong to the widget's document until the button comes up. The host cannot\n * see them at all — not even through an overlay of its own, because capture beats hit\n * testing. So the host cannot take over a drag mid-gesture; it can only be told.\n *\n * `dx`/`dy` are deltas in SCREEN coordinates, which is the other half of the trick:\n * client coordinates are measured against the iframe's own viewport, and the iframe is\n * being moved under the cursor as the drag proceeds — so the pointer would appear to\n * stop moving and the launcher would stall after one step. Screen coordinates are\n * absolute and immune to that feedback loop.\n */\n | { type: 'widget.dragStart' }\n | { type: 'widget.dragMove'; dx: number; dy: number }\n | { type: 'widget.dragEnd' }\n /** Put the launcher back in the default corner and forget the saved size/dock. */\n | { type: 'widget.resetPos' }\n /** Get out of the way until the page reloads (a menu action, not a setting). */\n | { type: 'widget.hide' }\n /** The widget has no session; the host must run the hosted-login popup. */\n | { type: 'widget.needsAuth' }\n /**\n * Deeplink the host to one of its OWN routes — the \"open on page\" control on a\n * co-embedded document's side-panel viewer. The widget is a cross-origin iframe and\n * can't navigate the top window itself, so it asks the loader, which resolves the\n * href against the host location and refuses anything off the host's own origin.\n */\n | { type: 'widget.navigate'; href: string }\n /**\n * A chat-turn lifecycle moment, forwarded by the loader to the host's `onEvent`.\n * Content-free: `phase` + an opaque `chatId`, never message text. Additive.\n */\n | {\n type: 'widget.chat';\n phase: 'message' | 'response-start' | 'response-end';\n chatId?: string;\n }\n /**\n * An artifact param changed. Emitted ONLY for user- and context-originated changes,\n * never for a change the host itself pushed. The type excludes `'host'` so a caller\n * cannot mislabel an echo as legitimate; the actual no-echo guarantee comes from the\n * param store filtering by `source` before this callback is invoked.\n */\n | {\n type: 'widget.artifactParams';\n params: ArtifactParams;\n source: 'user' | 'context';\n };\n\n/* ───────────────────────────── envelope ───────────────────────────────── */\n\n/**\n * Everything on the wire is wrapped.\n *\n * `channel` keeps us out of the host page's own postMessage traffic (and out of\n * every other widget's). The receiver ALSO checks `event.origin` against an\n * allowlist and `event.source` against the expected window, on every single\n * message — a namespace is not a security boundary, it is a politeness.\n */\nexport interface Envelope<T> {\n channel: typeof CHANNEL;\n protocol: number;\n /** Correlates a response to its request; absent for notifications. */\n id?: string;\n payload: T;\n}\n\nexport function envelope<T>(payload: T, id?: string): Envelope<T> {\n return {\n channel: CHANNEL,\n protocol: PROTOCOL_VERSION,\n ...(id ? { id } : {}),\n payload,\n };\n}\n\nexport function isEnvelope(data: unknown): data is Envelope<unknown> {\n return (\n typeof data === 'object' &&\n data !== null &&\n (data as { channel?: unknown }).channel === CHANNEL\n );\n}\n","/**\n * Pure geometry for the embed launcher/panel.\n *\n * No DOM, no React — every function here is a total map from numbers/strings to\n * numbers/strings. That is deliberate: the loader attaches a CLOSED shadow root, so\n * the iframe inside it is unreachable from a test. Keeping the whole positioning\n * model in pure functions is what makes it verifiable at all; `loader.ts` is then a\n * thin applier that sets the returned box on the light-DOM host element.\n *\n * There are two MODES, and the mode decides both how the widget rests and how it opens:\n *\n * dock (default) — the launcher is a TAB flush against a side edge, and opening\n * gives a full-height side panel on that edge. This is the shape\n * most hosts want: it reads as part of the app, not as a bubble\n * floating over it.\n * float — the launcher is a CIRCLE the user can put anywhere, and opening\n * gives a panel anchored near it.\n *\n * In float the position is FREE. A corner is only recorded when the user drops the\n * launcher near one (see `snapOrFree`), because a snap that always fires isn't a\n * convenience, it's a constraint — you could never place the thing mid-edge. Recording\n * the corner when it IS snapped is what lets it stay in that corner across a viewport\n * resize, where a raw x/y would drift.\n */\n\nexport type Corner = 'tl' | 'tr' | 'bl' | 'br';\nexport type DockSide = 'left' | 'right';\nexport type Mode = 'float' | 'dock';\n\nexport interface EmbedGeometry {\n mode: Mode;\n /** float: the corner it snapped to, or null when freely placed. */\n corner: Corner | null;\n /** float: free launcher top-left, used only when `corner` is null. */\n x: number;\n y: number;\n floatW: number;\n floatH: number;\n dockSide: DockSide;\n /** dock: the tab's top offset along its edge. */\n tabY: number;\n dockW: number;\n}\n\n/**\n * A set of fixed-position box properties to apply to the host `<div>`.\n * Exactly two of top/right/bottom/left carry a px value; the other two are 'auto',\n * so the box stays pinned to the edges it grows away from.\n */\nexport interface BoxStyle {\n top: string;\n right: string;\n bottom: string;\n left: string;\n width: string;\n height: string;\n}\n\nexport const MARGIN = 20;\nexport const LAUNCHER = 56;\nexport const PILL_W = 180;\n/** How close to a corner a drop must land before it snaps. Generous enough to feel\n * magnetic, small enough that the middle of an edge is still reachable. */\nexport const SNAP_RADIUS = 96;\n/**\n * The docked launcher tab.\n *\n * Wide enough to carry the logo mark, and clearly TALLER than it is wide so the shape\n * still reads as a tab on the page edge. The ratio is the thing that matters: at a\n * near-square size any inner radius rounds it into a lozenge, which is what \"squished\n * circle\" looks like.\n */\nexport const TAB_W = 44;\nexport const TAB_H = 132;\n/**\n * How close to a SIDE edge a drop must land to become a docked tab. Dragging the tab\n * away from the edge is therefore how you float it, and dragging the circle back to an\n * edge is how you dock it — the placement follows where you put the thing, so there is\n * no mode switch to go hunting for.\n */\nexport const EDGE_DOCK = 64;\nexport const DEFAULT_FLOAT_W = 420;\nexport const DEFAULT_FLOAT_H = 640;\nexport const MIN_FLOAT_W = 320;\nexport const MIN_FLOAT_H = 420;\nexport const DEFAULT_DOCK_W = 600;\nexport const MIN_DOCK_W = 320;\n\nconst CORNERS: readonly Corner[] = ['tl', 'tr', 'bl', 'br'];\n\nexport function defaultGeometry(): EmbedGeometry {\n return {\n // Dock is the default: a side panel reads as part of the host app.\n mode: 'dock',\n corner: 'br',\n x: 0,\n y: 0,\n floatW: DEFAULT_FLOAT_W,\n floatH: DEFAULT_FLOAT_H,\n dockSide: 'right',\n tabY: 0, // 0 => centre it (see tabTop)\n dockW: DEFAULT_DOCK_W,\n };\n}\n\nfunction clamp(n: number, lo: number, hi: number): number {\n return Math.min(Math.max(n, lo), hi);\n}\n\n/** Where a snapped corner puts the launcher's top-left. */\nfunction cornerOrigin(\n corner: Corner,\n w: number,\n h: number,\n vw: number,\n vh: number,\n): { x: number; y: number } {\n return {\n x: corner[1] === 'l' ? MARGIN : vw - MARGIN - w,\n y: corner[0] === 't' ? MARGIN : vh - MARGIN - h,\n };\n}\n\n/** The launcher's top-left in float mode: its snapped corner, else its free spot. */\nexport function launcherOrigin(\n g: EmbedGeometry,\n vw: number,\n vh: number,\n): { x: number; y: number } {\n if (g.corner) return cornerOrigin(g.corner, LAUNCHER, LAUNCHER, vw, vh);\n return {\n x: clamp(g.x, MARGIN, Math.max(MARGIN, vw - MARGIN - LAUNCHER)),\n y: clamp(g.y, MARGIN, Math.max(MARGIN, vh - MARGIN - LAUNCHER)),\n };\n}\n\n/**\n * Which way things grow from the launcher: toward the viewport centre.\n *\n * Distinct from `geometry.corner` on purpose — a FREELY placed launcher has no corner,\n * but its pill, menu and panel still have to open inward or they'd run off the page.\n */\nexport function growthCorner(g: EmbedGeometry, vw: number, vh: number): Corner {\n if (g.corner) return g.corner;\n const o = launcherOrigin(g, vw, vh);\n return nearestCorner(o.x + LAUNCHER / 2, o.y + LAUNCHER / 2, vw, vh);\n}\n\n/** Which quadrant a point is in. */\nexport function nearestCorner(\n cx: number,\n cy: number,\n vw: number,\n vh: number,\n): Corner {\n return `${cy < vh / 2 ? 't' : 'b'}${cx < vw / 2 ? 'l' : 'r'}` as Corner;\n}\n\n/**\n * Resolve a drop: snap to a corner only if it landed NEAR one, else keep it where the\n * user put it (clamped on-screen). Returns the fields to merge into the geometry.\n */\nexport function snapOrFree(\n x: number,\n y: number,\n vw: number,\n vh: number,\n): { corner: Corner | null; x: number; y: number } {\n const cx = clamp(x, MARGIN, Math.max(MARGIN, vw - MARGIN - LAUNCHER));\n const cy = clamp(y, MARGIN, Math.max(MARGIN, vh - MARGIN - LAUNCHER));\n for (const corner of CORNERS) {\n const o = cornerOrigin(corner, LAUNCHER, LAUNCHER, vw, vh);\n if (Math.hypot(cx - o.x, cy - o.y) <= SNAP_RADIUS) {\n return { corner, x: cx, y: cy };\n }\n }\n return { corner: null, x: cx, y: cy };\n}\n\n/** Is this box close enough to a side edge to be docked? The live half of the drag. */\nexport function edgeDock(x: number, w: number, vw: number): DockSide | null {\n if (x <= EDGE_DOCK) return 'left';\n if (x + w >= vw - EDGE_DOCK) return 'right';\n return null;\n}\n\n/**\n * The point a drag MOVES — the widget's logical anchor, not its painted box.\n *\n * They differ once the panel is open: the box is the whole panel, but what the geometry\n * stores (and what a corner drop resolves) is the launcher's own origin. Dragging by the\n * box's top-left would shift the widget by the panel's size the moment you grabbed it.\n */\nexport function dragAnchor(\n g: EmbedGeometry,\n vw: number,\n vh: number,\n): { x: number; y: number } {\n if (g.mode === 'dock') {\n return { x: g.dockSide === 'left' ? 0 : vw - TAB_W, y: tabTop(g, vh) };\n }\n return launcherOrigin(g, vw, vh);\n}\n\n/**\n * The widget's painted horizontal extent for the state it is in.\n *\n * Derived, not measured: `getBoundingClientRect` is unavailable before layout (and is\n * all-zeros under jsdom), and the loader already knows every input. This is what the\n * edge test reads — \"dragged to the side\" is about the box you can see touching the\n * edge, which for an open panel is nowhere near its anchor.\n */\nexport function visibleBox(\n g: EmbedGeometry,\n open: boolean,\n vw: number,\n vh: number,\n): { x: number; w: number } {\n if (g.mode === 'dock') {\n const w = open\n ? Math.min(Math.max(g.dockW, MIN_DOCK_W), Math.max(0, vw - 2 * MARGIN))\n : TAB_W;\n return { x: g.dockSide === 'left' ? 0 : vw - w, w };\n }\n const o = launcherOrigin(g, vw, vh);\n if (!open) return { x: o.x, w: LAUNCHER };\n const { w } = clampSize(g.floatW, g.floatH, vw, vh, MIN_FLOAT_W, MIN_FLOAT_H);\n // A right-growing panel hangs LEFT off the launcher, so its left edge is the\n // launcher's right edge minus the panel width.\n return {\n x: growthCorner(g, vw, vh)[1] === 'r' ? o.x + LAUNCHER - w : o.x,\n w,\n };\n}\n\n/**\n * Resolve a drop into a PLACEMENT — which mode the widget lands in, and where.\n *\n * Two different measurements, deliberately:\n * - the EDGE test uses the widget's visible box, because \"dragged to the side\" is\n * about what you can see touching the edge. Testing the anchor instead means a\n * 420px-wide panel whose right edge is already against the screen refuses to dock,\n * since its anchor is still 400px away.\n * - the CORNER test uses the anchor, because that is what a corner placement stores.\n *\n * Priority matters, and corner beats edge on purpose: every corner is also within reach\n * of a side edge, so checking the edge first would make corner-snapping unreachable and\n * quietly turn the floating circle into a tab whenever you aimed for a corner.\n */\nexport function placeDrop(\n anchorX: number,\n anchorY: number,\n boxLeft: number,\n boxW: number,\n vw: number,\n vh: number,\n):\n | { mode: 'float'; corner: Corner | null; x: number; y: number }\n | { mode: 'dock'; dockSide: DockSide; tabY: number } {\n const snapped = snapOrFree(anchorX, anchorY, vw, vh);\n if (snapped.corner) return { mode: 'float', ...snapped };\n const side = edgeDock(boxLeft, boxW, vw);\n if (side) {\n return {\n mode: 'dock',\n dockSide: side,\n tabY: clamp(anchorY, 0, Math.max(0, vh - TAB_H)),\n };\n }\n return { mode: 'float', ...snapped };\n}\n\nexport function dockSideForCorner(corner: Corner): DockSide {\n return corner[1] === 'l' ? 'left' : 'right';\n}\n\nexport function effectiveDockSide(g: EmbedGeometry): DockSide {\n return g.dockSide;\n}\n\n/**\n * Fit a size inside the viewport.\n *\n * Order matters: the minimum is applied FIRST and the viewport cap LAST, so on a\n * viewport smaller than the minimum the VIEWPORT wins. That is the whole point — a\n * panel that honoured its minimum past the viewport edge would hang off the page.\n */\nexport function clampSize(\n w: number,\n h: number,\n vw: number,\n vh: number,\n minW: number,\n minH: number,\n): { w: number; h: number } {\n return {\n w: Math.min(Math.max(w, minW), Math.max(0, vw - 2 * MARGIN)),\n h: Math.min(Math.max(h, minH), Math.max(0, vh - 2 * MARGIN)),\n };\n}\n\n/**\n * Apply a resize drag to the float size. The grip sits on the panel's INNER corner\n * (the one facing viewport centre), so dragging it away from the anchored corner\n * grows the panel — which flips the sign of the delta per corner. Unclamped.\n */\nexport function applyResizeDelta(\n corner: Corner,\n startW: number,\n startH: number,\n dx: number,\n dy: number,\n): { w: number; h: number } {\n const wSign = corner[1] === 'r' ? -1 : 1; // right-anchored grows on -dx\n const hSign = corner[0] === 'b' ? -1 : 1; // bottom-anchored grows on -dy\n return { w: startW + wSign * dx, h: startH + hSign * dy };\n}\n\n/** Widen/narrow a docked panel by dragging its inner edge. */\nexport function applyDockResizeDelta(\n side: DockSide,\n startW: number,\n dx: number,\n): number {\n return side === 'right' ? startW - dx : startW + dx;\n}\n\nexport function parseGeometry(raw: string | null): EmbedGeometry | null {\n if (!raw) return null;\n try {\n const p = JSON.parse(raw) as Partial<EmbedGeometry>;\n // Pre-mode values (and the even older {right,bottom}) are dropped rather than\n // guessed at — there is no honest mapping from them onto this model.\n if (!p || (p.mode !== 'float' && p.mode !== 'dock')) return null;\n const d = defaultGeometry();\n const num = (v: unknown, dv: number) => (typeof v === 'number' ? v : dv);\n return {\n mode: p.mode,\n corner: CORNERS.includes(p.corner as Corner)\n ? (p.corner as Corner)\n : null,\n x: num(p.x, d.x),\n y: num(p.y, d.y),\n floatW: num(p.floatW, d.floatW),\n floatH: num(p.floatH, d.floatH),\n dockSide: p.dockSide === 'left' ? 'left' : 'right',\n tabY: num(p.tabY, d.tabY),\n dockW: num(p.dockW, d.dockW),\n };\n } catch {\n return null;\n }\n}\n\nexport function serializeGeometry(g: EmbedGeometry): string {\n return JSON.stringify(g);\n}\n\n/* ─────────────────────────── box builders ─────────────────────────────── */\n\nconst px = (n: number) => `${n}px`;\n\n/**\n * Pin the box by the two edges nearest the growth corner, so it grows inward.\n * Works for a snapped launcher and a freely-placed one alike: the anchor is always\n * the launcher's own top-left plus its size, expressed from whichever edges it grows\n * away from.\n */\nfunction boxAt(\n ox: number,\n oy: number,\n w: number,\n h: number,\n corner: Corner,\n vw: number,\n vh: number,\n): BoxStyle {\n const right = corner[1] === 'r';\n const bottom = corner[0] === 'b';\n // The anchored edges are measured from the LAUNCHER's own far side, so the launcher\n // stays put while the box grows past it...\n const rawX = right ? vw - (ox + LAUNCHER) : ox;\n const rawY = bottom ? vh - (oy + LAUNCHER) : oy;\n // ...but only as far as the viewport allows. Clamping the SIZE is not enough: a\n // launcher parked mid-page opens a 640px panel that grows straight off the top, and\n // the panel's header — the only way to close, dock or drag it — goes with it. So the\n // pinned offset is clamped too, which shifts the panel back into view while keeping\n // it as close to the launcher as it can be.\n const x = clamp(rawX, MARGIN, Math.max(MARGIN, vw - MARGIN - w));\n const y = clamp(rawY, MARGIN, Math.max(MARGIN, vh - MARGIN - h));\n return {\n top: bottom ? 'auto' : px(y),\n bottom: bottom ? px(y) : 'auto',\n left: right ? 'auto' : px(x),\n right: right ? px(x) : 'auto',\n width: px(w),\n height: px(h),\n };\n}\n\n/** The tab's top offset; a stored 0 means \"centre it\". */\nfunction tabTop(g: EmbedGeometry, vh: number): number {\n const max = Math.max(0, vh - TAB_H);\n return clamp(g.tabY || Math.round((vh - TAB_H) / 2), 0, max);\n}\n\n/** The docked launcher: a slim tab flush against its edge (no margin — it hugs). */\nexport function tabBox(g: EmbedGeometry, vw: number, vh: number): BoxStyle {\n const left = g.dockSide === 'left';\n return {\n top: px(tabTop(g, vh)),\n bottom: 'auto',\n left: left ? '0px' : 'auto',\n right: left ? 'auto' : '0px',\n width: px(TAB_W),\n height: px(TAB_H),\n };\n}\n\n/**\n * ANY float-mode box — the circle, the hover pill, the menu region, the open panel.\n *\n * They are all the same shape: a box of size w×h anchored at the launcher and growing\n * inward. One function rather than four wrappers, because this module is bundled into\n * the per-page loader stub and four near-identical bodies is pure weight.\n */\nexport function floatChrome(\n g: EmbedGeometry,\n w: number,\n h: number,\n vw: number,\n vh: number,\n /**\n * Override the growth direction. Used to FREEZE it for the length of a drag: growth\n * is derived from which half of the viewport the launcher is in, so dragging an open\n * panel across the middle would otherwise flip the side it grows from and jump the\n * panel by its own width. Held still, it tracks the cursor.\n */\n growth?: Corner,\n): BoxStyle {\n const o = launcherOrigin(g, vw, vh);\n return boxAt(o.x, o.y, w, h, growth ?? growthCorner(g, vw, vh), vw, vh);\n}\n\n/** The region the docked tab grows into for its context menu — hugging its edge. */\nexport function dockChrome(\n g: EmbedGeometry,\n w: number,\n h: number,\n vw: number,\n vh: number,\n): BoxStyle {\n const left = g.dockSide === 'left';\n return {\n top: px(tabTop(g, vh)),\n bottom: 'auto',\n left: left ? '0px' : 'auto',\n right: left ? 'auto' : '0px',\n width: px(w),\n height: px(h),\n };\n}\n\n/** The collapsed launcher for the current mode: dock → tab, float → circle. */\nexport function launcherBox(\n g: EmbedGeometry,\n vw: number,\n vh: number,\n): BoxStyle {\n return g.mode === 'dock'\n ? tabBox(g, vw, vh)\n : floatChrome(g, LAUNCHER, LAUNCHER, vw, vh);\n}\n\n/** The floating open panel, anchored at the launcher and clamped on-screen. */\nexport function floatBox(\n g: EmbedGeometry,\n vw: number,\n vh: number,\n growth?: Corner,\n): BoxStyle {\n const f = clampSize(g.floatW, g.floatH, vw, vh, MIN_FLOAT_W, MIN_FLOAT_H);\n return floatChrome(g, f.w, f.h, vw, vh, growth);\n}\n\n/** Full-height side panel flush against its edge. */\nexport function dockBox(g: EmbedGeometry, vw: number, _vh: number): BoxStyle {\n const w = Math.min(\n Math.max(g.dockW, MIN_DOCK_W),\n Math.max(0, vw - 2 * MARGIN),\n );\n const left = g.dockSide === 'left';\n return {\n top: '0px',\n bottom: '0px',\n height: 'auto',\n left: left ? '0px' : 'auto',\n right: left ? 'auto' : '0px',\n width: px(w),\n };\n}\n","import {\n CHANNEL,\n PROTOCOL_VERSION,\n envelope,\n isEnvelope,\n type ArtifactParams,\n type HostToWidget,\n type MatterfactEvent,\n type WidgetToHost,\n} from './protocol';\n// Type-only: erased at build, so it does NOT pull the lazy ./context chunk eagerly.\nimport type { PageContextProvider } from './context';\n\ndeclare global {\n interface Window {\n /**\n * The loader's public host API. Declared here so the members this file\n * installs are checked on both the read and the write; hosts also see a\n * typed surface instead of an opaque bag.\n */\n matterfact?: {\n setArtifactParams?: (params: ArtifactParams) => void;\n getPageContext?: unknown;\n [key: string]: unknown;\n };\n }\n}\nimport {\n applyDockResizeDelta,\n applyResizeDelta,\n clampSize,\n defaultGeometry,\n dockBox,\n floatBox,\n growthCorner,\n tabBox,\n dockChrome,\n floatChrome,\n parseGeometry,\n serializeGeometry,\n dragAnchor,\n visibleBox,\n edgeDock,\n placeDrop,\n LAUNCHER,\n MARGIN,\n MIN_DOCK_W,\n MIN_FLOAT_H,\n MIN_FLOAT_W,\n DEFAULT_DOCK_W,\n type BoxStyle,\n type Corner,\n type EmbedGeometry,\n} from './geometry';\n\n/**\n * The matterfact embed loader.\n *\n * <script type=\"module\" src=\"https://app.matterfact.com/embed/embed.js\"\n * data-key=\"pk_live_acme_...\"\n * data-origin=\"https://app.matterfact.com\"></script>\n *\n * This file runs on EVERY page load of a customer's site, so it is deliberately\n * tiny and deliberately dumb:\n *\n * - zero dependencies\n * - it does NOT read the DOM. The snapshot/activity code is a separate chunk,\n * fetched only when the user actually opens the chat. A customer should pay\n * nothing for a widget nobody clicked.\n * - it does NOT bundle React, or a CSS framework, or an RPC library.\n *\n * `iframe-resizer` is the thing you will be tempted to reach for here. It is\n * GPL-3.0 and cannot ship in this file. The ResizeObserver bridge below is the\n * fifteen lines it would have cost you anyway.\n */\n\n/** Returns a token this matterfact deployment's auth provider trusts (Firebase idToken,\n * Entra token, …). May be async; return null when there's no signed-in user. */\nexport type AuthTokenProvider = () => string | null | Promise<string | null>;\n\nexport interface LoaderConfig {\n publishableKey: string;\n /** Origin of the widget. Overridable for dev/self-hosted; defaults to production. */\n origin: string;\n theme: 'light' | 'dark' | 'auto';\n /** Optional label identifying THIS embedding, when one app embeds the agent in more\n * than one place (e.g. \"orders\" vs \"support\"). Stamped on runs/threads so usage and\n * history can be told apart per surface. Free-form; the app_id (from the session)\n * is the trusted half. */\n surface: string;\n /**\n * OPTIONAL host-auth passthrough, for TRUSTED first-party embeds (the admin portal,\n * EMC). When set, the host page already holds a token this deployment's auth provider\n * trusts (its Firebase idToken, an Entra token, …); the loader hands it to the widget\n * on `widget.needsAuth` instead of opening a sign-in popup. The backend still verifies\n * the token, so identity can't be forged — this only skips a redundant second sign-in.\n * Called on demand (so tokens can refresh). Absent ⇒ the popup/inline sign-in flow.\n * The `<script>` loader has no config here; it reads window.matterfact.getEmbedAuthToken\n * instead (see provideAuth).\n */\n authTokenProvider?: AuthTokenProvider;\n /**\n * OPTIONAL pull callback for DECLARED page context, for a host whose context changes\n * without a URL navigation (a virtualized SPA: team switch, filter edit, selection). The\n * widget calls it at turn-assembly time (via `widget.requestContext`), so every turn\n * carries current context instead of context as of the last route change. Symmetric with\n * `authTokenProvider`. The `<script>` loader has no config here; it reads\n * `window.matterfact.getPageContext` instead (see resolveDeclaredContext in context.ts).\n * Absent ⇒ falls back to the static `window.matterfact.context`.\n */\n pageContextProvider?: PageContextProvider;\n /**\n * Render INTO this element instead of floating in the corner — for a host that has its\n * own side panel, tool drawer or tab and wants the agent to live there.\n *\n * This inverts who owns the chrome. In corner mode the widget owns everything: it draws\n * its own launcher and it tells us how big to be (setOpen/setDocked/resize). Inside\n * someone else's panel, both are theirs — a widget that resized its host's drawer, or\n * drew a second floating bubble inside it, is just broken. So here we fill the\n * container, ignore the size messages, and the widget skips the launcher and renders\n * the chat directly.\n *\n * The host also owns visibility: showing and hiding their own panel IS the open/close\n * control, so there is nothing for us to add.\n */\n container?: HTMLElement | null;\n /**\n * Page observation mode: `full` (default / `true`), `declared` (getPageContext +\n * host sitemap only), or `off` (`false`). Auto DOM/focus/activity require `full`.\n */\n pageContext?: boolean | 'full' | 'declared' | 'off';\n /**\n * Force dev mode (see `devRequested()` below) without needing `?mfdev=1` on the host\n * URL. This is the programmatic equivalent of that trigger, not a replacement for it —\n * the URL param still works untouched; `mount()` ORs the two together.\n */\n dev?: boolean;\n}\n\nconst DEFAULT_ORIGIN = 'https://app.matterfact.com';\n\n/**\n * Has someone asked to see what the widget is doing? `?mfdev=1` on the HOST page, or\n * `localStorage.mfdev = '1'` on the host's own origin.\n *\n * Driven by the host's own URL rather than a `data-` attribute on purpose: an engineer\n * debugging a page they didn't write can add a query param, but they cannot redeploy the\n * customer's site to change a script tag.\n *\n * The localStorage twin exists because the URL trigger assumes the host LEAVES the query\n * string alone, and plenty of app shells don't — a client-side router that rewrites the\n * URL on boot drops `?mfdev=1` before we ever read it, which is unfixable from our side\n * and leaves the panel unreachable on exactly the deployments most in need of it. From\n * the host page's console:\n *\n * localStorage.mfdev = '1'; location.reload(); // on (survives navigation)\n * delete localStorage.mfdev; location.reload(); // off\n *\n * Read at mount, so it needs that reload to take effect. It trades the URL trigger's\n * ephemerality for surviving a rewrite — deliberately, and it is per-origin,\n * per-browser, and cleared by the same devtools pane that set it.\n *\n * This only surfaces what the widget already collects FROM THE PAGE IT'S ON, to the\n * person whose page it is. It grants no access: the session, the origin allowlist and\n * the route scope are all enforced server-side and none of them consult this.\n */\nfunction devRequested(): boolean {\n try {\n if (new URLSearchParams(location.search).get('mfdev') === '1') return true;\n // Separate try: Safari/incognito and blocked third-party storage THROW on access,\n // and that must not take the URL trigger down with it.\n try {\n return localStorage.getItem('mfdev') === '1';\n } catch {\n return false;\n }\n } catch {\n return false;\n }\n}\n\n/**\n * Host-auth diagnostics, printed only when dev mode is on (`devRequested`).\n *\n * Gated rather than always-on because \"no host-auth provider\" is a perfectly normal\n * configuration for a third-party site that uses the widget's own sign-in — warning\n * about it on every page load would be noise on the majority of installs. It is only\n * interesting when someone is asking why a FIRST-PARTY passthrough didn't happen, and\n * that someone can turn it on from the console. Shares the `[embed auth]` prefix with\n * the widget-side logs so one filter shows the whole flow across both frames.\n *\n * Messages are terse ON PURPOSE — every byte here is paid by every customer page (see\n * the stub budget in build.mjs). The words that explain them live at the call sites.\n */\nfunction alog(m: string, x?: unknown) {\n if (devRequested()) console.info('[embed auth] host: ' + m, x ?? '');\n}\n\nfunction readConfig(): LoaderConfig | null {\n // `document.currentScript` is null inside a module or after async execution, so\n // fall back to finding our own tag by src.\n const el =\n (document.currentScript as HTMLScriptElement | null) ??\n document.querySelector<HTMLScriptElement>('script[data-key][src*=\"embed\"]');\n\n const publishableKey = el?.dataset.key;\n if (!publishableKey) {\n console.error('[matterfact] missing data-key on the embed script tag');\n return null;\n }\n // `data-container=\"#panel\"` renders into the host's own element instead of the\n // corner. A selector rather than an element, because a script tag can't hand us a\n // reference — and if it doesn't resolve we fall back to the corner rather than\n // vanish: a widget nobody can find looks identical to a broken install.\n const sel = el?.dataset.container;\n const container = sel ? document.querySelector<HTMLElement>(sel) : null;\n if (sel && !container) {\n console.error(\n `[matterfact] data-container=\"${sel}\" matched nothing; falling back to the corner`,\n );\n }\n\n // `data-page-context`: full|declared|off (aliases: true/on/1 → full; false/off/0 → off).\n // Absent → full. Declared keeps getPageContext/host sitemap; skips auto DOM/focus.\n const pageContextAttr = el?.dataset.pageContext?.toLowerCase();\n let pageContext: LoaderConfig['pageContext'] = true;\n if (\n pageContextAttr === 'off' ||\n pageContextAttr === 'false' ||\n pageContextAttr === '0'\n ) {\n pageContext = false;\n } else if (pageContextAttr === 'declared') {\n pageContext = 'declared';\n } else if (\n pageContextAttr === 'full' ||\n pageContextAttr === 'on' ||\n pageContextAttr === 'true' ||\n pageContextAttr === '1'\n ) {\n pageContext = true;\n }\n\n return {\n publishableKey,\n origin: el?.dataset.origin || DEFAULT_ORIGIN,\n theme: (el?.dataset.theme as LoaderConfig['theme']) || 'auto',\n surface: el?.dataset.surface || '',\n container,\n pageContext,\n // No `data-dev` — see the `dev` field's doc comment: the URL trigger is the\n // point for the script-tag path, so there is deliberately no script-tag knob here.\n // No `data-actions` either: the action policy lives entirely in the lazy chunk,\n // read straight off `window.matterfact.hoist.actions` by hoist-runtime.ts's\n // readActionsConfig() — see LoaderConfig's class doc and MatterfactAgentProps.actions\n // (react.tsx) for the one remaining way to SET that global programmatically.\n };\n}\n\nconst POS_KEY = 'mf.embed.pos';\n\nclass EmbedHost {\n private iframe: HTMLIFrameElement | null = null;\n private shadow: ShadowRoot | null = null;\n /** Buffered until the widget says it's listening — postMessage before load is dropped silently. */\n private queue: HostToWidget[] = [];\n private ready = false;\n private open = false;\n /** The persisted box: launcher corner, float size, dock state/side/width. */\n private geo: EmbedGeometry = defaultGeometry();\n /** Float size at resizeStart; resizeMove deltas apply against it (they're cumulative). */\n private resizeBase: { w: number; h: number } | null = null;\n /** Docked width at resizeStart — the docked panel resizes in one axis only. */\n private dockBase = DEFAULT_DOCK_W;\n /** The widget's logical anchor when the drag began, and where it is now. Deltas arrive\n * cumulative from the press, so the current spot is simply origin + delta — no need to\n * read the box back, which stops being the thing we're moving the moment it docks. */\n private dragFrom: { x: number; y: number } | null = null;\n private dragAt = { x: 0, y: 0 };\n /** The painted box when the drag began — the edge test measures what you can SEE\n * touching the screen edge, which is not the anchor once the panel is open. */\n private dragBox = { x: 0, w: 0 };\n /** Growth direction frozen for the drag — see floatChrome's `growth` param. */\n private dragGrowth: Corner | null = null;\n /**\n * Has this drag been clear of the dock band yet?\n *\n * The resting position is INSIDE the band — a corner-anchored widget sits MARGIN\n * (20px) from the edge, well within EDGE_DOCK (64px) — so without this, picking one\n * up docked it instantly, before it had moved anywhere. Docking now requires\n * ENTERING the band rather than merely starting in it.\n */\n private dragLeftBand = false;\n /** Loaded on first open. Holds everything that touches the customer's DOM. */\n private context: Promise<typeof import('./context')> | null = null;\n /** The host element; kept so `destroy()` can remove it (React lifecycle). */\n private hostEl: HTMLDivElement | null = null;\n /** Rendering into the host's own element: they own the box, the chrome and visibility. */\n private readonly inline: boolean;\n /** Host-auth storm guard (see provideAuth). Counts getAuthToken calls; past a cap we stop\n * calling the provider, rather than joining the storm. This host instance OUTLIVES iframe\n * reloads, so — unlike anything in the widget — the counter survives the very reload loop\n * that drives the storm. (Kept tiny on purpose: this file has a hard per-page size budget;\n * the widget side carries the windowed/retry-after-settle half.) Terse name = fewer bytes\n * in the stub; it is the host-auth request count. */\n private ac = 0;\n\n constructor(private config: LoaderConfig) {\n this.inline = !!config.container;\n }\n\n mount() {\n // A CLOSED shadow root, on a container that resets every inherited property.\n //\n // We are a guest on someone else's page. Their CSS must not reach into us, our\n // CSS must not leak out, and their JS should not be able to walk into our tree\n // by accident (closed mode: `host.shadowRoot` is null). `contain` stops our\n // layout/paint from invalidating theirs.\n const host = document.createElement('div');\n this.hostEl = host;\n host.id = 'matterfact-embed';\n\n if (this.inline) {\n // The container's box IS the widget's box. No fixed positioning, no z-index\n // scramble, no drag: where this sits is the host's layout problem, which is the\n // entire point of asking for it.\n host.style.cssText = [\n 'all: initial',\n 'position: relative',\n 'display: block',\n 'width: 100%',\n 'height: 100%',\n 'contain: layout style',\n ].join(';');\n this.config.container!.appendChild(host);\n } else {\n this.geo = this.readGeometry();\n host.style.cssText = [\n 'all: initial',\n 'position: fixed',\n // Below the max so a host that genuinely needs to cover us (a modal, a cookie\n // banner they are legally obliged to show) still can.\n 'z-index: 2147483000',\n 'contain: layout style',\n // The host element owns the BOX now (position AND size); the iframe fills it.\n // Transitioning the box is what makes the corner snap and the open/dock read as\n // motion rather than a jump. Suppressed during drag/resize (see those handlers).\n 'transition: width .18s ease, height .18s ease, top .18s ease,' +\n ' right .18s ease, bottom .18s ease, left .18s ease',\n ].join(';');\n document.body.appendChild(host);\n }\n\n this.shadow = host.attachShadow({ mode: 'closed' });\n\n const style = document.createElement('style');\n // Shared prefix, then only what differs. This ships on every page load and the stub\n // has ~no headroom, so the two modes must not each carry a whole stylesheet.\n //\n // Inline fills the host's box and adds nothing: no radius, no shadow. Their panel\n // already has whatever chrome they chose; ours would sit inside it looking like a\n // mistake.\n // The iframe simply FILLS the host element, in both modes. The host owns every\n // dimension, so all the geometry lives in one place (and stays observable in the\n // light DOM — this shadow root is closed, so nothing in here can be inspected).\n // Only the corner treatment differs, switched off the host's data-mode: a circle\n // while it's the launcher, a card once it's a pill, menu or panel.\n //\n // Hairline ring + soft ambient: the widget must read as a crisp shape on ANY host\n // background, including pure white where a shadow alone bleeds.\n // Only the OPEN panel is a surface of ours. While collapsed the widget draws its\n // own shapes — the tab, the circle, the menu — onto the host's page, so the iframe\n // must not paint a card behind them: the host box grows to make room for the menu,\n // and a background there shows up as a slab hanging off the launcher.\n style.textContent =\n ':host{all:initial}' +\n 'iframe{border:0;display:block;width:100%;height:100%;background:transparent}' +\n (this.inline\n ? ''\n : // color-scheme only on the OPEN panel — on a collapsed launcher it makes the\n // UA paint an opaque canvas base behind our shapes.\n ':host([data-mode=\"float\"]) iframe,:host([data-mode=\"dock\"]) iframe' +\n '{color-scheme:light dark}' +\n ':host([data-mode=\"float\"]) iframe{border-radius:12px;' +\n 'box-shadow:0 0 0 1px #00000014,0 8px 40px #00000029}' +\n ':host([data-mode=\"dock\"]) iframe{box-shadow:0 0 0 1px #00000014,' +\n '0 8px 40px #00000029}' +\n ':host([data-mode=\"dock\"][data-flush=\"right\"]) iframe' +\n '{border-radius:12px 0 0 12px}' +\n ':host([data-mode=\"dock\"][data-flush=\"left\"]) iframe' +\n '{border-radius:0 12px 12px 0}');\n this.shadow.appendChild(style);\n\n const iframe = document.createElement('iframe');\n iframe.title = 'matterfact assistant';\n //\n // `allow-same-origin` is REQUIRED, and leaving it out is the trap.\n //\n // Without it the framed document gets an OPAQUE origin: its postMessage arrives\n // as `event.origin === \"null\"`, so our own origin check rejects the handshake and\n // the bridge silently never connects. It also denies the widget any storage at\n // all — no session, no auth, ever.\n //\n // The familiar warning — \"allow-scripts + allow-same-origin lets the frame escape\n // its sandbox\" — is about a frame that is SAME-ORIGIN WITH THE FRAMER, which can\n // then reach up and delete its own sandbox attribute. Ours is cross-origin to the\n // host by construction (that is the entire security model), so it cannot touch\n // this element. What the sandbox still buys us is real: no top-level navigation,\n // no downloads, no pointer lock.\n // `allow-downloads` lets the chat save a file in place. Every download path\n // is an in-place `<a download>`.click(); WITHOUT this flag a sandboxed frame\n // blocks that and the browser falls back to opening the file in a new tab.\n // It is a sandbox flag, not a permissions-policy `allow` token.\n iframe.setAttribute(\n 'sandbox',\n 'allow-scripts allow-same-origin allow-forms allow-popups allow-popups-to-escape-sandbox allow-downloads',\n );\n // Powerful features need an explicit permissions-policy grant: a cross-origin\n // frame is granted NONE by default, so without `allow` they are hard-blocked\n // no matter what the user approves.\n // - microphone: dictation's getUserMedia.\n // - clipboard-write: the \"Copy\" button on a chat message calls\n // navigator.clipboard.writeText, which throws NotAllowedError without it.\n // The bare tokens scope each grant to the iframe's own origin (the app), not\n // the host page. A host page can still deny either via its own\n // Permissions-Policy response header — outside our control, which is why the\n // in-app copy path also degrades gracefully rather than throwing.\n iframe.setAttribute('allow', 'microphone; clipboard-write');\n iframe.src =\n `${this.config.origin}/embed/chat?k=${encodeURIComponent(\n this.config.publishableKey,\n )}&o=${encodeURIComponent(location.origin)}` +\n (this.config.surface\n ? `&s=${encodeURIComponent(this.config.surface)}`\n : '') +\n // Either trigger works: the URL param (no redeploy needed) OR the config's\n // programmatic `dev` (the React prop). See both doc comments above.\n (devRequested() || this.config.dev ? '&dev=1' : '') +\n (this.inline ? '&inline=1' : '');\n this.iframe = iframe;\n this.shadow.appendChild(iframe);\n\n window.addEventListener('message', this.onMessage);\n if (!this.inline) {\n // A viewport change can strand a saved box off-page (a laptop undocked from an\n // external monitor is the common one), so re-clamp and re-anchor on resize.\n window.addEventListener('resize', this.onViewportResize);\n this.place();\n }\n }\n\n /**\n * Every message is checked twice, on every single message — not once at setup.\n *\n * `channel` is a namespace, not a boundary. The origin and source checks are the\n * boundary: any frame on the page can postMessage us, and a page with an ad iframe\n * on it has plenty of frames.\n */\n private onMessage = (event: MessageEvent) => {\n if (event.origin !== this.config.origin) return;\n if (event.source !== this.iframe?.contentWindow) return;\n if (!isEnvelope(event.data)) return;\n\n this.handle(event.data.payload as WidgetToHost);\n };\n\n /**\n * Test seam. The shadow root is CLOSED, so a test cannot reach the iframe to forge a\n * source-valid MessageEvent — this routes a message through the identical logic\n * without weakening the origin/source checks above, which stay the only real door.\n */\n __testHandle(msg: WidgetToHost) {\n this.handle(msg);\n }\n\n /**\n * The eager stub's own emit-and-swallow for the host's `onEvent` telemetry hook.\n * Deliberately does NOT import the registry's `emitEvent`: pulling the lazy ./context\n * chunk (where the registry lives) into the size-budgeted stub would blow it, so this\n * ~5-line duplication across the bundle boundary is intentional. Host telemetry is\n * host code — a throw here must never break the widget.\n */\n private emit(e: MatterfactEvent) {\n const cb = (\n globalThis as { matterfact?: { onEvent?: (e: MatterfactEvent) => void } }\n ).matterfact?.onEvent;\n if (typeof cb !== 'function') return;\n try {\n cb(e);\n } catch {\n /* host telemetry must never break the widget */\n }\n }\n\n private handle(msg: WidgetToHost) {\n switch (msg.type) {\n case 'widget.ready':\n this.ready = true;\n this.send({\n type: 'host.ready',\n protocol: PROTOCOL_VERSION,\n origin: location.origin,\n });\n // Theme NOW, not on first open. It used to ride in with the lazy context\n // chunk, which is only fetched when the chat is opened — so a collapsed\n // launcher sat in the wrong colours until you interacted with it.\n this.send({ type: 'host.theme', mode: this.themeMode() });\n this.flush();\n // Inline has no \"open\" event to hang this off — the chat is visible the moment\n // the host renders their panel, so the eye has to load now or the agent is\n // blind to the page for the whole session. The corner keeps its lazy load: a\n // page whose visitors never click still pays nothing.\n if (this.inline) void this.loadContext();\n this.emit({ type: 'ready' });\n break;\n\n case 'widget.setOpen':\n // Host telemetry sees the open/close moment regardless of placement (the inline\n // guard below only skips the popup SIZING, which is meaningless inline).\n this.emit({ type: msg.open ? 'open' : 'close' });\n // Inline: the host's panel IS the open/close control. There's no popup to size.\n if (this.inline) break;\n this.open = msg.open;\n this.place();\n // The DOM code is fetched HERE — the first time someone actually opens the\n // chat — and never on a page where they don't.\n if (msg.open) void this.loadContext();\n break;\n\n case 'widget.setMode':\n // Inline: the host owns the box; float/dock is not ours to choose.\n if (this.inline) break;\n this.geo.mode = msg.mode;\n this.writeGeometry(this.geo);\n this.place();\n break;\n\n case 'widget.setDockSide':\n if (this.inline) break;\n this.geo.dockSide = msg.side;\n this.writeGeometry(this.geo);\n this.place();\n break;\n\n case 'widget.snapCorner':\n if (this.inline) break;\n // Snapping is a FLOAT affordance; the picker only shows in float mode.\n this.geo.corner = msg.corner;\n this.writeGeometry(this.geo);\n this.place();\n break;\n\n case 'widget.setLauncherRegion': {\n // Only meaningful while collapsed — open, the panel is already bigger than any\n // pill or menu, and growing the box would fight the panel's own size.\n if (this.inline || this.open) break;\n const vw = window.innerWidth;\n const vh = window.innerHeight;\n if (msg.w <= LAUNCHER && msg.h <= LAUNCHER) {\n // Back to the bare launcher — place() already knows how to draw it.\n this.place();\n } else {\n const region =\n this.geo.mode === 'dock'\n ? dockChrome(this.geo, msg.w, msg.h, vw, vh)\n : floatChrome(this.geo, msg.w, msg.h, vw, vh);\n this.applyBox(region, msg.h > LAUNCHER ? 'menu' : 'pill');\n }\n break;\n }\n\n case 'widget.resize':\n // Inline: the container's height is the host's business, and an auto-grow here\n // would fight their layout every time the transcript got longer.\n if (this.inline) break;\n // Content height auto-grow, for the FLOATING panel only. Docked, the edges own\n // the height; and a manual resize has already written floatH, which place()\n // would restore on the next call anyway.\n if (this.hostEl && this.open && this.geo.mode === 'float') {\n this.hostEl.style.height = `${Math.min(\n msg.height,\n Math.max(0, window.innerHeight - 2 * MARGIN),\n )}px`;\n }\n break;\n\n case 'widget.resizeStart':\n if (this.inline) break;\n this.resizeBase = { w: this.geo.floatW, h: this.geo.floatH };\n this.dockBase = this.geo.dockW;\n // The box must track the pointer exactly; an eased transition lags behind it.\n if (this.hostEl) this.hostEl.style.transition = 'none';\n break;\n\n case 'widget.resizeMove': {\n if (this.inline || !this.resizeBase) break;\n if (this.geo.mode === 'dock') {\n // Docked, only the WIDTH is the user's — the edges own the height.\n const w = applyDockResizeDelta(\n this.geo.dockSide,\n this.dockBase,\n msg.dx,\n );\n this.geo.dockW = Math.min(\n Math.max(w, MIN_DOCK_W),\n Math.max(0, window.innerWidth - 2 * MARGIN),\n );\n } else {\n const next = applyResizeDelta(\n growthCorner(this.geo, window.innerWidth, window.innerHeight),\n this.resizeBase.w,\n this.resizeBase.h,\n msg.dx,\n msg.dy,\n );\n const fit = clampSize(\n next.w,\n next.h,\n window.innerWidth,\n window.innerHeight,\n MIN_FLOAT_W,\n MIN_FLOAT_H,\n );\n this.geo.floatW = fit.w;\n this.geo.floatH = fit.h;\n }\n this.place();\n break;\n }\n\n case 'widget.resizeEnd':\n if (this.inline) break;\n this.resizeBase = null;\n if (this.hostEl) this.hostEl.style.transition = '';\n this.writeGeometry(this.geo);\n break;\n\n case 'widget.requestContext':\n // The pull path for declared context — re-read it now (via getPageContext if the\n // host supplied one) and re-publish `host.context`. No-op if pageContext is off.\n void this.loadContext().then((m) => m.provideContext());\n break;\n\n case 'widget.pageContextMax':\n // Admin max from bootstrap — clamp host observation without remounting.\n void this.loadContext().then((m) => {\n m.setPageContextMax(msg.mode);\n });\n break;\n\n case 'widget.requestSnapshot':\n // pageContext: false ⇒ `sendSnapshot` itself no-ops (see context.ts) rather\n // than gating here — this file is the size-budgeted stub, and the check\n // costs nothing extra in the lazy chunk that already owns the DOM read.\n void this.loadContext().then((m) => m.sendSnapshot(this.send));\n break;\n\n case 'widget.readRegion':\n void this.loadContext().then((m) => m.sendRegion(msg.ref, this.send));\n break;\n\n case 'widget.callTool':\n void this.loadContext().then((m) => m.callTool(msg.call, this.send));\n break;\n\n // Everything below is LAUNCHER chrome: there is no launcher inline (the widget\n // doesn't draw one), and moving or hiding the host's own panel from inside it\n // would be us redecorating their app. An older cached loader could still be told\n // any of these by a newer widget, so they're guarded rather than assumed absent.\n case 'widget.dragStart':\n if (this.inline) break;\n this.dragFrom = dragAnchor(\n this.geo,\n window.innerWidth,\n window.innerHeight,\n );\n this.dragAt = { ...this.dragFrom };\n this.dragLeftBand = false;\n this.dragGrowth = growthCorner(\n this.geo,\n window.innerWidth,\n window.innerHeight,\n );\n this.dragBox = visibleBox(\n this.geo,\n this.open,\n window.innerWidth,\n window.innerHeight,\n );\n // Follow the pointer 1:1 while dragging; the ease is for the snap on release.\n if (this.hostEl) this.hostEl.style.transition = 'none';\n break;\n\n case 'widget.dragMove': {\n if (this.inline || !this.dragFrom) break;\n const vw = window.innerWidth;\n const vh = window.innerHeight;\n this.dragAt = {\n x: this.dragFrom.x + msg.dx,\n y: this.dragFrom.y + msg.dy,\n };\n // Switch placement DURING the drag, not on release: drag toward a side and it\n // becomes the tab under your cursor, drag away and it pops back to the circle.\n // Deliberately only the EDGE test here — running the full drop resolver would\n // also snap corners mid-gesture, yanking the widget away while you're still\n // holding it. Corners are resolved on release, where a jump reads as a snap.\n const side = edgeDock(this.dragBox.x + msg.dx, this.dragBox.w, vw);\n if (!side) this.dragLeftBand = true;\n // A tab is already docked, so it may stay docked without having left first;\n // anything else must have been clear of the band at some point in this drag.\n if (side && (this.dragLeftBand || this.geo.mode === 'dock')) {\n this.geo.mode = 'dock';\n this.geo.dockSide = side;\n this.geo.tabY = Math.max(0, this.dragAt.y);\n } else {\n this.geo.mode = 'float';\n this.geo.corner = null;\n this.geo.x = this.dragAt.x;\n this.geo.y = this.dragAt.y;\n }\n this.place();\n break;\n }\n\n case 'widget.dragEnd': {\n if (this.inline || !this.dragFrom) break;\n // The drop resolves the FULL placement — including the corner snap the live\n // pass deliberately skipped. Restoring the transition first is what makes that\n // final snap animate rather than teleport.\n const at = placeDrop(\n this.dragAt.x,\n this.dragAt.y,\n this.dragBox.x + (this.dragAt.x - this.dragFrom.x),\n this.dragBox.w,\n window.innerWidth,\n window.innerHeight,\n );\n this.geo.mode = at.mode;\n if (at.mode === 'dock') {\n this.geo.dockSide = at.dockSide;\n this.geo.tabY = at.tabY;\n } else {\n this.geo.corner = at.corner;\n this.geo.x = at.x;\n this.geo.y = at.y;\n }\n this.dragFrom = null;\n this.dragGrowth = null;\n if (this.hostEl) this.hostEl.style.transition = '';\n this.place();\n this.writeGeometry(this.geo);\n break;\n }\n\n case 'widget.resetPos':\n if (this.inline) break;\n this.geo = defaultGeometry();\n this.writeGeometry(this.geo);\n this.place();\n break;\n\n case 'widget.hide':\n // Inline: hiding is the host's panel to hide. Blanking their drawer from in\n // here would leave them with an empty box and no way to get it back.\n if (this.inline) break;\n if (this.hostEl) this.hostEl.style.display = 'none';\n break;\n\n case 'widget.needsAuth':\n this.emit({ type: 'auth', phase: 'required' });\n void this.provideAuth();\n break;\n\n case 'widget.navigate':\n this.emit({ type: 'navigate', href: msg.href });\n // Delegated to the lazy context chunk (kept out of the every-page stub —\n // the eager budget is tight). By the time a document deeplink is clicked the\n // chat is open, so context is already loaded.\n void this.loadContext().then((m) => m.navigateHost(msg.href));\n break;\n\n case 'widget.chat':\n // Content-free chat-turn telemetry (phase + opaque chatId), relayed straight\n // to the host's onEvent. Never carries message text.\n this.emit({ type: 'chat', phase: msg.phase, chatId: msg.chatId });\n break;\n\n case 'widget.artifactParams':\n // An artifact param changed in the widget (user edit or context binding),\n // relayed straight to the host's onEvent. params/source pass through unchanged.\n this.emit({\n type: 'artifactParams',\n params: msg.params,\n source: msg.source,\n });\n break;\n }\n }\n\n /**\n * Own a drag for its lifetime.\n *\n * The widget reports the press and then goes quiet: a cross-origin iframe only gets\n * pointer events while the pointer is over it, and a drag leaves that box immediately.\n * So we lay a transparent layer over the whole viewport and track the gesture in the\n * host document, where it can't be lost. The layer also stops the pointer landing on\n * the customer's own UI mid-drag (text selection, hover states, stray clicks).\n *\n * We are the dumb half on purpose — see `widget.dragMove` in the protocol for why the\n * widget has to own the gesture. All we do is take a delta and place the element.\n */\n /** Write a box onto the host element and stamp the mode the shadow CSS keys off. */\n private applyBox(\n box: BoxStyle,\n mode: 'launcher' | 'tab' | 'pill' | 'menu' | 'float' | 'dock',\n ) {\n if (!this.hostEl || this.inline) return;\n const s = this.hostEl.style;\n s.top = box.top;\n s.right = box.right;\n s.bottom = box.bottom;\n s.left = box.left;\n s.width = box.width;\n s.height = box.height;\n this.hostEl.dataset.mode = mode;\n // The flush shapes (tab, docked panel) round only on the side facing the page.\n if (mode === 'tab' || mode === 'dock')\n this.hostEl.dataset.flush = this.geo.dockSide;\n else delete this.hostEl.dataset.flush;\n }\n\n /** Re-apply the box for whatever state we're in: collapsed launcher, float, or dock. */\n /**\n * The growth direction to use RIGHT NOW — frozen while a drag is in flight.\n *\n * Every consumer has to agree on this, which is the bug that made the widget\n * flip-flop under small movements near the viewport centre: the box was frozen but\n * `host.geometry` still published a freshly-derived growth, so the WIDGET kept\n * swapping which corner it drew against (and it animates that), twitching back and\n * forth while the loader held perfectly still.\n */\n private growthNow(): Corner {\n return (\n this.dragGrowth ??\n growthCorner(this.geo, window.innerWidth, window.innerHeight)\n );\n }\n\n private place() {\n const vw = window.innerWidth;\n const vh = window.innerHeight;\n if (!this.open) {\n const dock = this.geo.mode === 'dock';\n this.applyBox(\n dock\n ? tabBox(this.geo, vw, vh)\n : floatChrome(this.geo, LAUNCHER, LAUNCHER, vw, vh, this.growthNow()),\n dock ? 'tab' : 'launcher',\n );\n } else if (this.geo.mode === 'dock') {\n this.applyBox(dockBox(this.geo, vw, vh), 'dock');\n } else {\n this.applyBox(floatBox(this.geo, vw, vh, this.growthNow()), 'float');\n }\n this.publishGeometry();\n }\n\n /**\n * Tell the widget where it is. It can't see the viewport and doesn't own the box, so\n * without this it draws its launcher against a stale guess — which is what made the\n * circle jump when the launcher sat on the left (see `host.geometry` in the protocol).\n */\n private publishGeometry() {\n this.send({\n type: 'host.geometry',\n mode: this.geo.mode,\n dockSide: this.geo.dockSide,\n growth: this.growthNow(),\n });\n }\n\n /** The theme to publish: an explicit data-theme wins, else the OS preference. */\n private themeMode(): 'light' | 'dark' {\n return this.config.theme === 'auto'\n ? matchMedia('(prefers-color-scheme: dark)').matches\n ? 'dark'\n : 'light'\n : this.config.theme;\n }\n\n private onViewportResize = () => {\n // Re-clamp and re-anchor: a saved box must never be left hanging off a smaller page.\n this.place();\n };\n\n private readGeometry(): EmbedGeometry {\n // First-party storage on the CUSTOMER's origin, so each site remembers its own spot.\n // Never throws: storage is unavailable in some embedding contexts, and a widget that\n // can't remember where it was put is fine — one that fails to mount is not.\n try {\n return parseGeometry(localStorage.getItem(POS_KEY)) ?? defaultGeometry();\n } catch {\n return defaultGeometry();\n }\n }\n\n private writeGeometry(g: EmbedGeometry) {\n try {\n localStorage.setItem(POS_KEY, serializeGeometry(g));\n } catch {\n /* storage denied — the box just won't survive the reload */\n }\n }\n\n /**\n * Answer `widget.needsAuth`. If a trusted first-party host has an auth-token provider,\n * call it and hand the token straight to the widget via `host.auth` — no popup. The\n * provider comes from EITHER the programmatic config (the React `<MatterfactAgent\n * getAuthToken>` prop) OR a global the host page sets for the `<script>` loader:\n *\n * window.matterfact = { getEmbedAuthToken: () => getIdToken(user) };\n *\n * Read fresh at call time (not at readConfig), so a global set after the loader booted\n * — e.g. once the host's auth is ready — is still picked up. `expiresAt: 0`: the widget\n * doesn't cache it, it exchanges the token for a rotating embed session anyway.\n *\n * No provider ⇒ this does nothing. The loader never opens a sign-in popup — the widget\n * owns interactive sign-in (inline Firebase/email, and Microsoft's own MSAL login popup\n * for Entra, then the PKCE token exchange), so the loader must not also fire its stale\n * `/embed/authorize?k=&o=` popup, which the deployed authorize page no longer accepts.\n */\n private async provideAuth() {\n const provider =\n this.config.authTokenProvider ??\n (globalThis as { matterfact?: { getEmbedAuthToken?: AuthTokenProvider } })\n .matterfact?.getEmbedAuthToken;\n if (!provider) {\n // The silent case, and the one that looks identical to every other failure from\n // the outside: a host that registers getEmbedAuthToken LATE (after its own login\n // resolves) isn't asked again — the widget requests host-auth once per session.\n // So \"the page is signed in but the widget shows a login form\" and \"this host\n // never wired host-auth\" are the same picture unless we say which one happened.\n alog('no provider');\n return; // the widget owns interactive sign-in; the loader never opens it\n }\n\n // Storm guard. A healthy host-auth flow asks ONCE — the widget exchanges the token for a\n // rotating session and never asks again. A flood of `widget.needsAuth` means the token\n // keeps being rejected (wrong audience/scope) and something is remounting the widget in a\n // loop; calling getAuthToken on every one hammers the host's identity provider (MSAL →\n // login.microsoftonline.com), which can get the app RATE-LIMITED. Past the cap we simply\n // stop calling the provider. Minimal by necessity (hard per-page size budget) and\n // silent to the USER — the widget-side guard is what surfaces the reason to them and\n // logs the rejection; `alog` only says the cap is why we stopped asking.\n if (++this.ac > 5) {\n alog('storm cap');\n return;\n }\n try {\n const token = await provider();\n if (token) this.send({ type: 'host.auth', token, expiresAt: 0 });\n // One emit either way — a null token is a failure to provide, same as a throw.\n this.emit({ type: 'auth', phase: token ? 'granted' : 'failed' });\n // 'empty' is the interesting one: the host HAS a provider and it resolved\n // nothing — its own login usually hasn't finished yet. Distinct from 'no\n // provider' (never wired, or wired after we asked) and from a throw.\n alog(token ? 'token' : 'empty');\n } catch (e) {\n this.emit({ type: 'auth', phase: 'failed' });\n alog('threw', e);\n /* no token this time; the widget falls back to its own inline / popup sign-in */\n }\n }\n\n private loadContext() {\n // One chunk, one fetch, cached. Everything that can see the customer's page\n // lives behind this boundary.\n this.context ??= import('./context').then((m) => {\n // The single place the eye actually starts: when pageContext is off, `start()`\n // skips installing every DOM/nav/focus/activity observer — not a filter over\n // what gets sent afterward, nothing is collected in the first place. Passed\n // through raw: `start`'s default parameter is what turns `undefined` (the\n // option omitted) into \"on\".\n m.start(\n this.send,\n this.config.origin,\n this.config.pageContext,\n this.config.pageContextProvider,\n );\n // start() emits the OS-preference theme. An explicit data-theme on the\n // script tag is the host's choice — resend it so it wins over auto-detect\n // (a light-branded site must be able to force a light widget on a dark-OS\n // visitor, and vice versa).\n if (this.config.theme !== 'auto') {\n this.send({ type: 'host.theme', mode: this.config.theme });\n }\n return m;\n });\n return this.context;\n }\n\n private send = (msg: HostToWidget) => {\n if (!this.ready) {\n this.queue.push(msg);\n return;\n }\n // targetOrigin is the widget's exact origin — never '*'. A '*' here would\n // broadcast the customer's page content to whatever happens to be listening.\n this.iframe?.contentWindow?.postMessage(envelope(msg), this.config.origin);\n };\n\n private flush() {\n const pending = this.queue;\n this.queue = [];\n for (const m of pending) this.send(m);\n }\n\n /**\n * Push artifact param values into the widget — a PARTIAL patch; only the named\n * params change. Buffered like every other host→widget message until the widget\n * says it's listening.\n *\n * Explicit and deliberate, so it applies even to a param whose page-context binding\n * the viewer has overridden (spec §7.3).\n */\n setArtifactParams(params: ArtifactParams): void {\n // Runtime check despite the type: this is a PUBLIC entry point reachable from\n // untyped host JS (`window.matterfact.setArtifactParams(...)`), where the\n // signature guarantees nothing.\n if (params == null || typeof params !== 'object' || Array.isArray(params)) {\n console.error(\n '[matterfact] setArtifactParams expects an object of param values',\n );\n return;\n }\n this.send({ type: 'host.artifactParams', params });\n }\n\n /** Tear down: stop listening and remove the host element. For the React wrapper's\n * unmount — the vanilla `<script>` loader lives for the page's lifetime and never\n * calls this. */\n destroy() {\n window.removeEventListener('message', this.onMessage);\n window.removeEventListener('resize', this.onViewportResize);\n this.hostEl?.remove();\n this.hostEl = null;\n this.iframe = null;\n this.shadow = null;\n this.ready = false;\n // context.ts's listeners/history-patch/ref-registry are module-global and\n // outlive this instance — stop() undoes exactly what start() installed. A no-op\n // if the eye was never loaded (chat never opened, so nothing was ever started).\n // This is the unmount-with-no-remount case; start()'s own stop()-first call\n // (see context.ts) is what protects a REMOUNT, which is why both exist.\n void this.context?.then((m) => m.stop());\n }\n}\n\n/**\n * Programmatic entry — mount the widget from a config object instead of the DOM\n * `<script data-*>`. This is what the React wrapper (`@matterfact/embed/react`) drives;\n * the vanilla loader below reads the script tag and calls this same class.\n */\nexport function mount(config: LoaderConfig): EmbedHost {\n const host = new EmbedHost(config);\n host.mount();\n // The <script> loader has no config object, so the host reaches this method the\n // same way it provides getPageContext — through window.matterfact. Declared on\n // Window (above) rather than reached through `window as unknown as {...}`: the\n // double assertion silenced the checker on BOTH the read and the write, so a\n // typo in either key was invisible, and it published the API as an untyped\n // `Record` to anyone reading this file for the host contract.\n window.matterfact = {\n ...(window.matterfact ?? {}),\n setArtifactParams: (params: ArtifactParams) =>\n host.setArtifactParams(params),\n };\n return host;\n}\n\nexport { EmbedHost, CHANNEL, readConfig };\n","/**\n * The auth context — `MatterfactAuthProvider` resolves a host's config props into\n * one plain object (`MatterfactConfig`) and hands it down via React context, so any\n * descendant can read it with `useMatterfactConfig()` instead of re-threading props.\n *\n * It also runs the host-auth BROKER: while mounted, it listens for\n * `mf.hostAuth.request` messages from child widget iframes and answers them with\n * `mf.hostAuth.grant` by calling the host's `getAuthToken`. This is what lets N\n * co-embedded widgets (e.g. an artifact iframe alongside the chat iframe) each\n * adopt the host's session without the host wiring auth per-child itself.\n */\n'use client';\n\nimport { createContext, useContext, useEffect, useMemo } from 'react';\n\nimport type { MatterfactEvent } from './protocol';\n\n// Duplicated on purpose, not imported: `loader.ts` and `react.tsx` each already\n// carry their own private copy of this same default rather than sharing one\n// export, and this file follows that existing convention.\nconst DEFAULT_ORIGIN = 'https://app.matterfact.com';\n\nexport interface MatterfactConfig {\n /** Publishable key (`pk_…`) identifying this embed app. Public, origin-scoped. */\n publishableKey: string;\n /** Origin serving the widget (the matterfact app). Always resolved — never undefined. */\n widgetOrigin: string;\n /** Host-auth passthrough, see `MatterfactAgentProps.getAuthToken` in `react.tsx`. */\n getAuthToken?: () => string | null | Promise<string | null>;\n theme: 'light' | 'dark' | 'auto';\n /** Host telemetry hook, see `MatterfactAgentProps.onEvent` in `react.tsx`. */\n onEvent?: (e: MatterfactEvent) => void;\n}\n\n/** Default `null` so a consumer with no ancestor `MatterfactAuthProvider` gets a\n * safe, explicit \"no config\" value instead of a made-up default. */\nexport const MatterfactAuthContext = createContext<MatterfactConfig | null>(null);\n\n/** The resolved config, or `null` outside a `MatterfactAuthProvider`. */\nexport function useMatterfactConfig(): MatterfactConfig | null {\n return useContext(MatterfactAuthContext);\n}\n\nexport interface MatterfactAuthProviderProps {\n /**\n * Publishable key (`pk_…`) identifying this embed app. Public and origin-scoped —\n * meant to sit in HTML. Every `MatterfactAgent`/`MatterfactArtifact`/`MatterfactDoc`\n * under this provider inherits it, so you set it once here rather than on each child.\n */\n publishableKey: string;\n /**\n * Origin serving the matterfact widgets (the app that hosts `/embed/*`). Defaults to\n * production. This is the ONE place to set it — every child inherits it, and the\n * `widgetOrigin` prop on `MatterfactAgent`/`MatterfactArtifact` is deprecated in\n * favour of setting it here.\n */\n widgetOrigin?: string;\n /**\n * HOST-AUTH PASSTHROUGH (trusted first-party embeds). Return a token this matterfact\n * deployment already trusts — the host app's Entra/Firebase token, whatever its auth\n * provider verifies — and every child iframe signs in SILENTLY with it, no separate\n * sign-in. The provider brokers it: when a child needs auth it asks the provider,\n * which calls this and hands the token back (over `postMessage`, pinned to the widget\n * origin). Called on demand so the token can be fresh; may be async. Omit for the\n * standard interactive sign-in.\n */\n getAuthToken?: () => string | null | Promise<string | null>;\n /** Colour theme applied to every child widget. Falls back to `'auto'`. */\n theme?: 'light' | 'dark' | 'auto';\n /**\n * Unified telemetry hook, fired for every embed lifecycle moment (`ready`, `auth`,\n * `error`, …) across the children — pipe the whole embed into your own observability\n * from one place. Never blocks or breaks a widget; a throw here is swallowed.\n */\n onEvent?: (e: MatterfactEvent) => void;\n /** The embed components (`MatterfactAgent`/`MatterfactArtifact`/`MatterfactDoc`) that\n * inherit this provider's config. */\n children: React.ReactNode;\n}\n\nexport function MatterfactAuthProvider({\n publishableKey,\n widgetOrigin,\n getAuthToken,\n theme,\n onEvent,\n children,\n}: MatterfactAuthProviderProps) {\n const resolvedWidgetOrigin = widgetOrigin || DEFAULT_ORIGIN;\n\n const value = useMemo<MatterfactConfig>(\n () => ({\n publishableKey,\n widgetOrigin: resolvedWidgetOrigin,\n getAuthToken,\n theme: theme || 'auto',\n onEvent,\n }),\n [publishableKey, resolvedWidgetOrigin, getAuthToken, theme, onEvent],\n );\n\n // The host-auth broker: answer `mf.hostAuth.request` from a child widget iframe\n // with `mf.hostAuth.grant`. No `getAuthToken` -> nothing to broker, no listener.\n useEffect(() => {\n if (typeof window === 'undefined' || !getAuthToken) return;\n // One in-flight `getAuthToken()` shared by every request until it settles, so\n // N children asking in the same tick fan out to a single call (they'd all\n // adopt the same session anyway) rather than N redundant token fetches.\n let inflight: Promise<string | null> | null = null;\n const onMessage = (e: MessageEvent) => {\n if (e.origin !== resolvedWidgetOrigin) return; // unforgeable origin pin\n if (!e.data || e.data.type !== 'mf.hostAuth.request') return;\n const source = e.source as WindowProxy | null;\n if (!source) return;\n // `getAuthToken()` can throw SYNCHRONOUSLY (evaluated before `Promise.resolve`,\n // so the `.catch` never sees it). Treat that the same as a null token — no grant\n // — instead of letting it propagate uncaught out of the message listener.\n try {\n inflight = inflight ?? Promise.resolve(getAuthToken()).catch(() => null);\n } catch {\n return;\n }\n const p = inflight;\n void p.then((token) => {\n if (p === inflight) inflight = null; // allow a fresh fetch next time\n if (typeof token === 'string' && token) {\n source.postMessage({ type: 'mf.hostAuth.grant', token }, resolvedWidgetOrigin);\n }\n });\n };\n window.addEventListener('message', onMessage);\n return () => window.removeEventListener('message', onMessage);\n }, [getAuthToken, resolvedWidgetOrigin]);\n\n return (\n <MatterfactAuthContext.Provider value={value}>\n {children}\n </MatterfactAuthContext.Provider>\n );\n}\n"],"mappings":"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","names":["useEffect","w","jsx","DEFAULT_ORIGIN","useEffect","snapshot"]}
|