/** * React entry point (`@guuey/mcp-apps-host/react`). * * `` is the one React-coupled surface — the root subpath stays * React-free (narrowing, rehydration, the relay, `attachViewHost` itself), * so server-side consumers never import React at all. The component is a * CONVENIENCE composition of the framework-agnostic primitive: iframe * creation + the sandbox invariant + the handshake + lifecycle. A host * that needs a different composition (a transcript renderer, a non-React * surface) uses `attachViewHost` directly. * * ## Sandbox posture (invariant, not preference) * * `sandbox="allow-scripts"` WITHOUT `allow-same-origin`: a `srcdoc` frame * inherits its embedder's origin, so granting both would run * agent-generated HTML AS the embedding page — reach into its DOM and its * signed-in session, an XSS by construction. Dropping `allow-same-origin` * puts the document in an opaque origin instead. Extra flags ride ON TOP * via {@link GuueyViewProps.dangerouslyAddSandboxFlags} — named the way it * is because every flag it adds widens what agent-generated HTML can do. * `allow="clipboard-write"` is delegated by default: generated views own * copy buttons, and without the delegation every one of them silently * no-ops inside the opaque origin. * * ## Who paints which state * * While `"negotiating"`, the component shows a small non-blocking status * line — never a bare blank frame (guuey#186 audit). On `"connected"` the * view owns its pixels (and its own failures) and the line disappears. On * `"no-handshake"` the meaning is channel-aware: a `"ggui"` shell always * negotiates, so silence is a boot failure and is labeled as one; an * `"inline"` card is arbitrary tenant HTML with no handshake obligation, * so the status line simply retires and the document stands as rendered. */ import { type CSSProperties, type ReactNode } from "react"; import { type AttachViewHostConfig } from "./view-host.js"; import type { ViewCspDiagnosis, ViewHostPhase } from "./view-host-protocol.js"; import type { ResolvedViewMount } from "./card-mount.js"; import { type ViewHostStrings } from "./view-strings.js"; export { attachViewHost, viewDocumentHtml, MESSAGE_ANSWER_CAP_MS } from "./view-host.js"; export type { AttachViewHostConfig, UserMessageDelivery, ViewCspEvents, ViewFrameLike, ViewHostEvents } from "./view-host.js"; export { attachSandboxPageDelivery, isSandboxProxyReady, SANDBOX_PROXY_READY_METHOD, SANDBOX_RESOURCE_READY_METHOD, type SandboxPageDeliveryConfig, } from "./sandbox-page.js"; export type { ViewCspDiagnosis, ViewCspOrigins, ViewHostPhase } from "./view-host-protocol.js"; export type { ResolvedViewMount, ViewMount, ViewMountChannel } from "./card-mount.js"; export { defaultViewHostStrings } from "./view-strings.js"; export type { ViewHostStrings } from "./view-strings.js"; export interface GuueyViewProps extends Pick { /** The resolved card to mount (see `toolResultViewMount`/`resolveViewMount`). */ mount: ResolvedViewMount; /** * Opt into the TWO-ORIGIN mount: instead of `srcdoc`, the frame loads * this host-served sandbox page (guuey's `/mcp-app-sandbox` pattern — the * caller builds the full URL, channel/app query included) and the * document is delivered over the page's relay protocol * (`attachSandboxPageDelivery`). Why: a `srcdoc` frame INHERITS the * embedder's CSP, so its egress confinement is whatever the page happens * to carry; the sandbox page is served WITH the per-request CSP that * confines the mount — and the untrusted document lands in the page's * own inner opaque frame, never in this one. In this mode the frame's * `sandbox` gains `allow-same-origin` — REQUIRED and safe: the frame * holds the cross-origin RELAY PAGE (which must run as its real origin * for its CSP + referrer checks to mean anything), never agent HTML. * The page must be a genuinely different origin; a same-origin URL is * refused with a labeled state, never mounted. * * `null` (as opposed to absent) means the two-origin mount is REQUIRED * by the embedder's posture but no page is configured — the mount is * refused with the same labeled state, and srcdoc is NEVER fallen back * to (falling back would silently trade the caller's egress confinement * for the page's CSP; the widget/Studio convergence posture). */ sandboxPageUrl?: string | null; /** * Apply the view's own size reports (`ui/notifications/size-changed` — * spec surface) to the frame: a reported HEIGHT becomes the frame's * height; width stays the container's (a transcript column owns its * width). Default OFF — the primitive changes nothing for existing * hosts; a caller's {@link AttachViewHostConfig.onSizeChanged} observer * fires either way. */ autoResize?: boolean; /** * The tallest frame `autoResize` may apply, in px (guuey#992). Default: * the host window's `innerHeight` at the time of the report — a card * taller than the viewport scrolls inside its frame. Reports are also * judged by the echo detector in `auto-resize.ts`: a view whose body is * viewport-bound measures the frame's own height back to the host, and a * host that applied every report verbatim grew a frame to 86,816 px. */ maxHeight?: number; /** * Sandbox flags appended to the safe default (`allow-scripts`). Every * entry widens what agent-generated HTML may do — `allow-same-origin` * in particular hands it the embedder's origin. Prefer leaving unset. * In `sandboxPageUrl` mode these forward to the INNER frame via the * page's relay (which strips `allow-same-origin` regardless). */ dangerouslyAddSandboxFlags?: string[]; /** Permissions-Policy delegation for the frame. Default `clipboard-write`. */ allow?: string; /** Accessible frame title. Default "Generated view". */ title?: string; className?: string; style?: CSSProperties; /** Observe the negotiation phase (the same states the default UI labels). */ onPhaseChange?: (phase: ViewHostPhase) => void; /** * Replace the default status/failure line for a phase. Return `null` for * "render nothing". The default: a quiet loading line while * `"negotiating"`; a labeled failure for `"no-handshake"` on the * `"ggui"` channel; nothing once `"connected"` (the view owns its * pixels) and nothing for a silent `"inline"` card. The second argument * is the CSP diagnosis when the tripwire caught one (see * {@link AttachViewHostConfig.cspOrigins}) — the default label folds it * in; a custom renderer decides how to show it. * * To change only the WORDS, prefer {@link GuueyViewProps.strings}: this * hook makes a host reimplement the CSP/channel branching to do it. */ renderStatus?: (phase: ViewHostPhase, diagnosis?: ViewCspDiagnosis) => ReactNode; /** * Override the default status line's copy (guuey#1325). Merged over * {@link defaultViewHostStrings}, so a partial override is enough. */ strings?: Partial; } /** * Mount a resolved view and play the MCP Apps Host for it. See the module * docblock for the sandbox and state contracts. */ export declare function GuueyView(props: GuueyViewProps): ReactNode; //# sourceMappingURL=react.d.ts.map