/** * The Host role's ACTION side (guuey#158) — the `tools/call` sibling of * `reader.ts`. A mounted card's sandbox posts a runtime action to the host * (SEP-1865 / ggui's relay-host contract); the host relays it over an * AUTHENTICATED transport it owns, and hands the result back in-band. Two * invariants mirror the reader: * * - the relay NEVER throws into the sandbox bridge: allowlist miss, * transport failure, and un-narrowable answers all collapse to an * in-band `isError` result the card can display; * - runtime re-narrowing, not trust: transports are host-supplied and the * upstream answer is wire data — every content arm is re-checked before * it crosses into the sandbox. */ /** The wire arms a relay hands back to the sandbox — re-narrowed, never trusted. */ export type McpToolCallContent = { type: "text"; text: string; } | { type: "image"; data: string; mimeType: string; } | { type: "resource"; resource: { uri: string; mimeType?: string; } & ({ text: string; } | { blob: string; }); }; /** * `structuredContent` is protocol-open by design (the MCP spec types it as * an arbitrary JSON object) — the index signature is the honest wire type, * not an erasure of a known shape. */ export type McpToolStructuredContent = { [key: string]: unknown; }; /** * The SEP-1865 CallToolResult surface a host hands back to the sandbox. * A `type` alias, deliberately: the MCP SDK's own result types carry Zod * passthrough index signatures, and only type aliases (never interfaces) * get the implicit index signature that makes this assignable to them. */ export type McpToolCallResult = { content: McpToolCallContent[]; isError?: boolean; structuredContent?: McpToolStructuredContent; }; /** * The runtime tools a card sandbox may RELAY over `tools/call` — the * client-side twin of the server allowlist (defense in depth: the proxy * enforces it again). Exactly ggui's iframe-runtime surface, all declared * `_meta.ui.visibility: ['app']` (never model-callable): * * - `ggui_runtime_submit_action` — the user's gesture (the SEMANTIC one); * - `ggui_runtime_refresh_ws_token` — live-channel credential refresh * (the wsToken TTL is 180 s; without the relay a view on SSE/polling * dies at that mark — guuey#220); * - `ggui_runtime_pull` — the host-relayed polling rung where the * sandbox's own transports are blocked (bridge-pull, terminal rung); * - `ggui_runtime_telemetry` — the card's own health events, relayed only * as the closed kind set {@link UI_TELEMETRY_KINDS}, filtered here before * the relay calls the door. * * Three of these are runtime plumbing whose arguments are opaque runtime * state — see {@link UI_SEMANTIC_ACTION_TOOLS} for the ONLY set a host may * treat as "the user did something". */ export declare const UI_ACTION_TOOLS: ReadonlySet; /** The card-health telemetry tool. Never a user gesture. */ export declare const UI_TELEMETRY_TOOL = "ggui_runtime_telemetry"; /** * What an admitted telemetry kind may carry as `detail`: nothing (`none`), or * one exact, closed shape with no free text — three booleans (`boot-path`), a * transport kind and a boolean (`subscribe`), or a render session id * (`render-session-id`). */ export type UiTelemetryDetailRule = "none" | "boot-path" | "subscribe" | "render-session-id"; /** * The closed set of health-event kinds a card may report through the relay: * event names and ggui's own ids only, no free text and no user identity. * Exact strings, never a prefix. The server doors admit exactly the same set * and refuse a whole call that carries anything else; this twin filters * events before the door, so a conforming host never sends a refusable call. */ export declare const UI_TELEMETRY_KINDS: ReadonlyMap; /** The per-call event cap the doors enforce. */ export declare const MAX_UI_TELEMETRY_EVENTS = 40; /** * The telemetry call the host relays: the card's `sessionId` (when it is a * string) and only the admitted events, oldest first, at most * {@link MAX_UI_TELEMETRY_EVENTS}. Whole events are dropped, never edited. * `undefined` when nothing is left to relay. */ export declare function admittedTelemetryArguments(args: McpToolStructuredContent | undefined): McpToolStructuredContent | undefined; /** * The strict subset of {@link UI_ACTION_TOOLS} that carries a USER gesture — * the only calls a host may project into user-facing affordances (composer * staging, "the user selected X" copy, telemetry as intent). Every other * relayed tool is runtime plumbing whose payload must NEVER reach the user * verbatim (guuey#215/#218: a staged `sessionId …` was exactly that leak). * A tool that is relayable is NOT thereby semantic; a host that widens * this set is asserting the wire semantics of the new name. */ export declare const UI_SEMANTIC_ACTION_TOOLS: ReadonlySet; /** The in-band answer for anything the relay cannot (or will not) do. */ export declare const UI_ACTION_UNAVAILABLE_TEXT = "This action isn't available right now."; /** * The in-band `isError` result for an action that cannot be performed — * the relay's own refusals use it, and `attachViewHost` posts it when an * embedder-supplied relay hook rejects (the view is always answered). */ export declare function unavailableToolCallResult(): McpToolCallResult; /** * Narrow an untrusted `tools/call` answer to the arms the sandbox may see. * Unknown content arms are DROPPED (never forwarded opaque); a value that * is not result-shaped at all is `undefined` (the relay answers in-band). */ export declare function asToolCallResult(value: unknown): McpToolCallResult | undefined; /** The host-supplied transport {@link createMcpUiActionRelay} assembles over. */ export interface CreateMcpUiActionRelayDeps { /** * One `tools/call` bound to the mounted card's locator `uri` over the * host's authenticated channel. Returns the raw result (narrowed here), * or `undefined` when the upstream denied/lost the session. Throwing is * treated as unavailable. */ callTool: (uri: string, name: string, args: McpToolStructuredContent | undefined) => Promise; /** * Fired ONCE when a card locator's `ggui_runtime_pull` circuit OPENS * (guuey#1249 item 4) — the live session is unrestorable (N consecutive * both-doors-gone pulls). The complement to leg 1's storm-break: the host * surfaces a visible "this session ended — start a new chat" state and drops * the stale thread, so a tripped circuit is never a silent frozen card. * Optional: a host that only wants the storm bounded omits it. * * Once per trip: it fires again only after {@link onSessionRestored} has * cleared an earlier verdict for the same locator and the circuit trips anew. */ onSessionUnrestorable?: (resourceUri: string) => void; /** * Fired when a locator whose pull circuit OPENED is seen live again on the * pull rung itself, so the host can withdraw the "session ended" state it * showed. Wiring it is what lets an open circuit recover in place at all: * without it the circuit stays open for the mount (recovery is a fresh * mount), exactly as a host that wires only {@link onSessionUnrestorable} * expects. * * The recovery is evidence on the channel that tripped: a successful * `ggui_runtime_refresh_ws_token` for the locator HALF-OPENS the circuit, * which lets the view's next pull through as a single probe. A probe that * the session answers closes the circuit and fires this, once per close; a * probe that fails leaves it open, and the host hears nothing new (it * already holds the verdict). A token refresh alone never reverses it: the * token channel can work while pulls still fail. */ onSessionRestored?: (resourceUri: string) => void; } /** The request shape a mounted card's `onCallTool` bridge produces. */ export interface UiActionRequest { /** The mounted card's persisted `ui://` locator — the action's scope. */ resourceUri: string; name: string; arguments?: McpToolStructuredContent; } /** * Consecutive `unavailable` pull results (per card locator) that OPEN the * circuit (guuey#1235 leg 1). At the #1233 storm's ~12-18 pulls/min a 3-strike * trip bounds the hammer in ~10-15s; a single good pull resets it, so a * transient blip never trips. */ export declare const PULL_CIRCUIT_THRESHOLD = 3; /** * Logged ONCE when a locator's pull circuit opens — the bounded-storm marker * (sentry's readability ask; the #1233 next-incident is scopeable from it). * The SYMBOL is the handle code and tests grep; its VALUE is the sentence a * builder reads in their own site's console (guuey#1375) — our one prefix, * then what happened and what follows, never an internal token. It says * exactly what the circuit does: the relay stops pulling for that locator, * the host is told the session is unrestorable (`onSessionUnrestorable`), * and nothing retries unattended. */ export declare const UI_ACTION_PULL_CIRCUIT_OPEN = "[guuey] card updates stopped \u2014 the session behind this card could not be restored; the widget reports it, and a new conversation starts fresh"; /** * Logged when an open circuit closes again: the probe pull a successful token * refresh let through was answered, and the host was told * (`onSessionRestored`). It can only happen for a host that wires that signal. */ export declare const UI_ACTION_PULL_CIRCUIT_CLOSED = "[guuey] card updates resumed \u2014 the session behind this card answered again"; /** * Logged when a host's `onSessionUnrestorable` or `onSessionRestored` * callback throws. The relay never rejects into the card, so it goes on; this * line is the page's only trace that its own "session ended" state may now be * out of step with the card. It names the callback and the error's `name`, * never the error's text, which is the host's and can carry anything. */ export declare const UI_ACTION_HOST_CALLBACK_THREW = "[guuey] the page's card-session handler threw \u2014 the card went on, but the page may not show its current state"; /** * Assemble the sandbox-facing action relay from a host transport. The * returned function is shaped for an `onCallTool` bridge: it always * resolves (never rejects), answering in-band. * * guuey#1235 leg 1 — the `ggui_runtime_pull` CIRCUIT BREAK. A card whose live * session died keeps auto-polling on its own interval; each pull `tools/call` * 404s at the pod door → `unavailable` → the sandbox polls again, hammering the * door indefinitely (the #1233 prod storm: 57+ over 15 min). After * {@link PULL_CIRCUIT_THRESHOLD} CONSECUTIVE `unavailable` pull results for a * locator, the circuit OPENS: the relay stops calling the transport for that * locator's pull (fail-fast, no network) — the server storm is bounded. A * single non-`unavailable` pull result CLOSES it (the session recovered). Only * the pull rung is counted — a failed user gesture (`submit_action`) or token * refresh must never trip the auto-poll break, and never opens another rung. * * The complement — telling the USER the session is unrestorable so a tripped * circuit is not a silent freeze — is the `onSessionUnrestorable` signal * (guuey#1249 item 4); this leg only bounds the hammer. * * An OPEN circuit is terminal for the mount unless the host wires * `onSessionRestored`. Then a successful `ggui_runtime_refresh_ws_token` for * the locator HALF-OPENS it: the next pull goes through as one probe, and only * a probe the session answers closes it (see `onSessionRestored`). */ export declare function createMcpUiActionRelay(deps: CreateMcpUiActionRelayDeps): (request: UiActionRequest) => Promise; //# sourceMappingURL=action.d.ts.map