{"version":3,"sources":["../../src/plugin/clientPush.ts","../../src/plugin/hostIdentity.ts","../../src/plugin/PluginRealtimeContext.ts","../../src/plugin/ExtensionRuntimeProvider.tsx","../../src/plugin/useMcpResource.ts","../../src/plugin/useMcpTool.ts","../../src/plugin/useMcpQuery.ts","../../src/plugin/define.ts","../../src/plugin/reconciler.ts","../../src/plugin/createRemoteRoot.ts","../../src/bridge/mcp-error.ts","../../src/plugin/createPortMcpTransport.ts","../../src/host/worker/bridge-envelopes.ts","../../src/plugin/bridge-client.ts","../../src/plugin/BridgeClientContext.ts","../../src/plugin/useBridgeTheme.ts","../../src/plugin/useBridgeLocale.ts","../../src/plugin/useFrontendSessionToken.ts","../../src/plugin/decodeJwtPayload.ts","../../src/plugin/useAuth.ts"],"names":["createContext","useContext","useRef","useEffect","useMemo","jsx","useState","useCallback","NODE_TYPE_TEXT","NODE_TYPE_ELEMENT","ReactReconciler","DefaultEventPriority","MUTATION_TYPE_REMOVE_CHILD","MUTATION_TYPE_INSERT_CHILD","MUTATION_TYPE_UPDATE_PROPERTY","MUTATION_TYPE_UPDATE_TEXT","ROOT_ID","BatchingRemoteConnection","LegacyRoot","nextId"],"mappings":"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","file":"index.cjs","sourcesContent":["import { createContext, useContext, useEffect, useRef } from \"react\";\n\n/**\n * A single client-push event delivered from the host to a plugin surface. The host\n * relays the plugin backend's `IClientPushPublisher` events over its realtime\n * channel (SignalR); the transport envelope's extension identity is bound by the\n * host at mount time, so the plugin sees only the event body.\n *\n * SCOPE + ORDERING: which channel/user/group an event concerns is carried INSIDE\n * `payloadJson` by the emitting plugin — the transport envelope intentionally has no\n * group field. Consumers therefore demultiplex + order by their own payload fields\n * (e.g. a per-channel sequence in the payload), NOT by {@link eventSequence}, which\n * is per-group at the host and would produce false gaps when multiple groups\n * multiplex over one connection.\n */\nexport interface ClientPushEvent\n{\n    /** Plugin-defined discriminator, e.g. `\"chatMessageReceived\"`. */\n    eventType: string;\n    /** Raw JSON payload authored by the plugin backend. */\n    payloadJson: string;\n    /**\n     * Host per-group monotonic sequence. Advisory only — do NOT use for\n     * cross-group gap detection (see the scope note above).\n     */\n    eventSequence: number;\n}\n\nexport interface ClientPushSubscribeOptions\n{\n    /**\n     * Opaque group names to enrol in (e.g. `\"chat:channel:{id}\"`). The host\n     * authorises each subscription via the plugin's `authorize-subscription` tool\n     * and enforces org/extension isolation — a plugin cannot subscribe outside its\n     * own extension + organisation.\n     */\n    groups: string[];\n    /** Called for each delivered (non-resync) event for the subscribed groups. */\n    onEvent: (event: ClientPushEvent) => void;\n    /**\n     * Called when the host signals a gap/resync for the subscribed groups (a\n     * dropped-event backpressure signal, or a reconnect). The consumer should\n     * re-fetch authoritative state (e.g. a delta/cold-load) rather than trusting\n     * incremental events.\n     */\n    onResync?: () => void;\n}\n\n/**\n * Host-provided channel for realtime server-push. The channel is already scoped to\n * the mounted surface's extension + organisation (bound by the host from the trusted\n * mount descriptor — a plugin CANNOT widen it), so {@link subscribe} takes only\n * opaque group names and returns an unsubscribe function.\n */\nexport interface ClientPushChannel\n{\n    subscribe(options: ClientPushSubscribeOptions): () => void;\n}\n\n/**\n * `null` = no host channel (standalone/mock, or a host that predates client-push) →\n * {@link useClientPushSubscription} is inert. Provided by\n * {@link ExtensionRuntimeProvider}'s optional `clientPush` prop.\n */\nexport const ClientPushContext = createContext<ClientPushChannel | null>(null);\n\nexport interface UseClientPushSubscriptionOptions\n{\n    /** Opaque groups to subscribe. Changing the SET re-subscribes; identity/order changes alone do not. */\n    groups: string[];\n    onEvent: (event: ClientPushEvent) => void;\n    onResync?: () => void;\n    /** Gate the subscription (e.g. until an id is known). Default `true`. */\n    enabled?: boolean;\n}\n\n/**\n * Subscribe a plugin surface to host client-push events for `groups`.\n *\n * Inert (no-op) when no host channel is present (standalone/mock), when `enabled` is\n * false, or when `groups` is empty. Re-subscribes when the group set changes and\n * unsubscribes on unmount. Callback identities are held in refs, so passing new\n * inline `onEvent`/`onResync` closures every render does NOT churn the subscription.\n */\nexport function useClientPushSubscription(options: UseClientPushSubscriptionOptions): void\n{\n    const { groups, onEvent, onResync, enabled = true } = options;\n    const channel = useContext(ClientPushContext);\n\n    const onEventRef = useRef(onEvent);\n    onEventRef.current = onEvent;\n    const onResyncRef = useRef(onResync);\n    onResyncRef.current = onResync;\n\n    // Normalise (dedupe + sort) so the effect re-runs only when the group SET actually\n    // changes — reordering the same groups, or passing a fresh array literal of the\n    // same set each render, must NOT churn the subscription.\n    const normalizedGroups = [...new Set(groups)].sort();\n    // JSON-encode (not space-join) so group names containing a delimiter can't alias\n    // distinct sets to the same key (e.g. [\"a b\"] vs [\"a\",\"b\"]).\n    const groupsKey = JSON.stringify(normalizedGroups);\n\n    useEffect(() =>\n    {\n        if (!channel || !enabled || normalizedGroups.length === 0)\n        {\n            return;\n        }\n        const unsubscribe = channel.subscribe({\n            groups: normalizedGroups,\n            onEvent: (event) => onEventRef.current(event),\n            onResync: () => onResyncRef.current?.(),\n        });\n        return unsubscribe;\n        // normalizedGroups is captured via groupsKey; callbacks via refs — intentionally excluded.\n        // eslint-disable-next-line react-hooks/exhaustive-deps\n    }, [channel, enabled, groupsKey]);\n}\n","/**\n * Host identity/permission seam for platform-react plugin surfaces (WI 5108, F-AUTH-SEAM).\n *\n * Mirrors the clientPush seam: the host binds a HostIdentity to the mounted surface and\n * provides it via ExtensionRuntimeProvider's `identity` prop; the plugin reads it with\n * `useHostIdentity`. Inert (returns null) when no host provides the context — standalone /\n * mock, or a host that predates this feature.\n *\n * SECURITY: this is presentation/UX data. The plugin BACKEND re-authorises every MCP call\n * from the server session; `permission` is NOT an authorization boundary. The host forwards\n * ONLY the caller's grant for the surface's own extension (least-privilege), never the full set.\n */\nimport { createContext, useContext } from \"react\";\n\n/** Current-user identity for a mounted plugin surface, bound by the host. */\nexport interface HostIdentityUser\n{\n    id: string;\n    firstName: string;\n    lastName: string;\n    fullName: string;\n    isExternal: boolean;\n}\n\n/** One resolved permission grant (bitMask over the plugin's PermissionMask bits). */\nexport interface HostPermission\n{\n    groupCode: string;\n    bitMask: number;\n}\n\n/** Host-provided identity context for the mounted surface. */\nexport interface HostIdentity\n{\n    /** null while host auth is still loading (see isLoading) OR when unauthenticated. */\n    user: HostIdentityUser | null;\n    /** true while the host's /auth/user resolution is in flight — disambiguates loading from unauthenticated. */\n    isLoading: boolean;\n    /** The mounted surface's OWN grant only, or null when the user has no grant for this extension. */\n    permission: HostPermission | null;\n    /** The mounted surface's own extension groupCode, host-bound from the trusted manifest. */\n    extensionGroupCode: string;\n    /**\n     * The active organisation id for the mounted surface, or null while host auth is loading /\n     * unauthenticated. Host-bound from the SPA's active-organisation context. Surfaces that scope\n     * realtime subscriptions or org-keyed queries read this (e.g. the chat client-push gate); it is\n     * NOT a security token — the plugin backend derives org from the server session independently.\n     */\n    organisationId: string | null;\n}\n\n/**\n * `null` = no host channel (standalone/mock, or a host that predates the identity seam) →\n * {@link useHostIdentity} returns null and the plugin falls back to its deny-by-default path.\n * Provided by {@link ExtensionRuntimeProvider}'s optional `identity` prop.\n */\nexport const HostIdentityContext = createContext<HostIdentity | null>(null);\n\n/** Returns the host identity for the mounted surface, or null when no host context is present. */\nexport function useHostIdentity(): HostIdentity | null\n{\n    return useContext(HostIdentityContext);\n}\n","import { createContext, useContext } from \"react\";\n\n/**\n * A host-supplied realtime subscription source for a plugin.\n *\n * The SDK keeps this intentionally dumb — it only calls `source.subscribe`.\n * All SignalR wiring, extensionId filtering, and connection lifecycle management\n * live host-side (Task 6 in coreconnect-web). This lets the SDK be tested with\n * a simple fake source.\n */\nexport interface PluginRealtimeSource\n{\n    /**\n     * Subscribe to notifications whose `typeCode` matches the given value.\n     *\n     * @param typeCode  The application-level event type code to filter on\n     *                  (e.g. `\"HelpdeskTicketCreated\"`). Filtering by\n     *                  extensionId is the host's responsibility.\n     * @param handler   Called with the raw notification payload whenever a\n     *                  matching notification arrives.\n     * @returns         An unsubscribe function. Calling it removes this handler.\n     */\n    subscribe(typeCode: string, handler: (payload: unknown) => void): () => void;\n}\n\n/**\n * React context carrying the plugin's active {@link PluginRealtimeSource}.\n *\n * `null` is the explicit \"not provided\" sentinel — hooks must treat null as a\n * clean no-op (dev/mock/no-connection) rather than an error.\n *\n * Provided by {@link ExtensionRuntimeProvider} when the host passes a\n * `realtime` prop; consumed by `usePluginRealtimeSource()`.\n */\nexport const PluginRealtimeContext = createContext<PluginRealtimeSource | null>(null);\n\n/**\n * Returns the {@link PluginRealtimeSource} from context, or `null` when none\n * is wired (dev/mock environments, unit tests that only care about MCP).\n *\n * Hooks built on top of this (e.g. `usePluginRealtime` in `plugin-ui`) should\n * skip their subscription entirely when this returns `null`.\n */\nexport function usePluginRealtimeSource(): PluginRealtimeSource | null\n{\n    return useContext(PluginRealtimeContext);\n}\n","import { createContext, useContext, useMemo, type ReactNode } from \"react\";\nimport type { McpTransport } from \"./transport\";\nimport { ClientPushContext, type ClientPushChannel } from \"./clientPush\";\nimport { HostIdentityContext, type HostIdentity } from \"./hostIdentity\";\nimport { PluginRealtimeContext, type PluginRealtimeSource } from \"./PluginRealtimeContext\";\n\n/**\n * React context carrying the {@link McpTransport} the plugin should use to\n * reach the host. `null` is the explicit \"not provided\" sentinel so the hooks\n * can disambiguate from a transport that was provided but is incidentally\n * falsy in some other dimension.\n */\nconst ExtensionRuntimeContext = createContext<McpTransport | null>(null);\n\nexport interface ExtensionRuntimeProviderProps\n{\n    transport: McpTransport;\n    /**\n     * Optional host realtime channel consumed by {@link useClientPushSubscription}.\n     * Absent (or `null`) in standalone/mock hosts and hosts that predate client-push,\n     * in which case the hook is inert. The host binds this channel to the mounted\n     * surface's trusted extension + organisation identity.\n     */\n    clientPush?: ClientPushChannel | null;\n    /**\n     * Optional host identity/permission context consumed by {@link useHostIdentity}.\n     * Absent (or `null`) in standalone/mock hosts and hosts that predate this seam,\n     * in which case the hook is inert. The host binds this to the mounted surface's\n     * trusted extension identity and forwards only that extension's own grant.\n     */\n    identity?: HostIdentity | null;\n    /**\n     * Optional realtime subscription source supplied by the host.\n     *\n     * When provided, descendant components can call `usePluginRealtimeSource()`\n     * to obtain it and subscribe to push notifications. When omitted (dev/mock\n     * environments or plugins that don't need realtime), the context defaults\n     * to `null` and consumers no-op cleanly.\n     */\n    realtime?: PluginRealtimeSource;\n    children?: ReactNode;\n}\n\n/**\n * Wrap a plugin's React tree so descendant {@link useMcpResource} and\n * {@link useMcpTool} calls resolve a default transport without having to\n * thread it through every component.\n *\n * Hooks still accept a per-call `transport` override, which takes precedence\n * over the context value — useful for tests and for plugins that want to\n * shard work across multiple hosts.\n */\nexport function ExtensionRuntimeProvider({ transport, clientPush = null, identity = null, realtime, children }: ExtensionRuntimeProviderProps): ReactNode\n{\n    // Memoise so swapping `children` doesn't churn the context identity.\n    const value = useMemo(() => transport, [transport]);\n    const push = useMemo(() => clientPush, [clientPush]);\n    const id = useMemo(() => identity, [identity]);\n    const realtimeValue = useMemo(() => realtime ?? null, [realtime]);\n    return (\n        <ExtensionRuntimeContext.Provider value={value}>\n            <ClientPushContext.Provider value={push}>\n                <HostIdentityContext.Provider value={id}>\n                    <PluginRealtimeContext.Provider value={realtimeValue}>\n                        {children}\n                    </PluginRealtimeContext.Provider>\n                </HostIdentityContext.Provider>\n            </ClientPushContext.Provider>\n        </ExtensionRuntimeContext.Provider>\n    );\n}\n\n/**\n * Internal helper used by the hooks. Returns the explicit override when\n * supplied, otherwise falls back to the context. Throws a deterministic\n * error if neither is available so misconfiguration fails loudly at the\n * first render rather than producing silent no-ops.\n */\nexport function useExtensionRuntimeTransport(override?: McpTransport): McpTransport\n{\n    const fromContext = useContext(ExtensionRuntimeContext);\n    const resolved = override ?? fromContext;\n    if (!resolved)\n    {\n        throw new Error(\n            \"No McpTransport available. Wrap your plugin in <ExtensionRuntimeProvider transport={...}> \"\n            + \"or pass `transport` directly to the hook.\",\n        );\n    }\n    return resolved;\n}\n\n/**\n * Non-throwing variant of {@link useExtensionRuntimeTransport}: returns the\n * resolved transport, or `null` when neither an override nor a provider is\n * present. For optional, fire-and-forget consumers (e.g. an auto-injected\n * org-format sync) that must degrade to a no-op rather than crash a surface that\n * renders without a host transport — a standalone/mock-mode run or an isolated\n * unit test.\n */\nexport function useOptionalExtensionRuntimeTransport(override?: McpTransport): McpTransport | null\n{\n    const fromContext = useContext(ExtensionRuntimeContext);\n    return override ?? fromContext ?? null;\n}\n","import { useCallback, useEffect, useRef, useState } from \"react\";\nimport { useExtensionRuntimeTransport } from \"./ExtensionRuntimeProvider\";\nimport type { McpTransport } from \"./transport\";\n\nexport interface UseMcpResourceOptions\n{\n    /**\n     * Override the transport resolved from {@link ExtensionRuntimeProvider}.\n     * Primarily intended for tests and advanced multi-host scenarios.\n     */\n    transport?: McpTransport;\n}\n\nexport interface UseMcpResourceResult<T>\n{\n    data: T | undefined;\n    error: Error | undefined;\n    loading: boolean;\n    /** Re-run the fetch against the current URI. Stable across renders. */\n    refetch: () => void;\n}\n\n/**\n * Subscribe to an MCP resource by URI.\n *\n * The hook fetches via the resolved {@link McpTransport} on mount and whenever\n * the URI changes. Each fetch is cancellable: when the component unmounts or\n * the URI changes, the in-flight call is aborted via an {@link AbortController}\n * so stale responses cannot overwrite later state.\n *\n * `refetch` re-runs the fetch against the latest URI. The returned callback is\n * stable across renders so it is safe to include in `useEffect` dependency\n * arrays.\n */\nexport function useMcpResource<T>(\n    uri: string,\n    opts?: UseMcpResourceOptions,\n): UseMcpResourceResult<T>\n{\n    const transport = useExtensionRuntimeTransport(opts?.transport);\n\n    const [data, setData] = useState<T | undefined>(undefined);\n    const [error, setError] = useState<Error | undefined>(undefined);\n    const [loading, setLoading] = useState<boolean>(true);\n\n    // Bump this counter to force a re-fetch from `refetch()`.\n    const [refetchTick, setRefetchTick] = useState(0);\n\n    // Track the active controller so `refetch` and unmount can abort it.\n    const controllerRef = useRef<AbortController | null>(null);\n\n    useEffect(() =>\n    {\n        const controller = new AbortController();\n        controllerRef.current?.abort();\n        controllerRef.current = controller;\n\n        let cancelled = false;\n        setLoading(true);\n        setError(undefined);\n\n        transport\n            .getResource<T>(uri, controller.signal)\n            .then((result) =>\n            {\n                if (cancelled || controller.signal.aborted)\n                {\n                    return;\n                }\n                setData(result.data);\n                setLoading(false);\n            })\n            .catch((err: unknown) =>\n            {\n                if (cancelled || controller.signal.aborted)\n                {\n                    return;\n                }\n                setError(err instanceof Error ? err : new Error(String(err)));\n                setLoading(false);\n            });\n\n        return () =>\n        {\n            cancelled = true;\n            controller.abort();\n        };\n    }, [uri, transport, refetchTick]);\n\n    const refetch = useCallback(() =>\n    {\n        setRefetchTick((tick) => tick + 1);\n    }, []);\n\n    return { data, error, loading, refetch };\n}\n","import { useCallback, useEffect, useRef, useState } from \"react\";\nimport { useExtensionRuntimeTransport } from \"./ExtensionRuntimeProvider\";\nimport type { McpTransport } from \"./transport\";\n\nexport interface UseMcpToolOptions\n{\n    /**\n     * Override the transport resolved from {@link ExtensionRuntimeProvider}.\n     * Primarily intended for tests and advanced multi-host scenarios.\n     */\n    transport?: McpTransport;\n}\n\nexport interface UseMcpToolResult<TReq, TRes>\n{\n    /**\n     * Invoke the tool. Each call gets a fresh {@link AbortController} that is\n     * aborted on unmount so unresolved promises cannot keep state alive after\n     * the component is gone.\n     */\n    invoke: (req: TReq) => Promise<TRes>;\n    loading: boolean;\n    error: Error | undefined;\n}\n\n/**\n * Hook returning a callback that invokes an MCP tool by name through the\n * resolved {@link McpTransport}.\n *\n * `loading` and `error` track the most recent in-flight invocation. Callers\n * may also `await` the returned promise directly — failures are both rejected\n * to the caller AND surfaced via `error` so component-level UI can react.\n *\n * `invoke` is a stable reference across renders for the same `toolName` and\n * transport, making it safe to use inside `useEffect`/`useCallback` deps.\n */\nexport function useMcpTool<TReq, TRes>(\n    toolName: string,\n    opts?: UseMcpToolOptions,\n): UseMcpToolResult<TReq, TRes>\n{\n    const transport = useExtensionRuntimeTransport(opts?.transport);\n\n    const [loading, setLoading] = useState(false);\n    const [error, setError] = useState<Error | undefined>(undefined);\n\n    // Track the latest controller so unmount can abort an in-flight call.\n    const controllerRef = useRef<AbortController | null>(null);\n    const mountedRef = useRef(true);\n\n    useEffect(() =>\n    {\n        mountedRef.current = true;\n        return () =>\n        {\n            mountedRef.current = false;\n            controllerRef.current?.abort();\n        };\n    }, []);\n\n    const invoke = useCallback(\n        async (req: TReq): Promise<TRes> =>\n        {\n            const controller = new AbortController();\n            controllerRef.current?.abort();\n            controllerRef.current = controller;\n\n            setLoading(true);\n            setError(undefined);\n\n            try\n            {\n                const result = await transport.invokeTool<TReq, TRes>(\n                    toolName,\n                    req,\n                    controller.signal,\n                );\n                if (mountedRef.current && !controller.signal.aborted)\n                {\n                    setLoading(false);\n                }\n                return result;\n            }\n            catch (err)\n            {\n                const wrapped = err instanceof Error ? err : new Error(String(err));\n                if (mountedRef.current && !controller.signal.aborted)\n                {\n                    setError(wrapped);\n                    setLoading(false);\n                }\n                throw wrapped;\n            }\n        },\n        [transport, toolName],\n    );\n\n    return { invoke, loading, error };\n}\n","import { useEffect, useState } from \"react\";\nimport { useMcpTool } from \"./useMcpTool\";\n\n/**\n * Result shape returned by {@link useMcpQuery}. Mirrors the host-app\n * `react-query`-style envelope so consumers can render loading / error /\n * data states declaratively. `refetch` is stable across renders.\n */\nexport interface UseMcpQueryResult<TRes>\n{\n    data: TRes | undefined;\n    loading: boolean;\n    error: Error | undefined;\n    /** Re-run the fetch against the current args. Stable across renders. */\n    refetch: () => void;\n}\n\n/**\n * Options for {@link useMcpQuery}.\n */\nexport interface UseMcpQueryOptions\n{\n    /**\n     * Gate the fetch on a guard. When false, the hook returns idle state and\n     * never invokes — important because hooks must be called unconditionally\n     * at the top of the component, so an `enabled` flag is the standard way\n     * to express \"fetch only when a selection is present\" without violating\n     * the rules of hooks. Defaults to `true`.\n     */\n    enabled?: boolean;\n}\n\n/**\n * Auto-fetch wrapper over the imperative {@link useMcpTool}. Fires the tool\n * call on mount AND whenever the serialised `args` change. Use for\n * read-shaped MCP tool calls that back a component's render data; for\n * mutations, call {@link useMcpTool} directly and trigger\n * `invoke(...)` from event handlers.\n *\n * Why this lives alongside {@link useMcpTool}: many plugin MCP tools are\n * effectively queries (`list-tasks`, `get-pending-approvals`, …) — paginated\n * lookups that change with filter inputs. The base hook's imperative\n * `invoke` signature is correct for mutations but ergonomically wrong for\n * reads, where every consumer ends up writing the same `useEffect` +\n * AbortController + cancellation-on-unmount boilerplate. This hook absorbs\n * that boilerplate once.\n *\n * @template TArgs The request shape sent to the tool.\n * @template TRes  The response shape the tool returns.\n */\nexport function useMcpQuery<TArgs, TRes>(\n    toolName: string,\n    args: TArgs,\n    options?: UseMcpQueryOptions,\n): UseMcpQueryResult<TRes>\n{\n    const enabled = options?.enabled ?? true;\n    const { invoke } = useMcpTool<TArgs, TRes>(toolName);\n    const [data, setData] = useState<TRes | undefined>(undefined);\n    const [loading, setLoading] = useState<boolean>(enabled);\n    const [error, setError] = useState<Error | undefined>(undefined);\n    const [tick, setTick] = useState(0);\n\n    // Stringify args for the effect's dependency key so an args object with\n    // new identity but unchanged contents does not force a refetch. The\n    // shape JSON-serialises trivially because tool args are wire-bound (JSON\n    // round-trips on the transport anyway).\n    const argsKey = JSON.stringify(args);\n\n    useEffect(() =>\n    {\n        if (!enabled)\n        {\n            setLoading(false);\n            return;\n        }\n        let cancelled = false;\n        setLoading(true);\n        invoke(args)\n            .then((result) =>\n            {\n                if (cancelled)\n                {\n                    return;\n                }\n                setData(result);\n                setError(undefined);\n                setLoading(false);\n            })\n            .catch((err: unknown) =>\n            {\n                if (cancelled)\n                {\n                    return;\n                }\n                setError(err instanceof Error ? err : new Error(String(err)));\n                setLoading(false);\n            });\n        return () =>\n        {\n            cancelled = true;\n        };\n        // `args` is intentionally tracked via `argsKey` (serialised contents)\n        // so reference-only changes don't refetch. eslint sees this as a\n        // missing dep but the effect is correct as-is.\n        // eslint-disable-next-line react-hooks/exhaustive-deps\n    }, [argsKey, tick, invoke, enabled]);\n\n    return {\n        data,\n        loading,\n        error,\n        refetch: () => setTick((t) => t + 1),\n    };\n}\n\n// ── Response-envelope helpers ────────────────────────────────────\n//\n// MCP tool responses across the platform's BE conventions arrive in either\n// `{ items: T[] }` (raw lists) or `{ rows: T[] }` (paginated envelopes). The\n// envelope shape is a BE-side detail plugin authors don't want to encode at\n// every read site. {@link ItemsResponse} types the union and\n// {@link unwrapItems} normalises to a plain array.\n\n/**\n * Envelope shape that covers both `{ items: T[] }` and `{ rows: T[] }`\n * conventions emitted by platform / plugin MCP read tools. Optional both\n * sides so empty responses still type-check.\n */\nexport interface ItemsResponse<T>\n{\n    items?: T[];\n    rows?: T[];\n}\n\n/**\n * Normalise an {@link ItemsResponse} envelope to a plain array.\n * Returns `[]` when the response is `undefined` (typical pre-first-fetch\n * state) or when neither field is populated.\n */\nexport function unwrapItems<T>(response: ItemsResponse<T> | undefined): T[]\n{\n    return response?.items ?? response?.rows ?? [];\n}\n","import type { RenderMode, SduiNode } from \"@ethisyscore/protocol\";\n\n/**\n * Configuration accepted by {@link defineDeclarativePlugin}.\n *\n * A Contract A (host-rendered) plugin contributes a map of resource URIs to\n * declarative SDUI trees. The host fetches a resource by URI and renders it\n * against the v1 vocabulary.\n */\nexport interface DeclarativePluginConfig\n{\n    resources: Record<string, SduiNode>;\n}\n\n/**\n * Configuration accepted by {@link defineEthisysPlugin}.\n *\n * `renderMode` is constrained to the protocol's {@link RenderMode} enum so\n * authoring mistakes (`\"iframe\"`, `\"webview\"`, …) are caught at compile time.\n * `mount` is generic so Contract B remote-runtime authors can attach their\n * own mount surface without losing type information at the call site.\n */\nexport interface EthisysPluginConfig<TMount>\n{\n    renderMode: RenderMode;\n    mount?: TMount;\n}\n\n/**\n * Author-facing identity helper for a Contract A (host-rendered) declarative\n * plugin definition.\n *\n * The helper performs no runtime work — it exists purely so authoring sites\n * receive precise type inference and editor tooling against the protocol's\n * {@link SduiNode} contract. The returned value is the exact same reference\n * the caller passed in.\n */\nexport const defineDeclarativePlugin = (\n    cfg: DeclarativePluginConfig,\n): DeclarativePluginConfig => cfg;\n\n/**\n * Author-facing identity helper for a generic EthisysCore plugin definition.\n *\n * The helper performs no runtime work. It constrains `renderMode` to the\n * protocol's {@link RenderMode} enum and preserves the inferred type of an\n * optional `mount` surface, so Contract B authors can pass through their own\n * mount object without widening it to `unknown`.\n */\nexport const defineEthisysPlugin = <TMount>(\n    cfg: EthisysPluginConfig<TMount>,\n): EthisysPluginConfig<TMount> => cfg;\n","import ReactReconciler from \"react-reconciler\";\nimport {\n  ConcurrentRoot,\n  LegacyRoot,\n  DefaultEventPriority,\n} from \"react-reconciler/constants.js\";\nimport {\n  MUTATION_TYPE_INSERT_CHILD,\n  MUTATION_TYPE_REMOVE_CHILD,\n  MUTATION_TYPE_UPDATE_TEXT,\n  MUTATION_TYPE_UPDATE_PROPERTY,\n  NODE_TYPE_ELEMENT,\n  NODE_TYPE_TEXT,\n  ROOT_ID,\n  type RemoteConnection,\n  type RemoteNodeSerialization,\n} from \"@remote-dom/core\";\n\n// ── Internal node representation ───────────────────────────────────\n//\n// Lightweight JS-object stand-ins for the Remote DOM tree. Each node carries\n// a stable string id that the host uses to address mutations. We do NOT use\n// `@remote-dom/core`'s `RemoteElement` Custom Element classes because:\n//   1. They require `customElements` registration which the worker realm\n//      does not have by default (would force every plugin to bundle a DOM\n//      polyfill).\n//   2. The plain-object form serialises identically over the connection's\n//      `RemoteNodeSerialization` contract — the host's `RemoteReceiver`\n//      reconstructs proper elements on its side.\n\ninterface RemoteElementInstance {\n  readonly id: string;\n  readonly kind: \"element\";\n  readonly type: string;\n  properties: Record<string, unknown>;\n  eventListeners: Record<string, (...args: unknown[]) => unknown>;\n  children: RemoteNode[];\n  parent: RemoteParent | null;\n}\n\ninterface RemoteTextInstance {\n  readonly id: string;\n  readonly kind: \"text\";\n  text: string;\n  parent: RemoteParent | null;\n}\n\ninterface RemoteRootContainer {\n  readonly id: typeof ROOT_ID;\n  readonly kind: \"root\";\n  children: RemoteNode[];\n  readonly connection: RemoteConnection;\n}\n\ntype RemoteNode = RemoteElementInstance | RemoteTextInstance;\ntype RemoteParent = RemoteElementInstance | RemoteRootContainer;\n\nlet nextId = 0;\nfunction newId(): string {\n  nextId += 1;\n  return `r-${nextId.toString(36)}`;\n}\n\nfunction serialize(node: RemoteNode): RemoteNodeSerialization {\n  if (node.kind === \"text\") {\n    return { id: node.id, type: NODE_TYPE_TEXT, data: node.text };\n  }\n  // Event-listener functions are intentionally NOT included in the\n  // serialised payload — they can't be structured-cloned across a\n  // MessagePort and would throw `DataCloneError` on the first commit.\n  // Listeners are retained locally on the RemoteElementInstance for the\n  // future `ethisys:remotedom:call` round-trip (see createRemoteRoot.ts's\n  // top-of-file \"What's NOT plumbed yet\" note); until that channel lands,\n  // worker-side `onClick`-style handlers are no-ops on the host. The host\n  // RemoteReceiver tolerates the missing field — `eventListeners` is\n  // optional on `RemoteNodeSerialization`.\n  return {\n    id: node.id,\n    type: NODE_TYPE_ELEMENT,\n    element: node.type,\n    properties: node.properties,\n    children: node.children.map(serialize),\n  };\n}\n\nfunction indexInParent(parent: RemoteParent, child: RemoteNode): number {\n  return parent.children.indexOf(child);\n}\n\n// ── Prop classification ────────────────────────────────────────────\n//\n// Children (the JSX content) are handled by the reconciler tree walk;\n// `ref` and `key` are React-internal. Everything else is forwarded as a\n// remote property OR an event listener. Convention: any prop starting with\n// `on` followed by an uppercase letter is treated as an event listener.\n\nfunction isEventListenerProp(key: string): boolean {\n  return key.length > 2 && key.startsWith(\"on\") && key[2] >= \"A\" && key[2] <= \"Z\";\n}\n\nfunction eventNameFromProp(propName: string): string {\n  // onClick → click. The host's RemoteReceiver expects lowercase event names.\n  return propName.slice(2).toLowerCase();\n}\n\nfunction isReservedProp(key: string): boolean {\n  return key === \"children\" || key === \"key\" || key === \"ref\";\n}\n\nfunction classifyProps(props: Record<string, unknown>): {\n  properties: Record<string, unknown>;\n  eventListeners: Record<string, (...args: unknown[]) => unknown>;\n} {\n  const properties: Record<string, unknown> = {};\n  const eventListeners: Record<string, (...args: unknown[]) => unknown> = {};\n  for (const key of Object.keys(props)) {\n    if (isReservedProp(key)) {\n      continue;\n    }\n    const value = props[key];\n    if (isEventListenerProp(key) && typeof value === \"function\") {\n      eventListeners[eventNameFromProp(key)] = value as (...args: unknown[]) => unknown;\n    } else if (value !== undefined) {\n      properties[key] = value;\n    }\n  }\n  return { properties, eventListeners };\n}\n\n// ── HostConfig factory ─────────────────────────────────────────────\n\n/**\n * Build the react-reconciler instance bound to the supplied `RemoteConnection`.\n * Every mutation the reconciler applies translates into a single\n * `connection.mutate([record])` call; the connection forwards the record over\n * the `MessagePort` set up by `createRemoteRoot`.\n */\nexport function createReactReconciler(connection: RemoteConnection): {\n  reconciler: ReactReconciler.Reconciler<\n    RemoteParent,\n    RemoteElementInstance,\n    RemoteTextInstance,\n    never,\n    RemoteElementInstance\n  >;\n  createContainer: () => RemoteRootContainer;\n} {\n  const reconciler = ReactReconciler<\n    string, // type\n    Record<string, unknown>, // props\n    RemoteParent, // container\n    RemoteElementInstance, // instance\n    RemoteTextInstance, // text instance\n    never, // suspense instance\n    never, // hydratable instance\n    RemoteElementInstance, // public instance (returned from ref)\n    object, // host context\n    string[], // update payload (list of changed prop names)\n    never, // child set (persistent mode only)\n    ReturnType<typeof setTimeout>, // timeout handle\n    -1 // no timeout sentinel\n  >({\n    supportsMutation: true,\n    supportsPersistence: false,\n    supportsHydration: false,\n    isPrimaryRenderer: true,\n    noTimeout: -1,\n    scheduleTimeout: setTimeout,\n    cancelTimeout: clearTimeout,\n    getCurrentEventPriority: () => DefaultEventPriority,\n    getRootHostContext: () => ({}),\n    getChildHostContext: () => ({}),\n    prepareForCommit: () => null,\n    resetAfterCommit: () => {},\n    preparePortalMount: () => {},\n    shouldSetTextContent: (_type, props) =>\n      typeof props.children === \"string\" || typeof props.children === \"number\",\n    getPublicInstance: (instance) => instance as RemoteElementInstance,\n    clearContainer: (container) => {\n      // Remove every direct child from the root. The host receiver mirrors\n      // each removal on its side.\n      while (container.children.length > 0) {\n        connection.mutate([[MUTATION_TYPE_REMOVE_CHILD, container.id, 0]]);\n        container.children.shift();\n      }\n    },\n\n    // ── createInstance / createTextInstance ────────────────────────\n    createInstance: (type, props): RemoteElementInstance => {\n      const { properties, eventListeners } = classifyProps(props);\n      return {\n        id: newId(),\n        kind: \"element\",\n        type,\n        properties,\n        eventListeners,\n        children: [],\n        parent: null,\n      };\n    },\n    createTextInstance: (text): RemoteTextInstance => ({\n      id: newId(),\n      kind: \"text\",\n      text,\n      parent: null,\n    }),\n\n    // ── Initial mount: build the subtree before attaching to root ──\n    appendInitialChild: (parent, child) => {\n      parent.children.push(child);\n      child.parent = parent;\n    },\n    finalizeInitialChildren: () => false,\n\n    // ── Container-level mutations (root attach/detach) ─────────────\n    appendChildToContainer: (container, child) => {\n      const index = container.children.length;\n      container.children.push(child);\n      child.parent = container;\n      connection.mutate([\n        [MUTATION_TYPE_INSERT_CHILD, container.id, serialize(child), index],\n      ]);\n    },\n    removeChildFromContainer: (container, child) => {\n      const index = indexInParent(container, child);\n      if (index < 0) {\n        return;\n      }\n      container.children.splice(index, 1);\n      child.parent = null;\n      connection.mutate([[MUTATION_TYPE_REMOVE_CHILD, container.id, index]]);\n    },\n    insertInContainerBefore: (container, child, beforeChild) => {\n      const beforeIndex = indexInParent(container, beforeChild);\n      const insertAt = beforeIndex < 0 ? container.children.length : beforeIndex;\n      container.children.splice(insertAt, 0, child);\n      child.parent = container;\n      connection.mutate([\n        [MUTATION_TYPE_INSERT_CHILD, container.id, serialize(child), insertAt],\n      ]);\n    },\n\n    // ── Sibling-level mutations (incremental tree updates) ─────────\n    appendChild: (parent, child) => {\n      const index = parent.children.length;\n      parent.children.push(child);\n      child.parent = parent;\n      connection.mutate([\n        [MUTATION_TYPE_INSERT_CHILD, parent.id, serialize(child), index],\n      ]);\n    },\n    removeChild: (parent, child) => {\n      const index = indexInParent(parent, child);\n      if (index < 0) {\n        return;\n      }\n      parent.children.splice(index, 1);\n      child.parent = null;\n      connection.mutate([[MUTATION_TYPE_REMOVE_CHILD, parent.id, index]]);\n    },\n    insertBefore: (parent, child, beforeChild) => {\n      const beforeIndex = indexInParent(parent, beforeChild);\n      const insertAt = beforeIndex < 0 ? parent.children.length : beforeIndex;\n      parent.children.splice(insertAt, 0, child);\n      child.parent = parent;\n      connection.mutate([\n        [MUTATION_TYPE_INSERT_CHILD, parent.id, serialize(child), insertAt],\n      ]);\n    },\n\n    // ── Update path ────────────────────────────────────────────────\n    prepareUpdate: (_instance, _type, oldProps, newProps): string[] | null => {\n      const changed: string[] = [];\n      const allKeys = new Set<string>([\n        ...Object.keys(oldProps),\n        ...Object.keys(newProps),\n      ]);\n      for (const key of allKeys) {\n        if (isReservedProp(key)) {\n          continue;\n        }\n        if (oldProps[key] !== newProps[key]) {\n          changed.push(key);\n        }\n      }\n      return changed.length > 0 ? changed : null;\n    },\n    commitUpdate: (instance, updatePayload, _type, _oldProps, newProps) => {\n      if (updatePayload === null) {\n        return;\n      }\n      for (const key of updatePayload) {\n        const value = newProps[key];\n        if (isEventListenerProp(key) && (typeof value === \"function\" || value === undefined)) {\n          const eventName = eventNameFromProp(key);\n          if (typeof value === \"function\") {\n            instance.eventListeners[eventName] = value as (...args: unknown[]) => unknown;\n          } else {\n            delete instance.eventListeners[eventName];\n          }\n          // Functions can't cross a MessagePort via structured clone, so the\n          // wire payload only signals whether the listener is now present or\n          // cleared — `true` for \"listener exists, wire it on the host once\n          // the call channel lands\", `null` for \"listener removed\". See the\n          // serialize() comment for the broader Phase 1 limitation.\n          connection.mutate([\n            [\n              MUTATION_TYPE_UPDATE_PROPERTY,\n              instance.id,\n              eventName,\n              typeof value === \"function\" ? true : null,\n              3 /* event listener */,\n            ],\n          ]);\n        } else {\n          if (value === undefined) {\n            delete instance.properties[key];\n          } else {\n            instance.properties[key] = value;\n          }\n          connection.mutate([\n            [MUTATION_TYPE_UPDATE_PROPERTY, instance.id, key, value],\n          ]);\n        }\n      }\n    },\n    commitTextUpdate: (textInstance, _oldText, newText) => {\n      textInstance.text = newText;\n      connection.mutate([[MUTATION_TYPE_UPDATE_TEXT, textInstance.id, newText]]);\n    },\n\n    // ── No-op methods required by the HostConfig contract ──────────\n    detachDeletedInstance: () => {},\n    beforeActiveInstanceBlur: () => {},\n    afterActiveInstanceBlur: () => {},\n    prepareScopeUpdate: () => {},\n    getInstanceFromScope: () => null,\n    getInstanceFromNode: () => null,\n\n    // Microtask scheduling — use the platform's default queueing.\n    supportsMicrotasks: true,\n    scheduleMicrotask:\n      typeof queueMicrotask === \"function\"\n        ? queueMicrotask\n        : (callback) => Promise.resolve().then(callback),\n  });\n\n  function createContainer(): RemoteRootContainer {\n    return { id: ROOT_ID, kind: \"root\", children: [], connection };\n  }\n\n  return { reconciler, createContainer };\n}\n\nexport { ConcurrentRoot, LegacyRoot };\nexport type { RemoteRootContainer, RemoteElementInstance, RemoteTextInstance };\n","/**\n * Contract B (worker remote-runtime) plugin-side React root.\n *\n * # The problem this solves\n *\n * A Contract B plugin runs in a sandboxed Web Worker — no `document`, no\n * `window`, no DOM. The host owns rendering: the worker constructs a\n * `RemoteElement` tree via `@remote-dom/core`, mutations forward over a\n * `MessagePort`, the host receives them and commits them to a real React tree\n * (see `coreconnect-web/src/extensions/runtime/WorkerSurfaceMount.tsx` for the\n * receiver side).\n *\n * Until now plugin authors had to hand-author the `RemoteElement` construction\n * + mutation forwarding manually. `@remote-dom/react` ships only the host-side\n * primitives (`createRemoteComponent`, `RemoteRootRenderer`, etc.) — there is\n * no worker-side React reconciler in the package. This helper provides one.\n *\n * # What this is\n *\n * The public surface (`createRemoteRoot(port, options)`) plus a working\n * `react-reconciler` HostConfig that commits to a Remote DOM tree and\n * forwards mutation records over the `MessagePort` to the host receiver.\n * Authors call `root.render(<App />)` and the host's `WorkerSurfaceMount`\n * sees the result.\n *\n * # What's NOT plumbed yet (Phase 1 limitation)\n *\n * Event-listener round-trip. The host transport currently has no\n * `ethisys:remotedom:call` channel — `WorkerRemoteDomTransport` only\n * forwards `ethisys:remotedom` payloads from worker → host, never the\n * other direction. So a worker-side `onClick={() => ...}` is **registered\n * locally** but the host can't call it. The reconciler retains every\n * listener on its in-memory `RemoteElementInstance` so the wiring is ready\n * the moment the call channel lands; until then, plugin authors should\n * keep interactive surfaces on Contract A.\n *\n * # The API\n *\n * ```ts\n * import { createRemoteRoot } from \"@ethisyscore/extension-runtime/plugin\";\n *\n * export async function activate(port: MessagePort): Promise<void> {\n *   const root = createRemoteRoot(port);\n *   root.render(<App />);\n * }\n * ```\n *\n * `<App />` is plain React. Any component shipped by the host's frozen\n * import-map allowlist (the closed Contract B primitive vocabulary —\n * `Button`, `DataTable`, `Form`, `Card`, `Tabs`, `Select`, `Alert`, etc.)\n * renders. The reconciler walks the React tree and commits to a\n * `RemoteRootElement`; mutation records forward over the port; the host's\n * `RemoteReceiver` commits the result into the real React tree.\n *\n * # Wire shape\n *\n * Every reconciler mutation is posted as\n * `{ type: \"ethisys:remotedom\", payload: RemoteMutationRecord[] }` to match\n * `WorkerRemoteDomTransport`'s `RemoteDomMessage` contract — the host\n * extracts `payload` and feeds it straight into `receiver.mutate(records)`.\n * `BatchingRemoteConnection` (controlled via `options.batchMutations`)\n * collapses contiguous mutations into a single port post per commit.\n */\nimport type { RemoteConnection, RemoteMutationRecord } from \"@remote-dom/core\";\nimport { BatchingRemoteConnection } from \"@remote-dom/core/elements\";\nimport type { ReactNode } from \"react\";\nimport {\n  createReactReconciler,\n  LegacyRoot,\n  type RemoteRootContainer,\n} from \"./reconciler\";\n\n/**\n * Options for {@link createRemoteRoot}. Reserved for forward compatibility —\n * Phase 1 ships with zero required options. Adding fields here is additive;\n * removing them is a breaking change.\n */\nexport interface CreateRemoteRootOptions\n{\n    /**\n     * When supplied, the reconciler batches contiguous mutations into a single\n     * `port.postMessage` rather than firing one per mutation. Default `true`\n     * — measurably reduces host-side commit cost on the first render of a\n     * non-trivial tree.\n     */\n    batchMutations?: boolean;\n\n    /**\n     * Override the connection factory. Reserved for tests; production callers\n     * never pass this. The factory's contract is \"build a RemoteConnection\n     * that forwards mutations over the supplied port\"; the default uses\n     * `createRemoteConnection` from `@remote-dom/core`.\n     */\n    connectionFactory?: (port: MessagePort) => RemoteConnection;\n}\n\n/**\n * Plugin-side React root. Mirrors the shape of `ReactDOMClient.Root` so\n * authors familiar with `createRoot().render(...)` find the same API on the\n * worker side.\n */\nexport interface RemoteRoot\n{\n    /**\n     * Render a React element tree against the worker's `RemoteRootElement`.\n     * The reconciler commits to the root, the mutation observer forwards\n     * mutations over the port, the host re-renders. Idempotent — calling\n     * `render` twice with the same element is fine; the reconciler dedupes.\n     */\n    render(element: ReactNode): void;\n\n    /**\n     * Tear down the reconciled tree and stop forwarding mutations. Authors\n     * should call this from a `Symbol.dispose` or equivalent when the worker\n     * is shutting down — leaking the reconciler holds the `MessagePort` open\n     * and prevents the worker from being collected.\n     */\n    unmount(): void;\n}\n\n/**\n * Construct a plugin-side React root that commits to a `RemoteRootElement`\n * and forwards mutations over the supplied `MessagePort`.\n *\n * **API stability:** the function signature is stable for Phase 1. The\n * options bag is forward-compatible (additive only).\n *\n * **Implementation status:** the connection + root construction lands in this\n * commit. The `react-reconciler` `HostConfig` is a scaffold — `render()`\n * throws a structured error directing authors to the W1A tracking issue.\n * Authors should treat this commit as \"the API is locked, the reconciler is\n * being authored.\" See follow-on commits on the `feature/contract-b-create-remote-root-w1a`\n * branch.\n */\nexport function createRemoteRoot(\n    port: MessagePort,\n    options: CreateRemoteRootOptions = {},\n): RemoteRoot\n{\n    if (port === undefined || port === null)\n    {\n        throw new TypeError(\n            \"[createRemoteRoot] `port` is required. Pass the MessagePort the host transferred via the worker's `activate(port)` entry point.\",\n        );\n    }\n\n    // Build the connection that forwards Remote DOM mutations over the port.\n    // The default factory uses `@remote-dom/core`'s `createRemoteConnection`;\n    // tests inject their own factory via `options.connectionFactory`.\n    const baseConnection = (options.connectionFactory ?? defaultConnectionFactory)(port);\n\n    const connection: RemoteConnection = options.batchMutations === false\n        ? baseConnection\n        : new BatchingRemoteConnection(baseConnection);\n\n    // W1A — lazy reconciler + container construction. The reconciler is built\n    // once on first render(); subsequent calls reuse the same container so React\n    // commits behave like ReactDOM.createRoot(...).render(...) — call render\n    // multiple times to update; call unmount to tear down.\n    const { reconciler, createContainer } = createReactReconciler(connection);\n    let container: RemoteRootContainer | null = null;\n    let fiberRoot: ReturnType<typeof reconciler.createContainer> | null = null;\n\n    return {\n        render(element: ReactNode): void\n        {\n            if (container === null)\n            {\n                container = createContainer();\n                fiberRoot = reconciler.createContainer(\n                    container,\n                    LegacyRoot,\n                    null, // hydration callbacks\n                    false, // isStrictMode\n                    null, // concurrentUpdatesByDefaultOverride\n                    \"\", // identifierPrefix\n                    (error: Error) =>\n                    {\n                        // The host's SurfaceErrorBoundary catches render-time\n                        // failures via the connection's call() path; mirroring\n                        // them locally as a console.error is the best we can do\n                        // from inside the worker.\n                        // eslint-disable-next-line no-console -- worker console is pumped to the host logger.\n                        console.error(\"[createRemoteRoot] render error\", error);\n                    },\n                    null, // transitionCallbacks\n                );\n            }\n            reconciler.updateContainer(element, fiberRoot!, null, null);\n        },\n        unmount(): void\n        {\n            if (fiberRoot !== null)\n            {\n                reconciler.updateContainer(null, fiberRoot, null, null);\n                fiberRoot = null;\n                container = null;\n            }\n            // The connection itself does NOT close the port — plugins may have\n            // side-channel consumers. Authors that want a hard tear-down call\n            // `port.close()` themselves after unmount().\n        },\n    };\n}\n\n// ── Internals ──────────────────────────────────────────────────────────\n\n/**\n * Default factory: build a `RemoteConnection` that posts mutation records\n * as the `payload` of a single `ethisys:remotedom` port message — the\n * wire shape `WorkerRemoteDomTransport`'s `RemoteDomMessage` declares\n * (see `host/worker/transport.ts`'s `handlePortMessage`: it forwards\n * `(message as RemoteDomMessage).payload` straight into the host's\n * `RemoteReceiver.mutate(records)` call).\n *\n * Going through `createRemoteConnection` (which fires per-op callbacks)\n * was the wrong abstraction for this wire — the host expects the raw\n * record list under `payload`, not bespoke `op`-suffixed envelopes. By\n * implementing the connection directly we hand the reconciler's\n * mutation records straight to the host receiver without translation.\n *\n * Wrapping in `BatchingRemoteConnection` (controlled via\n * `options.batchMutations`) collapses contiguous mutation calls into a\n * single `port.postMessage` payload, which is the difference between \"one\n * postMessage per appended child\" and \"one postMessage per React commit\"\n * — measurably cheaper for the first paint of a non-trivial tree.\n */\nfunction defaultConnectionFactory(port: MessagePort): RemoteConnection\n{\n    return {\n        mutate(records: readonly RemoteMutationRecord[]): void\n        {\n            // Snapshot to a plain array so the host receiver isn't handed a\n            // reference into the reconciler's internal buffer (the BatchingRemoteConnection\n            // wrapper recycles its own array between flushes).\n            port.postMessage({ type: \"ethisys:remotedom\", payload: records.slice() });\n        },\n        call(_id, _method, ..._args): unknown\n        {\n            // Worker → host method invocation. Reserved for the future\n            // `ethisys:remotedom:call` channel — `WorkerRemoteDomTransport`\n            // doesn't yet handle a `call`-shaped reply path, so event-\n            // listener round-trip is still on the Phase 1 follow-on list\n            // (see this file's top-of-file \"What's NOT plumbed yet\" note).\n            return undefined;\n        },\n    };\n}\n\n","/**\n * A machine-readable classification for an MCP call that failed, carried across the host/plugin\n * bridge alongside the human-readable message.\n *\n * ## Why this exists\n *\n * The bridge used to reduce every failure to `err.message`, a bare string. A plugin therefore could\n * not tell \"you lack permission for this tool\" from \"the host is unreachable\" without matching on\n * host error prose, and the practical consequence was that plugins swallowed *all* failures into a\n * successful empty result to keep a permission denial from looking like a crash. That turns a\n * transient outage into \"you have no connectors\" - a false statement about the user's data, and one\n * that also suppresses the query layer's retry.\n *\n * A closed vocabulary lets the plugin branch on the one case it wants to tolerate and rethrow the\n * rest.\n *\n * ## Deliberately small, and deliberately not HTTP\n *\n * These are the distinctions a *caller* acts on differently, not a mirror of any status enum. Codes\n * that would prompt the same handling are folded together, because a vocabulary nobody can apply is\n * just a wider surface to get wrong. `unavailable` and `timeout` stay separate only because retry\n * policy differs between them.\n */\nexport type McpErrorCode =\n    /** The caller is not authenticated, or its token expired. Re-authentication may fix it. */\n    | \"unauthorized\"\n    /** Authenticated, but not permitted this tool or resource. Retrying will not help. */\n    | \"forbidden\"\n    /** The tool, resource, or addressed entity does not exist. */\n    | \"not_found\"\n    /** The arguments were rejected. A caller bug, or stale client-side validation. */\n    | \"invalid_request\"\n    /** A concurrency or state conflict - a row version, or a duplicate. */\n    | \"conflict\"\n    /** Throttled. Retry later, with backoff. */\n    | \"rate_limited\"\n    /** The call did not complete in time. Safe to retry only if the operation is idempotent. */\n    | \"timeout\"\n    /** The host or an upstream dependency is down. Retryable. */\n    | \"unavailable\"\n    /** Anything else, including an unclassifiable failure. The default - never a claim. */\n    | \"internal\";\n\n/**\n * Every valid {@link McpErrorCode}. Used to validate a code arriving over the wire: an unknown\n * string is downgraded rather than trusted, so a newer host cannot make an older plugin branch on a\n * code it has never heard of.\n */\nexport const MCP_ERROR_CODES: readonly McpErrorCode[] = [\n    \"unauthorized\",\n    \"forbidden\",\n    \"not_found\",\n    \"invalid_request\",\n    \"conflict\",\n    \"rate_limited\",\n    \"timeout\",\n    \"unavailable\",\n    \"internal\",\n];\n\n/** True when `value` is a code this build understands. */\nexport function isMcpErrorCode(value: unknown): value is McpErrorCode\n{\n    return typeof value === \"string\"\n        && (MCP_ERROR_CODES as readonly string[]).includes(value);\n}\n\n/**\n * Codes a caller can retry without changing anything about the request. Exposed so retry policy is\n * decided once here rather than re-derived, subtly differently, at each call site.\n *\n * `unauthorized` is absent on purpose: a retry only helps once something else has refreshed the\n * token, which is a different action from retrying.\n */\nconst RETRYABLE: readonly McpErrorCode[] = [\"rate_limited\", \"timeout\", \"unavailable\"];\n\n/** True when the failure is worth retrying as-is. */\nexport function isRetryableMcpErrorCode(code: McpErrorCode): boolean\n{\n    return RETRYABLE.includes(code);\n}\n\n/**\n * An MCP call rejected by the host, carrying its {@link McpErrorCode}.\n *\n * `name` stays `\"McpHostError\"` - the string the bridge has always set - so code that matches on the\n * name keeps working. Prefer {@link isMcpToolError}, which survives a name change and works across\n * realm boundaries where `instanceof` does not.\n */\nexport class McpToolError extends Error\n{\n    /** Machine-readable classification. `internal` when the host sent none. */\n    public readonly code: McpErrorCode;\n\n    /**\n     * Marks the instance for {@link isMcpToolError}. A structural marker rather than a prototype\n     * check because the error can be constructed in one bundle and inspected in another, where\n     * `instanceof` compares two different class objects and answers false.\n     */\n    public readonly isMcpToolError = true as const;\n\n    public constructor(message: string, code: McpErrorCode = \"internal\")\n    {\n        super(message);\n        this.name = \"McpHostError\";\n        this.code = code;\n    }\n\n    /** True when this failure is worth retrying unchanged. */\n    public get retryable(): boolean\n    {\n        return isRetryableMcpErrorCode(this.code);\n    }\n}\n\n/** True when `value` is an {@link McpToolError}, including one from another bundle. */\nexport function isMcpToolError(value: unknown): value is McpToolError\n{\n    return value instanceof McpToolError\n        || (value !== null\n            && typeof value === \"object\"\n            && (value as { isMcpToolError?: unknown }).isMcpToolError === true\n            && isMcpErrorCode((value as { code?: unknown }).code));\n}\n\n/**\n * Maps an HTTP status onto a code. Split out because host MCP clients are commonly HTTP clients, so\n * a status is the classification most of them already have.\n *\n * Unrecognised statuses (including every 2xx and 3xx, which should not be reaching an error path)\n * yield `internal` rather than a guess.\n */\nexport function mcpErrorCodeFromHttpStatus(status: number): McpErrorCode\n{\n    switch (status)\n    {\n        case 401:\n            return \"unauthorized\";\n        case 403:\n            return \"forbidden\";\n        case 404:\n            return \"not_found\";\n        case 400:\n        case 422:\n            return \"invalid_request\";\n        case 409:\n        case 412:\n            return \"conflict\";\n        case 429:\n            return \"rate_limited\";\n        case 408:\n        case 504:\n            return \"timeout\";\n        case 502:\n        case 503:\n            return \"unavailable\";\n        default:\n            // Everything unmapped, 500 included. `internal` is the honest answer for a status this\n            // vocabulary has no distinct handling for - inventing a closer-looking code would tell\n            // the caller something the status does not actually say.\n            return \"internal\";\n    }\n}\n\n/**\n * Derives a code from a host MCP client's RETURNED failure, as opposed to a thrown one.\n *\n * Both paths exist and both have to be covered. A client that throws is handled by\n * {@link classifyHostError}; a client that reports failure as `{ ok: false, error, status }` - the\n * common shape for anything wrapping HTTP - comes through here. Covering only the throw path leaves\n * the majority of real failures arriving as `internal`, which is the same blindness the code was\n * added to remove.\n *\n * Returns `undefined` for a successful response, so a caller can spread it without putting a\n * meaningless code on the happy path.\n */\nexport function classifyHostResponse(response: {\n    readonly ok: boolean;\n    readonly status?: number;\n    readonly mcpErrorCode?: unknown;\n}): McpErrorCode | undefined\n{\n    if (response.ok)\n    {\n        return undefined;\n    }\n\n    // Same precedence as the thrown path: an explicit code from the client beats the transport's\n    // status, and neither is ever inferred from the message.\n    if (isMcpErrorCode(response.mcpErrorCode))\n    {\n        return response.mcpErrorCode;\n    }\n\n    return typeof response.status === \"number\"\n        ? mcpErrorCodeFromHttpStatus(response.status)\n        : \"internal\";\n}\n\n/**\n * Derives a code from an arbitrary thrown value, for the host side of the bridge.\n *\n * Precedence, most explicit first:\n *\n * 1. An `mcpErrorCode` property holding a known code. The intended contract: a host MCP client that\n *    knows why a call failed says so directly.\n * 2. A numeric `status` / `statusCode`, mapped by {@link mcpErrorCodeFromHttpStatus}. Covers the\n *    HTTP clients that already carry one without asking every host to adopt the field above.\n * 3. `name === \"AbortError\"` / `\"TimeoutError\"`, which is how the platform's own aborts surface.\n * 4. `internal`.\n *\n * **Never infers from the message.** Matching prose would make the classification depend on wording\n * nobody treats as a contract, and it would silently reclassify itself the day someone improves an\n * error string. An unclassifiable failure is `internal`, which is honest.\n */\nexport function classifyHostError(err: unknown): McpErrorCode\n{\n    if (err === null || typeof err !== \"object\")\n    {\n        return \"internal\";\n    }\n\n    const candidate = err as {\n        mcpErrorCode?: unknown;\n        status?: unknown;\n        statusCode?: unknown;\n        name?: unknown;\n    };\n\n    if (isMcpErrorCode(candidate.mcpErrorCode))\n    {\n        return candidate.mcpErrorCode;\n    }\n\n    const status = typeof candidate.status === \"number\"\n        ? candidate.status\n        : typeof candidate.statusCode === \"number\" ? candidate.statusCode : undefined;\n\n    if (status !== undefined)\n    {\n        return mcpErrorCodeFromHttpStatus(status);\n    }\n\n    if (candidate.name === \"AbortError\" || candidate.name === \"TimeoutError\")\n    {\n        return \"timeout\";\n    }\n\n    return \"internal\";\n}\n","/**\n * W1B — MessagePort-backed McpTransport for Contract B (worker remote-runtime)\n * plugins.\n *\n * The plugin-side React hooks (`useMcpResource`, `useMcpTool`) consume an\n * {@link McpTransport} — an abstraction over the host call. Contract A\n * (host-rendered) plugins pick up the host-injected transport via\n * `ExtensionRuntimeProvider`. Contract B plugins run in a Web Worker with\n * no shared object surface — the only channel is the `MessagePort` the\n * host transferred via `activate(port)`. This helper bridges the two\n * worlds: it exposes the {@link McpTransport} contract on the worker side\n * and serialises every call into a request/response envelope over the\n * port.\n *\n * # The protocol on the wire\n *\n * The wire shape mirrors `WorkerRemoteDomTransport` (in `host/worker/transport.ts`)\n * one-for-one — that transport is the host receiver and decides what a \"well-\n * formed\" message looks like. Two request shapes, two `:result`-suffixed reply\n * shapes, one abort envelope. Each call mints a fresh request id (`req-{n}`).\n *\n * ```jsonc\n * // worker → host\n * { type: \"ethisys:mcp:getResource\", id: \"req-3\", uri: \"tickets://home\" }\n * { type: \"ethisys:mcp:invokeTool\",  id: \"req-4\", name: \"tickets:open\", args: { ... } }\n *\n * // host → worker (matching id, suffixed type)\n * { type: \"ethisys:mcp:getResource:result\", id: \"req-3\", ok: true,  data: { uri, data } }\n * { type: \"ethisys:mcp:invokeTool:result\",  id: \"req-4\", ok: false, error: \"...\" }\n * ```\n *\n * Requests honour the optional `AbortSignal`. On abort, the transport posts\n * a `{ type: \"ethisys:mcp:abort\", id }` envelope so the host can cancel\n * in-flight work, then rejects the pending promise with an `AbortError`-shaped\n * `Error` so the consuming hooks see the same shape as native fetch cancellation.\n *\n * # Authoring shape\n *\n * ```ts\n * export async function activate(port: MessagePort): Promise<void> {\n *   const transport = createPortMcpTransport(port);\n *   const root = createRemoteRoot(port);\n *   root.render(\n *     <ExtensionRuntimeProvider transport={transport}>\n *       <App />\n *     </ExtensionRuntimeProvider>,\n *   );\n * }\n * ```\n *\n * `<App />` uses `useMcpResource` / `useMcpTool` as it would on the host\n * side; the transport translates each call into the port envelope.\n */\nimport type { McpTransport, UploadDocumentMeta, UploadDocumentResult } from \"./transport\";\nimport { McpToolError, isMcpErrorCode } from \"../bridge/mcp-error\";\n\n/**\n * Options for {@link createPortMcpTransport}. Reserved for forward\n * compatibility — the public surface is empty in Phase 1.\n */\nexport interface CreatePortMcpTransportOptions\n{\n    /**\n     * Override the request-id generator. Reserved for tests so they can\n     * make request ids deterministic. Production callers never pass this.\n     */\n    requestIdFactory?: () => string;\n\n    /**\n     * Override the port's `addEventListener` / `removeEventListener` /\n     * `postMessage` triple. Reserved for tests so the transport can be\n     * exercised without instantiating a real MessageChannel.\n     */\n    portShimForTests?: PortShim;\n}\n\n/**\n * Minimal subset of the MessagePort surface the transport actually uses.\n * Exposed so tests can construct a polyfill without faking the full\n * MessagePort.\n */\nexport interface PortShim\n{\n    addEventListener(\n        type: \"message\",\n        listener: (event: { data: unknown }) => void,\n    ): void;\n    removeEventListener(\n        type: \"message\",\n        listener: (event: { data: unknown }) => void,\n    ): void;\n    postMessage(value: unknown, transfer?: Transferable[]): void;\n}\n\n// ── Wire-shape constants ────────────────────────────────────────────\n//\n// These four strings are the contract with `WorkerRemoteDomTransport`. Any\n// rename here breaks the host receiver silently — the unknown message hits\n// the host's default branch and is dropped. Keep these in sync with\n// `host/worker/transport.ts`'s `handlePortMessage` switch / result-type\n// strings.\nconst WIRE_INVOKE_TOOL_REQUEST = \"ethisys:mcp:invokeTool\";\nconst WIRE_INVOKE_TOOL_RESULT  = \"ethisys:mcp:invokeTool:result\";\nconst WIRE_GET_RESOURCE_REQUEST = \"ethisys:mcp:getResource\";\nconst WIRE_GET_RESOURCE_RESULT  = \"ethisys:mcp:getResource:result\";\nconst WIRE_UPLOAD_DOCUMENT_REQUEST = \"ethisys:mcp:uploadDocument\";\nconst WIRE_UPLOAD_DOCUMENT_RESULT  = \"ethisys:mcp:uploadDocument:result\";\nconst WIRE_ABORT = \"ethisys:mcp:abort\";\n\ntype McpResultType =\n    | typeof WIRE_INVOKE_TOOL_RESULT\n    | typeof WIRE_GET_RESOURCE_RESULT\n    | typeof WIRE_UPLOAD_DOCUMENT_RESULT;\n\ninterface McpResultEnvelope\n{\n    type: McpResultType;\n    id: string;\n    ok?: boolean;\n    error?: string;\n    /**\n     * Machine-readable failure classification. Optional: a host built before the bridge carried one\n     * sends none, and those replies must still settle rather than being treated as malformed.\n     */\n    code?: unknown;\n    data?: unknown;\n}\n\nfunction isMcpResultEnvelope(value: unknown): value is McpResultEnvelope\n{\n    if (value === null || typeof value !== \"object\")\n    {\n        return false;\n    }\n    const v = value as { type?: unknown; id?: unknown };\n    return (v.type === WIRE_INVOKE_TOOL_RESULT\n            || v.type === WIRE_GET_RESOURCE_RESULT\n            || v.type === WIRE_UPLOAD_DOCUMENT_RESULT)\n        && typeof v.id === \"string\";\n}\n\n/**\n * Construct an {@link McpTransport} backed by a MessagePort.\n *\n * @param port The host-transferred MessagePort for the worker surface.\n * @param options Reserved for forward compatibility / test injection.\n *\n * @returns A transport implementation honouring the {@link McpTransport}\n *          contract — fetch a resource, invoke a tool, observe abort\n *          signals, settle the promise on the host's reply envelope.\n */\nexport function createPortMcpTransport(\n    port: MessagePort | PortShim,\n    options: CreatePortMcpTransportOptions = {},\n): McpTransport\n{\n    if (port === undefined || port === null)\n    {\n        throw new TypeError(\n            \"[createPortMcpTransport] `port` is required. Pass the MessagePort the host transferred via the worker's `activate(port)` entry point.\",\n        );\n    }\n\n    const shim: PortShim = options.portShimForTests ?? port as PortShim;\n    const factory = options.requestIdFactory ?? defaultRequestIdFactory();\n\n    type Resolver = {\n        resolve: (value: unknown) => void;\n        reject: (reason: unknown) => void;\n    };\n    const pending = new Map<string, Resolver>();\n\n    function onMessage(event: { data: unknown }): void\n    {\n        if (!isMcpResultEnvelope(event.data))\n        {\n            return;\n        }\n        const reply = event.data;\n        const resolver = pending.get(reply.id);\n        if (resolver === undefined)\n        {\n            return;\n        }\n        pending.delete(reply.id);\n        if (reply.ok === true)\n        {\n            // Host posts the payload under `data` (matching\n            // `WorkerRemoteDomTransport.safePostMessage`). For getResource\n            // calls the data is the resource body; for invokeTool calls it\n            // is the tool's return value.\n            resolver.resolve(reply.data);\n        }\n        else\n        {\n            // The code is VALIDATED, not trusted: an unknown string from a newer host degrades to\n            // `internal` rather than reaching plugin code that cannot branch on it. A host with no\n            // `code` at all also lands on `internal`, so an older host keeps working and simply\n            // offers no classification.\n            resolver.reject(new McpToolError(\n                typeof reply.error === \"string\" ? reply.error : \"Unknown host error\",\n                isMcpErrorCode(reply.code) ? reply.code : \"internal\",\n            ));\n        }\n    }\n\n    shim.addEventListener(\"message\", onMessage);\n    // MessagePort.addEventListener doesn't auto-start the port — without an\n    // explicit `start()`, the message queue stays paused and the host's\n    // replies never reach `onMessage`. This is a no-op on plain\n    // EventTarget-shaped shims used in tests (the type guard guarantees\n    // safety) but is required on real MessagePort instances.\n    const realPort = port as Partial<MessagePort>;\n    if (!options.portShimForTests && typeof realPort.start === \"function\")\n    {\n        realPort.start();\n    }\n\n    function send(\n        requestType:\n            | typeof WIRE_INVOKE_TOOL_REQUEST\n            | typeof WIRE_GET_RESOURCE_REQUEST\n            | typeof WIRE_UPLOAD_DOCUMENT_REQUEST,\n        body: Record<string, unknown>,\n        signal?: AbortSignal,\n        transfer?: Transferable[],\n    ): Promise<unknown>\n    {\n        const id = factory();\n        return new Promise<unknown>((resolve, reject) =>\n        {\n            if (signal?.aborted === true)\n            {\n                reject(abortError());\n                return;\n            }\n            pending.set(id, { resolve, reject });\n\n            const onAbort = (): void =>\n            {\n                if (!pending.has(id))\n                {\n                    return;\n                }\n                pending.delete(id);\n                shim.postMessage({ type: WIRE_ABORT, id });\n                reject(abortError());\n            };\n            signal?.addEventListener(\"abort\", onAbort, { once: true });\n\n            // `transfer` moves the ArrayBuffer zero-copy for uploadDocument; the\n            // JSON request shapes pass no transfer list and post by structured clone.\n            shim.postMessage({ type: requestType, id, ...body }, transfer);\n        });\n    }\n\n    return {\n        async getResource<T>(uri: string, signal?: AbortSignal): Promise<{ uri: string; data: T }>\n        {\n            // Host sends only the resource body under `data`. Reconstruct the\n            // `{ uri, data }` envelope the McpTransport contract returns —\n            // `uri` is round-tripped from the request because the resolver\n            // only sees the reply.\n            const data = await send(WIRE_GET_RESOURCE_REQUEST, { uri }, signal);\n            return { uri, data: data as T };\n        },\n        async invokeTool<TReq, TRes>(name: string, args: TReq, signal?: AbortSignal): Promise<TRes>\n        {\n            const data = await send(WIRE_INVOKE_TOOL_REQUEST, { name, args: args as unknown }, signal);\n            return data as TRes;\n        },\n        async uploadDocument(\n            meta: UploadDocumentMeta,\n            buffer: ArrayBuffer,\n            signal?: AbortSignal,\n        ): Promise<UploadDocumentResult>\n        {\n            // The ArrayBuffer rides as a transferable — after posting, `buffer`\n            // is detached on the worker side (zero-copy hand-off to the host).\n            const data = await send(\n                WIRE_UPLOAD_DOCUMENT_REQUEST,\n                { meta, buffer },\n                signal,\n                [buffer],\n            );\n            return data as UploadDocumentResult;\n        },\n    };\n}\n\nfunction defaultRequestIdFactory(): () => string\n{\n    let counter = 0;\n    return (): string =>\n    {\n        counter += 1;\n        return `req-${counter}`;\n    };\n}\n\nfunction abortError(): Error\n{\n    const e = new Error(\"Aborted\");\n    e.name = \"AbortError\";\n    return e;\n}\n","/**\n * Wire-shape constants and type guards for host→plugin bridge push messages\n * (WI 4858 sub-plan 2). These types ride the existing MessagePort channel\n * alongside the `ethisys:mcp:*` and `ethisys:remotedom` envelopes — the\n * `type` discriminant keeps them fully separate at the dispatch site.\n *\n * The host posts bridge pushes through `WorkerRemoteDomTransport.hostPort`;\n * the plugin side reads them in `createPortBridgeClient` (plugin-ui package).\n * In-realm (Tier T) plugins use `InMemoryBridgeTransport` which calls the\n * subscriber callbacks directly — no serialisation needed.\n */\n\n// ── Type constants ───────────────────────────────────────────────────────────\n\nexport const BRIDGE_PUSH_THEME       = \"ethisys:bridge:theme\"          as const;\nexport const BRIDGE_PUSH_LOCALE      = \"ethisys:bridge:locale\"         as const;\nexport const BRIDGE_PUSH_DENSITY     = \"ethisys:bridge:density\"        as const;\nexport const BRIDGE_PUSH_A11Y        = \"ethisys:bridge:a11y\"           as const;\nexport const BRIDGE_NAV_PUSH         = \"ethisys:bridge:nav\"            as const;\nexport const BRIDGE_CHROME_REQUEST   = \"ethisys:bridge:chrome:request\" as const;\nexport const BRIDGE_CHROME_RESPONSE  = \"ethisys:bridge:chrome:response\" as const;\nexport const BRIDGE_SESSION_TOKEN_PUSH = \"ethisys:bridge:token\"        as const;\nexport const BRIDGE_LIFECYCLE_EVENT  = \"ethisys:bridge:lifecycle\"      as const;\nexport const BRIDGE_A11Y_ANNOUNCE    = \"ethisys:bridge:a11y:announce\"  as const;\n\n// ── Envelope shapes ──────────────────────────────────────────────────────────\n\n/** Theme push: host → plugin whenever the host theme changes. */\nexport interface BridgePushThemeEnvelope {\n  readonly type: typeof BRIDGE_PUSH_THEME;\n  /** \"light\" | \"dark\" | \"high-contrast\" */\n  readonly mode: string;\n  /** Flat design-token map (CSS-variable-name → value). May be empty. */\n  readonly tokens: Readonly<Record<string, string>>;\n}\n\n/** Locale push: host → plugin whenever the active locale changes. */\nexport interface BridgePushLocaleEnvelope {\n  readonly type: typeof BRIDGE_PUSH_LOCALE;\n  /** BCP 47 tag, e.g. \"en-GB\". */\n  readonly locale: string;\n  /** \"ltr\" | \"rtl\" */\n  readonly dir: \"ltr\" | \"rtl\";\n}\n\n/** Density push: host → plugin when the UI density preference changes. */\nexport interface BridgePushDensityEnvelope {\n  readonly type: typeof BRIDGE_PUSH_DENSITY;\n  /** \"comfortable\" | \"compact\" */\n  readonly density: string;\n}\n\n/** A11y push: host → plugin when accessibility prefs change. */\nexport interface BridgePushA11yEnvelope {\n  readonly type: typeof BRIDGE_PUSH_A11Y;\n  readonly reducedMotion: boolean;\n  readonly highContrast: boolean;\n}\n\n/** Nav push: host → plugin when the SPA location changes. */\nexport interface BridgeNavPushEnvelope {\n  readonly type: typeof BRIDGE_NAV_PUSH;\n  /** Current SPA path (pathname + search). */\n  readonly path: string;\n  /** `window.history.length` at the time of push. */\n  readonly historyLength: number;\n}\n\n/** Plugin → host: request a chrome action (e.g. toast, modal). */\nexport interface BridgeChromeRequestEnvelope {\n  readonly type: typeof BRIDGE_CHROME_REQUEST;\n  /** Correlation id — matched against the response. */\n  readonly id: string;\n  /** \"toast\" | \"confirm\" | \"openUrl\" */\n  readonly action: string;\n  readonly payload: Readonly<Record<string, unknown>>;\n}\n\n/** Host → plugin: response to a chrome request. */\nexport interface BridgeChromeResponseEnvelope {\n  readonly type: typeof BRIDGE_CHROME_RESPONSE;\n  readonly id: string;\n  readonly ok: boolean;\n  readonly result?: unknown;\n  readonly error?: string;\n}\n\n/** Host → plugin: push a short-lived frontend-session token. */\nexport interface BridgeSessionTokenPushEnvelope {\n  readonly type: typeof BRIDGE_SESSION_TOKEN_PUSH;\n  /** Opaque JWT string — audience-restricted to the plugin's own backend. */\n  readonly token: string;\n  /** Absolute epoch-ms at which the token expires. Refresh fires 30 s before. */\n  readonly expiresAtMs: number;\n}\n\n/** Host ↔ plugin lifecycle event. */\nexport type BridgeLifecyclePhase = \"load\" | \"ready\" | \"error\" | \"unload\";\n\nexport interface BridgeLifecycleEventEnvelope {\n  readonly type: typeof BRIDGE_LIFECYCLE_EVENT;\n  readonly phase: BridgeLifecyclePhase;\n  /** Present when phase === \"error\". */\n  readonly message?: string;\n}\n\n/** Plugin → host: a11y live-region announcement request. */\nexport interface BridgeA11yAnnounceEnvelope {\n  readonly type: typeof BRIDGE_A11Y_ANNOUNCE;\n  readonly message: string;\n  /** \"polite\" | \"assertive\" */\n  readonly politeness: \"polite\" | \"assertive\";\n}\n\n// ── Type guards ──────────────────────────────────────────────────────────────\n\nfunction isObj(v: unknown): v is Record<string, unknown> {\n  return v !== null && typeof v === \"object\";\n}\n\nexport function isBridgePushTheme(v: unknown): v is BridgePushThemeEnvelope {\n  return isObj(v) && v[\"type\"] === BRIDGE_PUSH_THEME && typeof v[\"mode\"] === \"string\";\n}\n\nexport function isBridgePushLocale(v: unknown): v is BridgePushLocaleEnvelope {\n  return isObj(v) && v[\"type\"] === BRIDGE_PUSH_LOCALE && typeof v[\"locale\"] === \"string\";\n}\n\nexport function isBridgePushDensity(v: unknown): v is BridgePushDensityEnvelope {\n  return isObj(v) && v[\"type\"] === BRIDGE_PUSH_DENSITY && typeof v[\"density\"] === \"string\";\n}\n\nexport function isBridgePushA11y(v: unknown): v is BridgePushA11yEnvelope {\n  return isObj(v) && v[\"type\"] === BRIDGE_PUSH_A11Y\n    && typeof v[\"reducedMotion\"] === \"boolean\"\n    && typeof v[\"highContrast\"] === \"boolean\";\n}\n\nexport function isBridgeNavPush(v: unknown): v is BridgeNavPushEnvelope {\n  return isObj(v) && v[\"type\"] === BRIDGE_NAV_PUSH && typeof v[\"path\"] === \"string\";\n}\n\nexport function isBridgeChromeRequest(v: unknown): v is BridgeChromeRequestEnvelope {\n  return isObj(v) && v[\"type\"] === BRIDGE_CHROME_REQUEST && typeof v[\"id\"] === \"string\";\n}\n\nexport function isBridgeChromeResponse(v: unknown): v is BridgeChromeResponseEnvelope {\n  return isObj(v) && v[\"type\"] === BRIDGE_CHROME_RESPONSE && typeof v[\"id\"] === \"string\";\n}\n\nexport function isBridgeSessionTokenPush(v: unknown): v is BridgeSessionTokenPushEnvelope {\n  return isObj(v) && v[\"type\"] === BRIDGE_SESSION_TOKEN_PUSH\n    && typeof v[\"token\"] === \"string\"\n    && typeof v[\"expiresAtMs\"] === \"number\";\n}\n\nexport function isBridgeLifecycleEvent(v: unknown): v is BridgeLifecycleEventEnvelope {\n  return isObj(v) && v[\"type\"] === BRIDGE_LIFECYCLE_EVENT && typeof v[\"phase\"] === \"string\";\n}\n\nexport function isBridgeA11yAnnounce(v: unknown): v is BridgeA11yAnnounceEnvelope {\n  return isObj(v) && v[\"type\"] === BRIDGE_A11Y_ANNOUNCE && typeof v[\"message\"] === \"string\";\n}\n","/**\n * Plugin-side bridge client for the host ↔ plugin platform contract\n * (WI 4858 sub-plan 2).\n *\n * `createPortBridgeClient` wraps a MessagePort (or an in-realm shim) and\n * exposes typed subscription callbacks for each host push (theme, locale,\n * density, a11y, nav, session token) and typed fire-and-wait calls for\n * chrome requests and a11y live-region announcements.\n *\n * For in-realm (Tier T) plugins, use `InMemoryBridgeTransport` from the\n * mock-host package instead — it calls callbacks synchronously without\n * any serialisation.\n */\nimport {\n  BRIDGE_CHROME_REQUEST,\n  BRIDGE_A11Y_ANNOUNCE,\n  isBridgePushTheme,\n  isBridgePushLocale,\n  isBridgePushDensity,\n  isBridgePushA11y,\n  isBridgeNavPush,\n  isBridgeChromeResponse,\n  isBridgeSessionTokenPush,\n  type BridgePushThemeEnvelope,\n  type BridgePushLocaleEnvelope,\n  type BridgePushDensityEnvelope,\n  type BridgePushA11yEnvelope,\n  type BridgeNavPushEnvelope,\n  type BridgeSessionTokenPushEnvelope,\n} from \"../host/worker/bridge-envelopes\";\n\n// ── Subscriber payload types (strips the `type` discriminant) ────────────────\n\nexport type ThemePayload  = Omit<BridgePushThemeEnvelope, \"type\">;\nexport type LocalePayload = Omit<BridgePushLocaleEnvelope, \"type\">;\nexport type DensityPayload = Omit<BridgePushDensityEnvelope, \"type\">;\nexport type A11yPayload   = Omit<BridgePushA11yEnvelope, \"type\">;\nexport type NavPayload    = Omit<BridgeNavPushEnvelope, \"type\">;\nexport type SessionTokenPayload = Omit<BridgeSessionTokenPushEnvelope, \"type\">;\n\n// ── Port shim (test injection) ───────────────────────────────────────────────\n\n/**\n * Minimal port surface `createPortBridgeClient` actually uses. Matches\n * `PortShim` in `createPortMcpTransport` — same test-injection pattern.\n */\nexport interface BridgePortShim {\n  onmessage: ((ev: { data: unknown }) => void) | null;\n  postMessage(msg: unknown): void;\n}\n\n// ── Public client interface ──────────────────────────────────────────────────\n\nexport interface PortBridgeClient {\n  /** Replace the theme subscriber. Only the latest subscriber is called. */\n  onTheme(cb: (payload: ThemePayload) => void): void;\n  /** Replace the locale subscriber. */\n  onLocale(cb: (payload: LocalePayload) => void): void;\n  /** Replace the density subscriber. */\n  onDensity(cb: (payload: DensityPayload) => void): void;\n  /** Replace the a11y-preferences subscriber. */\n  onA11y(cb: (payload: A11yPayload) => void): void;\n  /** Replace the nav-state subscriber. */\n  onNav(cb: (payload: NavPayload) => void): void;\n  /** Replace the session-token subscriber. */\n  onSessionToken(cb: (payload: SessionTokenPayload) => void): void;\n  /**\n   * Request a host chrome action. Returns the host's result or rejects with\n   * the host's error string. Callers should catch and surface gracefully.\n   *\n   * @param action \"toast\" | \"confirm\" | \"openUrl\"\n   * @param payload Action-specific payload.\n   */\n  requestChrome(action: string, payload: Record<string, unknown>): Promise<unknown>;\n  /**\n   * Ask the host to make a live-region announcement on behalf of the plugin.\n   * Prefers this over writing directly to the DOM so the host can manage\n   * ARIA live regions centrally.\n   */\n  announceA11y(message: string, politeness: \"polite\" | \"assertive\"): void;\n}\n\n// ── Implementation ───────────────────────────────────────────────────────────\n\nlet _requestCounter = 0;\nfunction nextId(): string {\n  _requestCounter += 1;\n  return `br-${_requestCounter}`;\n}\n\n/**\n * Construct a bridge client backed by a `MessagePort` (or shim).\n *\n * Registers a single `onmessage` handler on the port that dispatches each\n * bridge push to the appropriate subscriber. Unknown message types are\n * silently ignored — the port carries MCP traffic on the same channel and\n * those messages must not trigger an error here.\n */\nexport function createPortBridgeClient(port: BridgePortShim): PortBridgeClient {\n  let themeCb:  ((p: ThemePayload) => void)  | undefined;\n  let localeCb: ((p: LocalePayload) => void) | undefined;\n  let densityCb: ((p: DensityPayload) => void) | undefined;\n  let a11yCb:   ((p: A11yPayload) => void)   | undefined;\n  let navCb:    ((p: NavPayload) => void)    | undefined;\n  let tokenCb:  ((p: SessionTokenPayload) => void) | undefined;\n\n  type PendingChrome = { resolve: (v: unknown) => void; reject: (e: Error) => void };\n  const pending = new Map<string, PendingChrome>();\n\n  port.onmessage = (ev: { data: unknown }): void => {\n    const msg = ev.data;\n    if (isBridgePushTheme(msg))        { themeCb?.({ mode: msg.mode, tokens: msg.tokens }); return; }\n    if (isBridgePushLocale(msg))       { localeCb?.({ locale: msg.locale, dir: msg.dir }); return; }\n    if (isBridgePushDensity(msg))      { densityCb?.({ density: msg.density }); return; }\n    if (isBridgePushA11y(msg))         { a11yCb?.({ reducedMotion: msg.reducedMotion, highContrast: msg.highContrast }); return; }\n    if (isBridgeNavPush(msg))          { navCb?.({ path: msg.path, historyLength: msg.historyLength }); return; }\n    if (isBridgeSessionTokenPush(msg)) { tokenCb?.({ token: msg.token, expiresAtMs: msg.expiresAtMs }); return; }\n    if (isBridgeChromeResponse(msg)) {\n      const p = pending.get(msg.id);\n      if (p === undefined) { return; }\n      pending.delete(msg.id);\n      if (msg.ok) { p.resolve(msg.result ?? null); }\n      else { p.reject(new Error(typeof msg.error === \"string\" ? msg.error : \"Chrome request failed\")); }\n    }\n  };\n\n  return {\n    onTheme(cb) { themeCb = cb; },\n    onLocale(cb) { localeCb = cb; },\n    onDensity(cb) { densityCb = cb; },\n    onA11y(cb) { a11yCb = cb; },\n    onNav(cb) { navCb = cb; },\n    onSessionToken(cb) { tokenCb = cb; },\n\n    requestChrome(action, payload): Promise<unknown> {\n      const id = nextId();\n      return new Promise<unknown>((resolve, reject) => {\n        pending.set(id, { resolve, reject });\n        port.postMessage({ type: BRIDGE_CHROME_REQUEST, id, action, payload });\n      });\n    },\n\n    announceA11y(message, politeness): void {\n      port.postMessage({ type: BRIDGE_A11Y_ANNOUNCE, message, politeness });\n    },\n  };\n}\n","import { createContext, useContext } from \"react\";\nimport type { PortBridgeClient } from \"./bridge-client\";\n\n/**\n * React context carrying the plugin's active {@link PortBridgeClient}.\n * Provided by the host mount (WorkerMockHost in dev, real bridge in production)\n * and consumed by `useBridgeTheme`, `useBridgeLocale`, and the `plugin-ui` hooks.\n */\nexport const BridgeClientContext = createContext<PortBridgeClient | null>(null);\n\n/**\n * Returns the bridge client from context, throwing a clear error when missing.\n * Used by the `useBridge*` hooks to fail loudly on misconfiguration.\n */\nexport function useBridgeClient(): PortBridgeClient {\n  const client = useContext(BridgeClientContext);\n  if (client === null) {\n    throw new Error(\n      \"No PortBridgeClient available. Wrap your plugin in <BridgeClientProvider> \"\n      + \"or ensure the host mount wires a BridgeClientContext.Provider.\",\n    );\n  }\n  return client;\n}\n","import { useEffect, useState } from \"react\";\nimport { useBridgeClient } from \"./BridgeClientContext\";\nimport type { ThemePayload } from \"./bridge-client\";\n\n/**\n * Subscribe to host theme pushes. Returns the most recently pushed theme,\n * or `undefined` before the first push (e.g. during initial render before\n * the host has sent its first `BRIDGE_PUSH_THEME` message).\n *\n * The hook re-registers with the bridge client if the client identity changes\n * (e.g. on hot-reload of the mock host in dev), keeping state consistent.\n */\nexport function useBridgeTheme(): ThemePayload | undefined {\n  const client = useBridgeClient();\n  const [theme, setTheme] = useState<ThemePayload | undefined>(undefined);\n\n  useEffect(() => {\n    client.onTheme((payload) => setTheme(payload));\n    // No cleanup needed — `onTheme` replaces the subscriber; unmounting\n    // drops the hook's closure reference, preventing stale-state updates.\n  }, [client]);\n\n  return theme;\n}\n","import { useEffect, useState } from \"react\";\nimport { useBridgeClient } from \"./BridgeClientContext\";\nimport type { LocalePayload } from \"./bridge-client\";\n\n/**\n * Subscribe to host locale pushes. Returns the most recently pushed locale\n * and text direction, or `undefined` before the first push.\n */\nexport function useBridgeLocale(): LocalePayload | undefined {\n  const client = useBridgeClient();\n  const [locale, setLocale] = useState<LocalePayload | undefined>(undefined);\n\n  useEffect(() => {\n    client.onLocale((payload) => setLocale(payload));\n  }, [client]);\n\n  return locale;\n}\n","/**\n * W4A — Bridge-mediated frontend-session token hook (WI 4858 sub-plan 4).\n *\n * # What this does\n * Fetches the audience-restricted FE-session token from the host bridge\n * (resource URI: `\"ethisys:session-token\"`) and manages silent refresh.\n * The token is delivered to this hook; it is NEVER the capability JWT\n * (that stays brokered host-side and never reaches plugin JS).\n *\n * # Refresh policy\n * Refresh fires 30 s before `expiresAtUtc`. The timer is cleared on\n * unmount; no stale async operations survive component teardown.\n *\n * # Transport\n * The hook is transport-agnostic — it accepts any {@link McpTransport}.\n * Pass the context transport in production; a mock in tests.\n *\n * This is the canonical FE-session-token hook; `@ethisyscore/plugin-ui/token`\n * re-exports it so plugin authors take a single dependency on plugin-ui.\n */\nimport { useState, useEffect, useRef, useCallback } from \"react\";\nimport type { McpTransport } from \"./transport\";\n\nconst SESSION_TOKEN_URI = \"ethisys:session-token\";\nconst REFRESH_SKEW_MS = 30_000; // refresh 30 s before expiry\n\ninterface FrontendSessionTokenPayload\n{\n    accessToken: string;\n    expiresAtUtc: string; // ISO-8601\n    jti: string;\n}\n\nexport interface UseFrontendSessionTokenResult\n{\n    /** The compact-serialised JWT, or null while loading / on error. */\n    token: string | null;\n    /** True while the first fetch (or a refresh) is in-flight. */\n    isLoading: boolean;\n    /** Set when the bridge rejects; null otherwise. */\n    error: Error | null;\n}\n\n/**\n * Returns the current FE-session token fetched from the host bridge.\n * Schedules silent refresh 30 s before the token expires.\n *\n * @param transport The {@link McpTransport} to use. Typically the context\n * transport from the extension runtime provider.\n */\nexport function useFrontendSessionToken(\n    transport: McpTransport,\n): UseFrontendSessionTokenResult\n{\n    const [token, setToken] = useState<string | null>(null);\n    const [isLoading, setIsLoading] = useState(true);\n    const [error, setError] = useState<Error | null>(null);\n    const refreshTimerRef = useRef<ReturnType<typeof setTimeout> | null>(null);\n    const mountedRef = useRef(true);\n\n    const fetch = useCallback(async (): Promise<void> =>\n    {\n        if (refreshTimerRef.current !== null)\n        {\n            clearTimeout(refreshTimerRef.current);\n            refreshTimerRef.current = null;\n        }\n        try\n        {\n            const { data } = await transport.getResource<FrontendSessionTokenPayload>(\n                SESSION_TOKEN_URI,\n            );\n\n            if (!mountedRef.current)\n            {\n                return;\n            }\n\n            setToken(data.accessToken);\n            setError(null);\n            setIsLoading(false);\n\n            // Schedule refresh 30 s before expiry.\n            const expiresAtMs = Date.parse(data.expiresAtUtc);\n            const refreshIn = expiresAtMs - Date.now() - REFRESH_SKEW_MS;\n            if (refreshIn > 0)\n            {\n                refreshTimerRef.current = setTimeout(() =>\n                {\n                    void fetch();\n                }, refreshIn);\n            }\n        }\n        catch (err)\n        {\n            if (!mountedRef.current)\n            {\n                return;\n            }\n            setError(err instanceof Error ? err : new Error(String(err)));\n            setToken(null);\n            setIsLoading(false);\n        }\n    }, [transport]);\n\n    useEffect(() =>\n    {\n        mountedRef.current = true;\n        setIsLoading(true);\n        void fetch();\n\n        return (): void =>\n        {\n            mountedRef.current = false;\n            if (refreshTimerRef.current !== null)\n            {\n                clearTimeout(refreshTimerRef.current);\n            }\n        };\n    }, [fetch]);\n\n    return { token, isLoading, error };\n}\n","/**\n * Pure JWT-payload decode helper (WI 5160 — F-AUTH-SEAM FE face).\n *\n * Extracts and JSON-parses the middle (payload) segment of a compact JWT.\n * URL-safe base64url characters (`-` → `+`, `_` → `/`) are normalised and\n * missing `=` padding is restored before passing to `atob`.\n *\n * This helper is intentionally side-effect-free and React-free so it can be\n * unit-tested without a DOM environment.\n *\n * **Security note:** this function does NOT validate the JWT signature —\n * the host already validated the token at mint time. The FE only reads claims.\n *\n * @param token A compact-serialised JWT (`header.payload.signature`) or null.\n * @returns The parsed payload object, or `{}` if the token is absent, malformed,\n *          has fewer than 3 segments, or the payload is not valid JSON.\n */\nexport function decodeJwtPayload(token: string | null): Record<string, unknown>\n{\n    if (token === null || token === \"\")\n    {\n        return {};\n    }\n\n    const segments = token.split(\".\");\n    if (segments.length < 3)\n    {\n        return {};\n    }\n\n    const payloadSegment = segments[1];\n\n    try\n    {\n        // Convert base64url → standard base64 and restore padding.\n        const base64 = payloadSegment\n            .replace(/-/g, \"+\")\n            .replace(/_/g, \"/\");\n\n        // Restore `=` padding so atob does not throw.\n        const padded = base64 + \"===\".slice(0, (4 - (base64.length % 4)) % 4);\n\n        const json = atob(padded);\n        const parsed: unknown = JSON.parse(json);\n\n        if (parsed === null || typeof parsed !== \"object\" || Array.isArray(parsed))\n        {\n            return {};\n        }\n\n        return parsed as Record<string, unknown>;\n    }\n    catch\n    {\n        return {};\n    }\n}\n","/**\n * W5160 — `useAuth` FE gate (F-AUTH-SEAM FE face).\n *\n * # What this does\n * Wraps {@link useFrontendSessionToken} and decodes the returned compact JWT\n * to expose the caller's identity (`currentUserId`) and a short-code permission\n * gate (`hasPermission`).\n *\n * # Claim layout (host-minted, per FrontendSessionTokenIssuer)\n * - `user_id` (or `sub` as fallback) — the caller's user ID.\n * - `permissions` — a space-delimited string of short codes,\n *   e.g. `\"timeslip.read timeslip.write\"`.\n *\n * # Fail-closed behaviour\n * While the token is loading, absent, or the bridge errors, `currentUserId`\n * is `null` and `hasPermission` always returns `false`.\n *\n * # Security note\n * The JWT signature is NOT re-validated here — the host validated it at mint.\n * The FE gate is advisory (UI gating); privileged execution is always\n * RBAC-gated host-side (§4.4 of the auth-seam spec).\n *\n * This is the canonical `useAuth` hook; `@ethisyscore/plugin-ui/token`\n * re-exports it so plugin authors take a single dependency on plugin-ui.\n */\nimport { useMemo } from \"react\";\nimport { useFrontendSessionToken } from \"./useFrontendSessionToken\";\nimport { decodeJwtPayload } from \"./decodeJwtPayload\";\nimport type { McpTransport } from \"./transport\";\n\nexport interface UseAuthResult\n{\n    /** The caller's user ID from the FE-session JWT, or null while loading / on error. */\n    currentUserId: string | null;\n    /**\n     * Returns true if the caller has the given short-code permission.\n     * Always returns false while the token is loading or absent (fail-closed).\n     *\n     * @param shortCode The exact permission short code, e.g. `\"timeslip.read\"`.\n     */\n    hasPermission(shortCode: string): boolean;\n    /** True while the first fetch (or a silent refresh) is in-flight. */\n    isLoading: boolean;\n    /** Set when the bridge rejects; null otherwise. */\n    error: Error | null;\n}\n\n/**\n * Returns the caller's identity and permission gate derived from the\n * FE-session token.\n *\n * @param transport The {@link McpTransport} to use. Typically the context\n * transport from the extension runtime provider.\n */\nexport function useAuth(transport: McpTransport): UseAuthResult\n{\n    const { token, isLoading, error } = useFrontendSessionToken(transport);\n\n    const { currentUserId, permissionSet } = useMemo(() =>\n    {\n        const claims = decodeJwtPayload(token);\n\n        const userId =\n            (typeof claims[\"user_id\"] === \"string\" && claims[\"user_id\"] !== \"\" ? claims[\"user_id\"] : null) ??\n            (typeof claims[\"sub\"] === \"string\" && claims[\"sub\"] !== \"\" ? claims[\"sub\"] : null);\n\n        const permissionsRaw = typeof claims[\"permissions\"] === \"string\" ? claims[\"permissions\"] : \"\";\n        const set = permissionsRaw.length > 0\n            ? new Set(permissionsRaw.split(\" \").filter((s) => s.length > 0))\n            : new Set<string>();\n\n        return { currentUserId: userId, permissionSet: set };\n    }, [token]);\n\n    return {\n        currentUserId,\n        hasPermission: (shortCode: string): boolean => permissionSet.has(shortCode),\n        isLoading,\n        error,\n    };\n}\n"]}