import { M as McpTransport } from '../bridge-envelopes-DA6vxbyb.cjs'; export { U as UploadDocumentMeta, f as UploadDocumentResult } from '../bridge-envelopes-DA6vxbyb.cjs'; import * as react from 'react'; import { ReactNode } from 'react'; import { SduiNode, RenderMode } from '@ethisyscore/protocol'; export { PagedResponse, PaginatedEnvelope } from '@ethisyscore/protocol'; import { RemoteConnection } from '@remote-dom/core'; import { P as PortBridgeClient, T as ThemePayload, L as LocalePayload } from '../bridge-client-DzRcKIJT.cjs'; export { A as A11yPayload, B as BridgePortShim, D as DensityPayload, N as NavPayload, S as SessionTokenPayload, c as createPortBridgeClient } from '../bridge-client-DzRcKIJT.cjs'; export { M as MCP_ERROR_CODES, a as McpErrorCode, b as McpToolError, c as classifyHostError, d as classifyHostResponse, i as isMcpErrorCode, e as isMcpToolError, f as isRetryableMcpErrorCode, m as mcpErrorCodeFromHttpStatus } from '../mcp-error-CCZQd8Xl.cjs'; /** * A single client-push event delivered from the host to a plugin surface. The host * relays the plugin backend's `IClientPushPublisher` events over its realtime * channel (SignalR); the transport envelope's extension identity is bound by the * host at mount time, so the plugin sees only the event body. * * SCOPE + ORDERING: which channel/user/group an event concerns is carried INSIDE * `payloadJson` by the emitting plugin — the transport envelope intentionally has no * group field. Consumers therefore demultiplex + order by their own payload fields * (e.g. a per-channel sequence in the payload), NOT by {@link eventSequence}, which * is per-group at the host and would produce false gaps when multiple groups * multiplex over one connection. */ interface ClientPushEvent { /** Plugin-defined discriminator, e.g. `"chatMessageReceived"`. */ eventType: string; /** Raw JSON payload authored by the plugin backend. */ payloadJson: string; /** * Host per-group monotonic sequence. Advisory only — do NOT use for * cross-group gap detection (see the scope note above). */ eventSequence: number; } interface ClientPushSubscribeOptions { /** * Opaque group names to enrol in (e.g. `"chat:channel:{id}"`). The host * authorises each subscription via the plugin's `authorize-subscription` tool * and enforces org/extension isolation — a plugin cannot subscribe outside its * own extension + organisation. */ groups: string[]; /** Called for each delivered (non-resync) event for the subscribed groups. */ onEvent: (event: ClientPushEvent) => void; /** * Called when the host signals a gap/resync for the subscribed groups (a * dropped-event backpressure signal, or a reconnect). The consumer should * re-fetch authoritative state (e.g. a delta/cold-load) rather than trusting * incremental events. */ onResync?: () => void; } /** * Host-provided channel for realtime server-push. The channel is already scoped to * the mounted surface's extension + organisation (bound by the host from the trusted * mount descriptor — a plugin CANNOT widen it), so {@link subscribe} takes only * opaque group names and returns an unsubscribe function. */ interface ClientPushChannel { subscribe(options: ClientPushSubscribeOptions): () => void; } /** * `null` = no host channel (standalone/mock, or a host that predates client-push) → * {@link useClientPushSubscription} is inert. Provided by * {@link ExtensionRuntimeProvider}'s optional `clientPush` prop. */ declare const ClientPushContext: react.Context; interface UseClientPushSubscriptionOptions { /** Opaque groups to subscribe. Changing the SET re-subscribes; identity/order changes alone do not. */ groups: string[]; onEvent: (event: ClientPushEvent) => void; onResync?: () => void; /** Gate the subscription (e.g. until an id is known). Default `true`. */ enabled?: boolean; } /** * Subscribe a plugin surface to host client-push events for `groups`. * * Inert (no-op) when no host channel is present (standalone/mock), when `enabled` is * false, or when `groups` is empty. Re-subscribes when the group set changes and * unsubscribes on unmount. Callback identities are held in refs, so passing new * inline `onEvent`/`onResync` closures every render does NOT churn the subscription. */ declare function useClientPushSubscription(options: UseClientPushSubscriptionOptions): void; /** Current-user identity for a mounted plugin surface, bound by the host. */ interface HostIdentityUser { id: string; firstName: string; lastName: string; fullName: string; isExternal: boolean; } /** One resolved permission grant (bitMask over the plugin's PermissionMask bits). */ interface HostPermission { groupCode: string; bitMask: number; } /** Host-provided identity context for the mounted surface. */ interface HostIdentity { /** null while host auth is still loading (see isLoading) OR when unauthenticated. */ user: HostIdentityUser | null; /** true while the host's /auth/user resolution is in flight — disambiguates loading from unauthenticated. */ isLoading: boolean; /** The mounted surface's OWN grant only, or null when the user has no grant for this extension. */ permission: HostPermission | null; /** The mounted surface's own extension groupCode, host-bound from the trusted manifest. */ extensionGroupCode: string; /** * The active organisation id for the mounted surface, or null while host auth is loading / * unauthenticated. Host-bound from the SPA's active-organisation context. Surfaces that scope * realtime subscriptions or org-keyed queries read this (e.g. the chat client-push gate); it is * NOT a security token — the plugin backend derives org from the server session independently. */ organisationId: string | null; } /** * `null` = no host channel (standalone/mock, or a host that predates the identity seam) → * {@link useHostIdentity} returns null and the plugin falls back to its deny-by-default path. * Provided by {@link ExtensionRuntimeProvider}'s optional `identity` prop. */ declare const HostIdentityContext: react.Context; /** Returns the host identity for the mounted surface, or null when no host context is present. */ declare function useHostIdentity(): HostIdentity | null; /** * A host-supplied realtime subscription source for a plugin. * * The SDK keeps this intentionally dumb — it only calls `source.subscribe`. * All SignalR wiring, extensionId filtering, and connection lifecycle management * live host-side (Task 6 in coreconnect-web). This lets the SDK be tested with * a simple fake source. */ interface PluginRealtimeSource { /** * Subscribe to notifications whose `typeCode` matches the given value. * * @param typeCode The application-level event type code to filter on * (e.g. `"HelpdeskTicketCreated"`). Filtering by * extensionId is the host's responsibility. * @param handler Called with the raw notification payload whenever a * matching notification arrives. * @returns An unsubscribe function. Calling it removes this handler. */ subscribe(typeCode: string, handler: (payload: unknown) => void): () => void; } /** * React context carrying the plugin's active {@link PluginRealtimeSource}. * * `null` is the explicit "not provided" sentinel — hooks must treat null as a * clean no-op (dev/mock/no-connection) rather than an error. * * Provided by {@link ExtensionRuntimeProvider} when the host passes a * `realtime` prop; consumed by `usePluginRealtimeSource()`. */ declare const PluginRealtimeContext: react.Context; /** * Returns the {@link PluginRealtimeSource} from context, or `null` when none * is wired (dev/mock environments, unit tests that only care about MCP). * * Hooks built on top of this (e.g. `usePluginRealtime` in `plugin-ui`) should * skip their subscription entirely when this returns `null`. */ declare function usePluginRealtimeSource(): PluginRealtimeSource | null; interface ExtensionRuntimeProviderProps { transport: McpTransport; /** * Optional host realtime channel consumed by {@link useClientPushSubscription}. * Absent (or `null`) in standalone/mock hosts and hosts that predate client-push, * in which case the hook is inert. The host binds this channel to the mounted * surface's trusted extension + organisation identity. */ clientPush?: ClientPushChannel | null; /** * Optional host identity/permission context consumed by {@link useHostIdentity}. * Absent (or `null`) in standalone/mock hosts and hosts that predate this seam, * in which case the hook is inert. The host binds this to the mounted surface's * trusted extension identity and forwards only that extension's own grant. */ identity?: HostIdentity | null; /** * Optional realtime subscription source supplied by the host. * * When provided, descendant components can call `usePluginRealtimeSource()` * to obtain it and subscribe to push notifications. When omitted (dev/mock * environments or plugins that don't need realtime), the context defaults * to `null` and consumers no-op cleanly. */ realtime?: PluginRealtimeSource; children?: ReactNode; } /** * Wrap a plugin's React tree so descendant {@link useMcpResource} and * {@link useMcpTool} calls resolve a default transport without having to * thread it through every component. * * Hooks still accept a per-call `transport` override, which takes precedence * over the context value — useful for tests and for plugins that want to * shard work across multiple hosts. */ declare function ExtensionRuntimeProvider({ transport, clientPush, identity, realtime, children }: ExtensionRuntimeProviderProps): ReactNode; /** * Internal helper used by the hooks. Returns the explicit override when * supplied, otherwise falls back to the context. Throws a deterministic * error if neither is available so misconfiguration fails loudly at the * first render rather than producing silent no-ops. */ declare function useExtensionRuntimeTransport(override?: McpTransport): McpTransport; /** * Non-throwing variant of {@link useExtensionRuntimeTransport}: returns the * resolved transport, or `null` when neither an override nor a provider is * present. For optional, fire-and-forget consumers (e.g. an auto-injected * org-format sync) that must degrade to a no-op rather than crash a surface that * renders without a host transport — a standalone/mock-mode run or an isolated * unit test. */ declare function useOptionalExtensionRuntimeTransport(override?: McpTransport): McpTransport | null; interface UseMcpResourceOptions { /** * Override the transport resolved from {@link ExtensionRuntimeProvider}. * Primarily intended for tests and advanced multi-host scenarios. */ transport?: McpTransport; } interface UseMcpResourceResult { data: T | undefined; error: Error | undefined; loading: boolean; /** Re-run the fetch against the current URI. Stable across renders. */ refetch: () => void; } /** * Subscribe to an MCP resource by URI. * * The hook fetches via the resolved {@link McpTransport} on mount and whenever * the URI changes. Each fetch is cancellable: when the component unmounts or * the URI changes, the in-flight call is aborted via an {@link AbortController} * so stale responses cannot overwrite later state. * * `refetch` re-runs the fetch against the latest URI. The returned callback is * stable across renders so it is safe to include in `useEffect` dependency * arrays. */ declare function useMcpResource(uri: string, opts?: UseMcpResourceOptions): UseMcpResourceResult; interface UseMcpToolOptions { /** * Override the transport resolved from {@link ExtensionRuntimeProvider}. * Primarily intended for tests and advanced multi-host scenarios. */ transport?: McpTransport; } interface UseMcpToolResult { /** * Invoke the tool. Each call gets a fresh {@link AbortController} that is * aborted on unmount so unresolved promises cannot keep state alive after * the component is gone. */ invoke: (req: TReq) => Promise; loading: boolean; error: Error | undefined; } /** * Hook returning a callback that invokes an MCP tool by name through the * resolved {@link McpTransport}. * * `loading` and `error` track the most recent in-flight invocation. Callers * may also `await` the returned promise directly — failures are both rejected * to the caller AND surfaced via `error` so component-level UI can react. * * `invoke` is a stable reference across renders for the same `toolName` and * transport, making it safe to use inside `useEffect`/`useCallback` deps. */ declare function useMcpTool(toolName: string, opts?: UseMcpToolOptions): UseMcpToolResult; /** * Result shape returned by {@link useMcpQuery}. Mirrors the host-app * `react-query`-style envelope so consumers can render loading / error / * data states declaratively. `refetch` is stable across renders. */ interface UseMcpQueryResult { data: TRes | undefined; loading: boolean; error: Error | undefined; /** Re-run the fetch against the current args. Stable across renders. */ refetch: () => void; } /** * Options for {@link useMcpQuery}. */ interface UseMcpQueryOptions { /** * Gate the fetch on a guard. When false, the hook returns idle state and * never invokes — important because hooks must be called unconditionally * at the top of the component, so an `enabled` flag is the standard way * to express "fetch only when a selection is present" without violating * the rules of hooks. Defaults to `true`. */ enabled?: boolean; } /** * Auto-fetch wrapper over the imperative {@link useMcpTool}. Fires the tool * call on mount AND whenever the serialised `args` change. Use for * read-shaped MCP tool calls that back a component's render data; for * mutations, call {@link useMcpTool} directly and trigger * `invoke(...)` from event handlers. * * Why this lives alongside {@link useMcpTool}: many plugin MCP tools are * effectively queries (`list-tasks`, `get-pending-approvals`, …) — paginated * lookups that change with filter inputs. The base hook's imperative * `invoke` signature is correct for mutations but ergonomically wrong for * reads, where every consumer ends up writing the same `useEffect` + * AbortController + cancellation-on-unmount boilerplate. This hook absorbs * that boilerplate once. * * @template TArgs The request shape sent to the tool. * @template TRes The response shape the tool returns. */ declare function useMcpQuery(toolName: string, args: TArgs, options?: UseMcpQueryOptions): UseMcpQueryResult; /** * Envelope shape that covers both `{ items: T[] }` and `{ rows: T[] }` * conventions emitted by platform / plugin MCP read tools. Optional both * sides so empty responses still type-check. */ interface ItemsResponse { items?: T[]; rows?: T[]; } /** * Normalise an {@link ItemsResponse} envelope to a plain array. * Returns `[]` when the response is `undefined` (typical pre-first-fetch * state) or when neither field is populated. */ declare function unwrapItems(response: ItemsResponse | undefined): T[]; /** * Configuration accepted by {@link defineDeclarativePlugin}. * * A Contract A (host-rendered) plugin contributes a map of resource URIs to * declarative SDUI trees. The host fetches a resource by URI and renders it * against the v1 vocabulary. */ interface DeclarativePluginConfig { resources: Record; } /** * Configuration accepted by {@link defineEthisysPlugin}. * * `renderMode` is constrained to the protocol's {@link RenderMode} enum so * authoring mistakes (`"iframe"`, `"webview"`, …) are caught at compile time. * `mount` is generic so Contract B remote-runtime authors can attach their * own mount surface without losing type information at the call site. */ interface EthisysPluginConfig { renderMode: RenderMode; mount?: TMount; } /** * Author-facing identity helper for a Contract A (host-rendered) declarative * plugin definition. * * The helper performs no runtime work — it exists purely so authoring sites * receive precise type inference and editor tooling against the protocol's * {@link SduiNode} contract. The returned value is the exact same reference * the caller passed in. */ declare const defineDeclarativePlugin: (cfg: DeclarativePluginConfig) => DeclarativePluginConfig; /** * Author-facing identity helper for a generic EthisysCore plugin definition. * * The helper performs no runtime work. It constrains `renderMode` to the * protocol's {@link RenderMode} enum and preserves the inferred type of an * optional `mount` surface, so Contract B authors can pass through their own * mount object without widening it to `unknown`. */ declare const defineEthisysPlugin: (cfg: EthisysPluginConfig) => EthisysPluginConfig; /** * Contract B (worker remote-runtime) plugin-side React root. * * # The problem this solves * * A Contract B plugin runs in a sandboxed Web Worker — no `document`, no * `window`, no DOM. The host owns rendering: the worker constructs a * `RemoteElement` tree via `@remote-dom/core`, mutations forward over a * `MessagePort`, the host receives them and commits them to a real React tree * (see `coreconnect-web/src/extensions/runtime/WorkerSurfaceMount.tsx` for the * receiver side). * * Until now plugin authors had to hand-author the `RemoteElement` construction * + mutation forwarding manually. `@remote-dom/react` ships only the host-side * primitives (`createRemoteComponent`, `RemoteRootRenderer`, etc.) — there is * no worker-side React reconciler in the package. This helper provides one. * * # What this is * * The public surface (`createRemoteRoot(port, options)`) plus a working * `react-reconciler` HostConfig that commits to a Remote DOM tree and * forwards mutation records over the `MessagePort` to the host receiver. * Authors call `root.render()` and the host's `WorkerSurfaceMount` * sees the result. * * # What's NOT plumbed yet (Phase 1 limitation) * * Event-listener round-trip. The host transport currently has no * `ethisys:remotedom:call` channel — `WorkerRemoteDomTransport` only * forwards `ethisys:remotedom` payloads from worker → host, never the * other direction. So a worker-side `onClick={() => ...}` is **registered * locally** but the host can't call it. The reconciler retains every * listener on its in-memory `RemoteElementInstance` so the wiring is ready * the moment the call channel lands; until then, plugin authors should * keep interactive surfaces on Contract A. * * # The API * * ```ts * import { createRemoteRoot } from "@ethisyscore/extension-runtime/plugin"; * * export async function activate(port: MessagePort): Promise { * const root = createRemoteRoot(port); * root.render(); * } * ``` * * `` is plain React. Any component shipped by the host's frozen * import-map allowlist (the closed Contract B primitive vocabulary — * `Button`, `DataTable`, `Form`, `Card`, `Tabs`, `Select`, `Alert`, etc.) * renders. The reconciler walks the React tree and commits to a * `RemoteRootElement`; mutation records forward over the port; the host's * `RemoteReceiver` commits the result into the real React tree. * * # Wire shape * * Every reconciler mutation is posted as * `{ type: "ethisys:remotedom", payload: RemoteMutationRecord[] }` to match * `WorkerRemoteDomTransport`'s `RemoteDomMessage` contract — the host * extracts `payload` and feeds it straight into `receiver.mutate(records)`. * `BatchingRemoteConnection` (controlled via `options.batchMutations`) * collapses contiguous mutations into a single port post per commit. */ /** * Options for {@link createRemoteRoot}. Reserved for forward compatibility — * Phase 1 ships with zero required options. Adding fields here is additive; * removing them is a breaking change. */ interface CreateRemoteRootOptions { /** * When supplied, the reconciler batches contiguous mutations into a single * `port.postMessage` rather than firing one per mutation. Default `true` * — measurably reduces host-side commit cost on the first render of a * non-trivial tree. */ batchMutations?: boolean; /** * Override the connection factory. Reserved for tests; production callers * never pass this. The factory's contract is "build a RemoteConnection * that forwards mutations over the supplied port"; the default uses * `createRemoteConnection` from `@remote-dom/core`. */ connectionFactory?: (port: MessagePort) => RemoteConnection; } /** * Plugin-side React root. Mirrors the shape of `ReactDOMClient.Root` so * authors familiar with `createRoot().render(...)` find the same API on the * worker side. */ interface RemoteRoot { /** * Render a React element tree against the worker's `RemoteRootElement`. * The reconciler commits to the root, the mutation observer forwards * mutations over the port, the host re-renders. Idempotent — calling * `render` twice with the same element is fine; the reconciler dedupes. */ render(element: ReactNode): void; /** * Tear down the reconciled tree and stop forwarding mutations. Authors * should call this from a `Symbol.dispose` or equivalent when the worker * is shutting down — leaking the reconciler holds the `MessagePort` open * and prevents the worker from being collected. */ unmount(): void; } /** * Construct a plugin-side React root that commits to a `RemoteRootElement` * and forwards mutations over the supplied `MessagePort`. * * **API stability:** the function signature is stable for Phase 1. The * options bag is forward-compatible (additive only). * * **Implementation status:** the connection + root construction lands in this * commit. The `react-reconciler` `HostConfig` is a scaffold — `render()` * throws a structured error directing authors to the W1A tracking issue. * Authors should treat this commit as "the API is locked, the reconciler is * being authored." See follow-on commits on the `feature/contract-b-create-remote-root-w1a` * branch. */ declare function createRemoteRoot(port: MessagePort, options?: CreateRemoteRootOptions): RemoteRoot; /** * W1B — MessagePort-backed McpTransport for Contract B (worker remote-runtime) * plugins. * * The plugin-side React hooks (`useMcpResource`, `useMcpTool`) consume an * {@link McpTransport} — an abstraction over the host call. Contract A * (host-rendered) plugins pick up the host-injected transport via * `ExtensionRuntimeProvider`. Contract B plugins run in a Web Worker with * no shared object surface — the only channel is the `MessagePort` the * host transferred via `activate(port)`. This helper bridges the two * worlds: it exposes the {@link McpTransport} contract on the worker side * and serialises every call into a request/response envelope over the * port. * * # The protocol on the wire * * The wire shape mirrors `WorkerRemoteDomTransport` (in `host/worker/transport.ts`) * one-for-one — that transport is the host receiver and decides what a "well- * formed" message looks like. Two request shapes, two `:result`-suffixed reply * shapes, one abort envelope. Each call mints a fresh request id (`req-{n}`). * * ```jsonc * // worker → host * { type: "ethisys:mcp:getResource", id: "req-3", uri: "tickets://home" } * { type: "ethisys:mcp:invokeTool", id: "req-4", name: "tickets:open", args: { ... } } * * // host → worker (matching id, suffixed type) * { type: "ethisys:mcp:getResource:result", id: "req-3", ok: true, data: { uri, data } } * { type: "ethisys:mcp:invokeTool:result", id: "req-4", ok: false, error: "..." } * ``` * * Requests honour the optional `AbortSignal`. On abort, the transport posts * a `{ type: "ethisys:mcp:abort", id }` envelope so the host can cancel * in-flight work, then rejects the pending promise with an `AbortError`-shaped * `Error` so the consuming hooks see the same shape as native fetch cancellation. * * # Authoring shape * * ```ts * export async function activate(port: MessagePort): Promise { * const transport = createPortMcpTransport(port); * const root = createRemoteRoot(port); * root.render( * * * , * ); * } * ``` * * `` uses `useMcpResource` / `useMcpTool` as it would on the host * side; the transport translates each call into the port envelope. */ /** * Options for {@link createPortMcpTransport}. Reserved for forward * compatibility — the public surface is empty in Phase 1. */ interface CreatePortMcpTransportOptions { /** * Override the request-id generator. Reserved for tests so they can * make request ids deterministic. Production callers never pass this. */ requestIdFactory?: () => string; /** * Override the port's `addEventListener` / `removeEventListener` / * `postMessage` triple. Reserved for tests so the transport can be * exercised without instantiating a real MessageChannel. */ portShimForTests?: PortShim; } /** * Minimal subset of the MessagePort surface the transport actually uses. * Exposed so tests can construct a polyfill without faking the full * MessagePort. */ interface PortShim { addEventListener(type: "message", listener: (event: { data: unknown; }) => void): void; removeEventListener(type: "message", listener: (event: { data: unknown; }) => void): void; postMessage(value: unknown, transfer?: Transferable[]): void; } /** * Construct an {@link McpTransport} backed by a MessagePort. * * @param port The host-transferred MessagePort for the worker surface. * @param options Reserved for forward compatibility / test injection. * * @returns A transport implementation honouring the {@link McpTransport} * contract — fetch a resource, invoke a tool, observe abort * signals, settle the promise on the host's reply envelope. */ declare function createPortMcpTransport(port: MessagePort | PortShim, options?: CreatePortMcpTransportOptions): McpTransport; /** * React context carrying the plugin's active {@link PortBridgeClient}. * Provided by the host mount (WorkerMockHost in dev, real bridge in production) * and consumed by `useBridgeTheme`, `useBridgeLocale`, and the `plugin-ui` hooks. */ declare const BridgeClientContext: react.Context; /** * Returns the bridge client from context, throwing a clear error when missing. * Used by the `useBridge*` hooks to fail loudly on misconfiguration. */ declare function useBridgeClient(): PortBridgeClient; /** * Subscribe to host theme pushes. Returns the most recently pushed theme, * or `undefined` before the first push (e.g. during initial render before * the host has sent its first `BRIDGE_PUSH_THEME` message). * * The hook re-registers with the bridge client if the client identity changes * (e.g. on hot-reload of the mock host in dev), keeping state consistent. */ declare function useBridgeTheme(): ThemePayload | undefined; /** * Subscribe to host locale pushes. Returns the most recently pushed locale * and text direction, or `undefined` before the first push. */ declare function useBridgeLocale(): LocalePayload | undefined; interface UseFrontendSessionTokenResult { /** The compact-serialised JWT, or null while loading / on error. */ token: string | null; /** True while the first fetch (or a refresh) is in-flight. */ isLoading: boolean; /** Set when the bridge rejects; null otherwise. */ error: Error | null; } /** * Returns the current FE-session token fetched from the host bridge. * Schedules silent refresh 30 s before the token expires. * * @param transport The {@link McpTransport} to use. Typically the context * transport from the extension runtime provider. */ declare function useFrontendSessionToken(transport: McpTransport): UseFrontendSessionTokenResult; /** * Pure JWT-payload decode helper (WI 5160 — F-AUTH-SEAM FE face). * * Extracts and JSON-parses the middle (payload) segment of a compact JWT. * URL-safe base64url characters (`-` → `+`, `_` → `/`) are normalised and * missing `=` padding is restored before passing to `atob`. * * This helper is intentionally side-effect-free and React-free so it can be * unit-tested without a DOM environment. * * **Security note:** this function does NOT validate the JWT signature — * the host already validated the token at mint time. The FE only reads claims. * * @param token A compact-serialised JWT (`header.payload.signature`) or null. * @returns The parsed payload object, or `{}` if the token is absent, malformed, * has fewer than 3 segments, or the payload is not valid JSON. */ declare function decodeJwtPayload(token: string | null): Record; interface UseAuthResult { /** The caller's user ID from the FE-session JWT, or null while loading / on error. */ currentUserId: string | null; /** * Returns true if the caller has the given short-code permission. * Always returns false while the token is loading or absent (fail-closed). * * @param shortCode The exact permission short code, e.g. `"timeslip.read"`. */ hasPermission(shortCode: string): boolean; /** True while the first fetch (or a silent refresh) is in-flight. */ isLoading: boolean; /** Set when the bridge rejects; null otherwise. */ error: Error | null; } /** * Returns the caller's identity and permission gate derived from the * FE-session token. * * @param transport The {@link McpTransport} to use. Typically the context * transport from the extension runtime provider. */ declare function useAuth(transport: McpTransport): UseAuthResult; export { BridgeClientContext, type ClientPushChannel, ClientPushContext, type ClientPushEvent, type ClientPushSubscribeOptions, type CreatePortMcpTransportOptions, type CreateRemoteRootOptions, type DeclarativePluginConfig, type EthisysPluginConfig, ExtensionRuntimeProvider, type ExtensionRuntimeProviderProps, type HostIdentity, HostIdentityContext, type HostIdentityUser, type HostPermission, type ItemsResponse, LocalePayload, McpTransport, PluginRealtimeContext, type PluginRealtimeSource, PortBridgeClient, type PortShim, type RemoteRoot, ThemePayload, type UseAuthResult, type UseClientPushSubscriptionOptions, type UseFrontendSessionTokenResult, type UseMcpQueryOptions, type UseMcpQueryResult, type UseMcpResourceOptions, type UseMcpResourceResult, type UseMcpToolOptions, type UseMcpToolResult, createPortMcpTransport, createRemoteRoot, decodeJwtPayload, defineDeclarativePlugin, defineEthisysPlugin, unwrapItems, useAuth, useBridgeClient, useBridgeLocale, useBridgeTheme, useClientPushSubscription, useExtensionRuntimeTransport, useFrontendSessionToken, useHostIdentity, useMcpQuery, useMcpResource, useMcpTool, useOptionalExtensionRuntimeTransport, usePluginRealtimeSource };