@ggui-ai/protocol 0.1.0-rc.1 → 0.2.0-alpha.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +3 -3
- package/dist/bridge/invoke-agent.d.ts +8 -8
- package/dist/bridge/invoke-agent.d.ts.map +1 -1
- package/dist/bridge/invoke-agent.js +12 -12
- package/dist/envelopes/builders.d.ts +2 -4
- package/dist/envelopes/builders.d.ts.map +1 -1
- package/dist/envelopes/builders.js +2 -6
- package/dist/gadgets/stdlib-gadgets.d.ts +1 -1
- package/dist/gadgets/stdlib-gadgets.js +1 -1
- package/dist/index.d.ts +2 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +4 -3
- package/dist/integrations/mcp-apps.d.ts +354 -711
- package/dist/integrations/mcp-apps.d.ts.map +1 -1
- package/dist/integrations/mcp-apps.js +266 -208
- package/dist/recommended-prompts.d.ts +1 -1
- package/dist/recommended-prompts.js +1 -1
- package/dist/schemas/data-contract.d.ts +4 -3
- package/dist/schemas/data-contract.d.ts.map +1 -1
- package/dist/schemas/data-contract.js +4 -3
- package/dist/schemas/invoke.d.ts +2 -2
- package/dist/schemas/invoke.js +2 -2
- package/dist/schemas/mcp.d.ts +60 -79
- package/dist/schemas/mcp.d.ts.map +1 -1
- package/dist/schemas/mcp.js +93 -116
- package/dist/schemas/sync-check.js +1 -1
- package/dist/stream/stream-parser.d.ts +4 -4
- package/dist/stream/stream-parser.d.ts.map +1 -1
- package/dist/stream/stream-parser.js +9 -9
- package/dist/transport/websocket.d.ts +14 -49
- package/dist/transport/websocket.d.ts.map +1 -1
- package/dist/types/app-config.d.ts +17 -32
- package/dist/types/app-config.d.ts.map +1 -1
- package/dist/types/canvas-lifecycle.d.ts +13 -13
- package/dist/types/canvas-lifecycle.d.ts.map +1 -1
- package/dist/types/canvas-lifecycle.js +3 -3
- package/dist/types/contract-inference.d.ts +4 -7
- package/dist/types/contract-inference.d.ts.map +1 -1
- package/dist/types/data-bindings.d.ts +1 -1
- package/dist/types/data-bindings.d.ts.map +1 -1
- package/dist/types/data-contract.d.ts +10 -10
- package/dist/types/data-contract.d.ts.map +1 -1
- package/dist/types/events.d.ts +14 -56
- package/dist/types/events.d.ts.map +1 -1
- package/dist/types/events.js +1 -8
- package/dist/types/feedback.d.ts +2 -4
- package/dist/types/feedback.d.ts.map +1 -1
- package/dist/types/handshake-suggestion.d.ts +1 -1
- package/dist/types/host-context.d.ts +5 -15
- package/dist/types/host-context.d.ts.map +1 -1
- package/dist/types/host-context.js +1 -1
- package/dist/types/live-channel.d.ts +128 -191
- package/dist/types/live-channel.d.ts.map +1 -1
- package/dist/types/llm-route.d.ts +233 -0
- package/dist/types/llm-route.d.ts.map +1 -0
- package/dist/types/llm-route.js +433 -0
- package/dist/types/llm.d.ts +35 -17
- package/dist/types/llm.d.ts.map +1 -1
- package/dist/types/llm.js +50 -39
- package/dist/types/mcp.d.ts +47 -137
- package/dist/types/mcp.d.ts.map +1 -1
- package/dist/types/mcp.js +2 -2
- package/dist/types/openrouter-models.d.ts +3 -2
- package/dist/types/openrouter-models.d.ts.map +1 -1
- package/dist/types/render-event.d.ts +119 -0
- package/dist/types/render-event.d.ts.map +1 -0
- package/dist/types/render-event.js +38 -0
- package/dist/types/session.d.ts +216 -342
- package/dist/types/session.d.ts.map +1 -1
- package/dist/types/ui-generator.d.ts +8 -9
- package/dist/types/ui-generator.d.ts.map +1 -1
- package/dist/validation/contract-validator.d.ts +60 -4
- package/dist/validation/contract-validator.d.ts.map +1 -1
- package/dist/validation/contract-validator.js +101 -5
- package/dist/validation/reserved-channels.d.ts +1 -1
- package/dist/validation/reserved-channels.js +5 -5
- package/dist/validation/sanitize-error.js +1 -1
- package/dist/validation/schema-compat-invariants.d.ts +2 -2
- package/dist/validation/schema-compat-invariants.js +2 -2
- package/dist/validation/schema-meta-validation.d.ts +1 -1
- package/dist/validation/schema-meta-validation.js +2 -2
- package/dist/validation/schema-subset.d.ts +1 -1
- package/dist/version.d.ts +253 -46
- package/dist/version.d.ts.map +1 -1
- package/dist/version.js +253 -46
- package/package.json +17 -16
- package/dist/navigation/index.d.ts +0 -3
- package/dist/navigation/index.d.ts.map +0 -1
- package/dist/navigation/index.js +0 -1
- package/dist/navigation/stack-navigation.d.ts +0 -55
- package/dist/navigation/stack-navigation.d.ts.map +0 -1
- package/dist/navigation/stack-navigation.js +0 -80
|
@@ -9,8 +9,9 @@
|
|
|
9
9
|
* ```ts
|
|
10
10
|
* import {
|
|
11
11
|
* MCP_APPS_UI_CAPABILITY,
|
|
12
|
-
*
|
|
13
|
-
*
|
|
12
|
+
* GGUI_RENDER_RESOURCE_URI,
|
|
13
|
+
* parseMcpAppAiGguiRenderMeta,
|
|
14
|
+
* type McpAppAiGguiRenderMeta,
|
|
14
15
|
* } from '@ggui-ai/protocol/integrations/mcp-apps';
|
|
15
16
|
* ```
|
|
16
17
|
*
|
|
@@ -20,7 +21,7 @@
|
|
|
20
21
|
* drift is bounded to callers that explicitly import from here.
|
|
21
22
|
*
|
|
22
23
|
* Core still carries two fields that make the bootstrap flow work —
|
|
23
|
-
* `SubscribePayload.bootstrap?: string` and `AckPayload.
|
|
24
|
+
* `SubscribePayload.bootstrap?: string` and `AckPayload.renderToken?:
|
|
24
25
|
* string`. Those are deliberately framed as **general transport bootstrap
|
|
25
26
|
* credentials** (opaque strings), not MCP-Apps-specific. Any future
|
|
26
27
|
* bootstrap mechanism (short-code auto-login, signed-URL bootstrap, etc.)
|
|
@@ -35,26 +36,26 @@ import type { JsonObject, JsonSchema, JsonValue } from '../types/data-contract.j
|
|
|
35
36
|
export declare const MCP_APPS_UI_CAPABILITY: "io.modelcontextprotocol/ui";
|
|
36
37
|
/**
|
|
37
38
|
* The single MCP Apps resource URI ggui exposes for outbound delivery.
|
|
38
|
-
* `
|
|
39
|
+
* `ggui_render` is the sole tool declaration that carries this in its
|
|
39
40
|
* `_meta.ui.resourceUri`. No other ggui tool gets a resource URI —
|
|
40
|
-
* `
|
|
41
|
+
* `ggui_render` is the single outbound entry point.
|
|
41
42
|
*/
|
|
42
|
-
export declare const
|
|
43
|
+
export declare const GGUI_RENDER_RESOURCE_URI: "ui://ggui/render";
|
|
43
44
|
/**
|
|
44
|
-
* MIME type for the `ui://ggui/
|
|
45
|
+
* MIME type for the `ui://ggui/render` resource. Per MCP Apps spec, UI
|
|
45
46
|
* resources carry the `text/html` base type with a `profile=mcp-app`
|
|
46
47
|
* parameter so hosts that don't support MCP Apps don't accidentally
|
|
47
48
|
* render them as plain HTML.
|
|
48
49
|
*/
|
|
49
|
-
export declare const
|
|
50
|
+
export declare const GGUI_RENDER_RESOURCE_MIME: "text/html;profile=mcp-app";
|
|
50
51
|
/**
|
|
51
52
|
* The single `_meta.ui.resourceUri` value ggui uses across every MCP Apps
|
|
52
53
|
* host surface. Exposed as a named constant so tool-declaration code,
|
|
53
54
|
* resource-serving code, and tests all agree on one spelling.
|
|
54
55
|
*/
|
|
55
|
-
export declare const
|
|
56
|
-
/** Resource URI hosts fetch via `resources/read` on a `
|
|
57
|
-
readonly resourceUri: "ui://ggui/
|
|
56
|
+
export declare const GGUI_RENDER_UI_META: {
|
|
57
|
+
/** Resource URI hosts fetch via `resources/read` on a `ggui_render` tool call. */
|
|
58
|
+
readonly resourceUri: "ui://ggui/render";
|
|
58
59
|
/** Only `"model"` — outer agent can call, iframe views cannot. */
|
|
59
60
|
readonly visibility: readonly ["model"];
|
|
60
61
|
};
|
|
@@ -66,525 +67,19 @@ export declare const GGUI_PUSH_UI_META: {
|
|
|
66
67
|
*/
|
|
67
68
|
export type McpAppsToolVisibility = 'model' | 'app';
|
|
68
69
|
/**
|
|
69
|
-
*
|
|
70
|
-
* ggui
|
|
71
|
-
* `
|
|
72
|
-
*
|
|
73
|
-
* **Not model-visible.** Agents SHOULD NOT consume these fields. They are
|
|
74
|
-
* scoped to the view lifecycle and carry credentials the model has no
|
|
75
|
-
* legitimate use for. The split between `structuredContent` (model-facing)
|
|
76
|
-
* and `_meta.ggui.bootstrap` (view-facing) is the protocol-level isolation
|
|
77
|
-
* that keeps WebSocket credentials out of conversation transcripts.
|
|
78
|
-
*
|
|
79
|
-
* **Three boot modes share this single shape**, distinguished by which
|
|
80
|
-
* of the three optional discriminator fields is populated:
|
|
81
|
-
*
|
|
82
|
-
* | Mode | Discriminator | What the view does |
|
|
83
|
-
* | ---------------- | ----------------- | --------------------------------------------------- |
|
|
84
|
-
* | live | `wsUrl` + `token` | Open live-channel WS, subscribe, render agent frames |
|
|
85
|
-
* | static-component | `codeUrl` | Fetch + mount compiled React component, no WS |
|
|
86
|
-
* | system-card | `kind` | Mount built-in system card by registry id, no WS |
|
|
87
|
-
*
|
|
88
|
-
* **Mutual-exclusion contract.** A consumer-acceptable bootstrap MUST
|
|
89
|
-
* carry at least one of `{wsUrl-with-token, codeUrl, kind}`.
|
|
90
|
-
* Multiple may coexist (e.g. a server emitting both `codeUrl`
|
|
91
|
-
* AND `wsUrl+token` lets the runtime pick — current iframe-runtime
|
|
92
|
-
* priority is code-then-system-then-live), but at least one MUST
|
|
93
|
-
* be present. A bootstrap with none is MALFORMED.
|
|
94
|
-
*
|
|
95
|
-
* **Live-mode auth semantics** (only relevant when `wsUrl` + `token`
|
|
96
|
-
* are present):
|
|
97
|
-
* - `token` is session-scoped, short-TTL, single-use on initial
|
|
98
|
-
* `subscribe`. Consumed at the first bootstrap-auth'd subscribe and
|
|
99
|
-
* cannot be reused.
|
|
100
|
-
* - `expiresAt` lets the view skip an obviously-stale bootstrap without
|
|
101
|
-
* a round-trip. Servers reject expired tokens anyway; this is UX
|
|
102
|
-
* sugar, not a security control.
|
|
103
|
-
* - For reconnects, the view should use the longer-lived `sessionToken`
|
|
104
|
-
* the server issues on the successful bootstrap-auth ack — see
|
|
105
|
-
* {@link AckPayload.sessionToken}.
|
|
106
|
-
*
|
|
107
|
-
* **Live-mode example:**
|
|
108
|
-
* ```json
|
|
109
|
-
* {
|
|
110
|
-
* "sessionId": "sess_001", "appId": "app_001",
|
|
111
|
-
* "runtimeUrl": "/_ggui/iframe-runtime.js",
|
|
112
|
-
* "wsUrl": "wss://server.example/ws",
|
|
113
|
-
* "token": "tok_abc", "expiresAt": "2099-01-01T00:00:00.000Z"
|
|
114
|
-
* }
|
|
115
|
-
* ```
|
|
116
|
-
*
|
|
117
|
-
* **Static-component example:**
|
|
118
|
-
* ```json
|
|
119
|
-
* {
|
|
120
|
-
* "sessionId": "sess_001", "appId": "app_001",
|
|
121
|
-
* "runtimeUrl": "/_ggui/iframe-runtime.js",
|
|
122
|
-
* "codeUrl": "https://server.example/code/sha256:abc.js",
|
|
123
|
-
* "codeHash": "sha256:abc...",
|
|
124
|
-
* "themeId": "indigo", "propsJson": "{\"name\":\"Ada\"}"
|
|
125
|
-
* }
|
|
126
|
-
* ```
|
|
70
|
+
* Phase B render-identity collapse — the previously two-slice wire
|
|
71
|
+
* (`ai.ggui/session` + `ai.ggui/stack-item`) is merged into ONE slice
|
|
72
|
+
* (`ai.ggui/render`). Consumers parse with {@link parseMcpAppAiGguiRenderMeta}
|
|
73
|
+
* and read fields directly off the {@link McpAppAiGguiRenderMeta} struct.
|
|
127
74
|
*
|
|
128
|
-
*
|
|
129
|
-
*
|
|
130
|
-
*
|
|
131
|
-
* "sessionId": "sess_001", "appId": "app_001",
|
|
132
|
-
* "runtimeUrl": "/_ggui/iframe-runtime.js",
|
|
133
|
-
* "kind": "loading"
|
|
134
|
-
* }
|
|
135
|
-
* ```
|
|
75
|
+
* Why the merge: every "session" wrapped exactly one stack item post-
|
|
76
|
+
* Phase-A, so the two slices were always activated in lock-step. The
|
|
77
|
+
* pair-holder added ceremony with no signal. Flat is the honest shape.
|
|
136
78
|
*/
|
|
137
|
-
export interface GguiBootstrapMeta {
|
|
138
|
-
/**
|
|
139
|
-
* WebSocket URL the view opens for live mode (e.g.
|
|
140
|
-
* `wss://server.example/ws`). REQUIRED in live mode; absent in
|
|
141
|
-
* static-component and system-card modes. When present, MUST be
|
|
142
|
-
* paired with a non-empty `token` — half-live (one without the
|
|
143
|
-
* other) is MALFORMED.
|
|
144
|
-
*/
|
|
145
|
-
readonly wsUrl?: string;
|
|
146
|
-
/**
|
|
147
|
-
* Short-TTL single-use bootstrap token, passed as
|
|
148
|
-
* `SubscribePayload.bootstrap`. REQUIRED in live mode; absent in
|
|
149
|
-
* the other modes.
|
|
150
|
-
*/
|
|
151
|
-
readonly token?: string;
|
|
152
|
-
/**
|
|
153
|
-
* ISO 8601 UTC timestamp after which the token is no longer accepted.
|
|
154
|
-
* Optional; meaningful only in live mode (the token it pairs with
|
|
155
|
-
* may be unbounded TTL when this is absent).
|
|
156
|
-
*/
|
|
157
|
-
readonly expiresAt?: string;
|
|
158
|
-
/**
|
|
159
|
-
* Session id the token binds to. Repeated here (also present on
|
|
160
|
-
* `structuredContent.sessionId`) so the view has everything it needs
|
|
161
|
-
* to subscribe from `_meta.ggui.bootstrap` alone — no need to cross-
|
|
162
|
-
* reference structuredContent. Keeps the view-side boot code small.
|
|
163
|
-
*/
|
|
164
|
-
readonly sessionId: string;
|
|
165
|
-
/**
|
|
166
|
-
* App (tenant) id the token binds to. Required in the subscribe
|
|
167
|
-
* payload; not necessarily appropriate for structuredContent
|
|
168
|
-
* (tenant identity is app-facing bootstrap metadata, not typically
|
|
169
|
-
* part of an agent's typed output surface).
|
|
170
|
-
*/
|
|
171
|
-
readonly appId: string;
|
|
172
|
-
/**
|
|
173
|
-
* URL of the iframe-runtime bundle the iframe should fetch. The
|
|
174
|
-
* thin-shell HTML's inline JS dynamically appends
|
|
175
|
-
* `<script src={runtimeUrl}>` to load the runtime. Server controls
|
|
176
|
-
* this per-session so the shell works in `srcdoc` iframes (which
|
|
177
|
-
* have no origin of their own) and across local / cloud
|
|
178
|
-
* deployments.
|
|
179
|
-
*
|
|
180
|
-
* OSS `ggui serve` resolves this to `/_ggui/iframe-runtime.js` —
|
|
181
|
-
* same-origin as the MCP server's HTTP listener. Hosted cloud
|
|
182
|
-
* serves from a dedicated CDN route. Either way, the server OWNS
|
|
183
|
-
* the string; the shell does not guess, concatenate, or fall back
|
|
184
|
-
* to a bundled default.
|
|
185
|
-
*
|
|
186
|
-
* Named parties: **server** produces; **thin shell** consumes;
|
|
187
|
-
* **iframe runtime bundle** is what the URL resolves to. Failure
|
|
188
|
-
* mode on fetch error: runtime surfaces a `BUNDLE_FETCH_FAILED`
|
|
189
|
-
* bootstrap failure via `postMessage({type:'ggui:bootstrap-failed',
|
|
190
|
-
* reason, message})` to the parent (C8 commit 3). Absent or empty
|
|
191
|
-
* at parse-time is `BOOTSTRAP_META_MISSING` — the shell rejects
|
|
192
|
-
* the bootstrap without attempting a script load.
|
|
193
|
-
*
|
|
194
|
-
* C8 (2026-04-23) made this required. Pre-C8 servers that emit
|
|
195
|
-
* bootstraps without `runtimeUrl` are incompatible with the
|
|
196
|
-
* post-C8 thin-shell HTML — the shell pivot (`~175` → `~30` LOC
|
|
197
|
-
* wrapper) moved all rendering logic out of the shell into the
|
|
198
|
-
* separately-served iframe-runtime bundle, so the URL is now
|
|
199
|
-
* load-bearing.
|
|
200
|
-
* @public
|
|
201
|
-
*/
|
|
202
|
-
readonly runtimeUrl: string;
|
|
203
|
-
/**
|
|
204
|
-
* Optional polling fallback URL the iframe-runtime fetches when its
|
|
205
|
-
* WebSocket transport is unavailable or fails. Points at the same
|
|
206
|
-
* `/api/bootstrap/<shortCode>` endpoint the iframe used to initially
|
|
207
|
-
* load the bootstrap envelope — polling re-fetches it and the
|
|
208
|
-
* iframe-runtime diffs the `propsJson` field to synthesize
|
|
209
|
-
* `props_update` frames for live re-render.
|
|
210
|
-
*
|
|
211
|
-
* Empirically required for MCP-Apps hosts whose iframe sandbox
|
|
212
|
-
* blocks `wss://` at the CSP layer regardless of our
|
|
213
|
-
* `_meta.ui.csp.connectDomains` declaration (Claude Desktop is the
|
|
214
|
-
* known case; claude.ai Connector honors WS). Without this field,
|
|
215
|
-
* polling has no URL to hit and live updates silently no-op when
|
|
216
|
-
* WS is unavailable.
|
|
217
|
-
*
|
|
218
|
-
* Producer: server's `/api/bootstrap/<shortCode>` endpoint stamps
|
|
219
|
-
* this on the response body — same origin as the request, same
|
|
220
|
-
* shortCode in the path. Consumer: iframe-runtime threads this
|
|
221
|
-
* through `createPropsUpdateHandler({pollingUrl})` so
|
|
222
|
-
* `PollingTransport` has a URL to fetch.
|
|
223
|
-
*
|
|
224
|
-
* Absent → no polling fallback. WS-only mode is fine for hosts
|
|
225
|
-
* whose CSP permits `wss://`.
|
|
226
|
-
*
|
|
227
|
-
* @public
|
|
228
|
-
*/
|
|
229
|
-
readonly pollingUrl?: string;
|
|
230
|
-
/**
|
|
231
|
-
* Names of same-server tools whose `_meta.ui.visibility` includes
|
|
232
|
-
* `"app"` and are therefore directly callable from this iframe via
|
|
233
|
-
* `tools/call` (per MCP-Apps spec §2026-01-26 Visibility rules:
|
|
234
|
-
* "app" = callable by the app from the same server connection only;
|
|
235
|
-
* cross-server tool calls are always blocked).
|
|
236
|
-
*
|
|
237
|
-
* Used by the iframe-runtime as a capability fingerprint — telemetry
|
|
238
|
-
* and debug surfaces consult it to know which tool names are reachable
|
|
239
|
-
* over the host's same-server `tools/call` path (e.g. the iframe-
|
|
240
|
-
* internal `ggui_runtime_submit_action` and `ggui_runtime_sync_context`
|
|
241
|
-
* relays). It is NOT consulted to choose a dispatch routing strategy:
|
|
242
|
-
* every user gesture flows through `ggui_runtime_submit_action`, lands
|
|
243
|
-
* on the per-stack-item pending-events pipe, and is drained by the
|
|
244
|
-
* agent's `ggui_consume` long-poll on the next turn. Actions ALWAYS
|
|
245
|
-
* drive turns through consume — there is no synchronous server-side
|
|
246
|
-
* dispatch in agent-mediated deployments.
|
|
247
|
-
*
|
|
248
|
-
* Producers SHOULD include every same-server-app-visible tool;
|
|
249
|
-
* consumers MUST treat an absent field as an empty list (legacy
|
|
250
|
-
* bootstrap envelopes predating this addition).
|
|
251
|
-
*
|
|
252
|
-
* @public
|
|
253
|
-
*/
|
|
254
|
-
readonly appCallableTools?: readonly string[];
|
|
255
|
-
/**
|
|
256
|
-
* Per-action `nextStep` hint mapping for the active stack item's
|
|
257
|
-
* `actionSpec`. Maps `actionName → toolName` where `toolName` is the
|
|
258
|
-
* value of `actionSpec[name].nextStep` — the optional hint naming the
|
|
259
|
-
* tool the agent SHOULD call next when the action fires. Only entries
|
|
260
|
-
* whose `actionSpec[name].nextStep` is declared are projected; actions
|
|
261
|
-
* without a hint are omitted from the map.
|
|
262
|
-
*
|
|
263
|
-
* Producer: server's push handler at push time, sourced from the
|
|
264
|
-
* resolved stack item's `actionSpec`. Consumer: iframe-runtime, which
|
|
265
|
-
* mirrors the hint onto outbound `_meta.ggui.userAction` fall-through
|
|
266
|
-
* envelopes (the inline variant in {@link InlineUserActionMeta.nextStep})
|
|
267
|
-
* so the agent gets a strong tool-choice steer when chat-shortcut
|
|
268
|
-
* fallback fires.
|
|
269
|
-
*
|
|
270
|
-
* Absent / empty mapping ⇒ no per-action hints; the iframe omits
|
|
271
|
-
* `nextStep` from the fall-through envelope and the agent picks the
|
|
272
|
-
* next tool freely.
|
|
273
|
-
*
|
|
274
|
-
* @public
|
|
275
|
-
*/
|
|
276
|
-
readonly actionNextSteps?: Readonly<Record<string, string>>;
|
|
277
|
-
/**
|
|
278
|
-
* Per-slot data for the active stack item's `contextSpec`. Each entry
|
|
279
|
-
* carries the slot name + the JsonSchema for runtime validation +
|
|
280
|
-
* optional debounceMs override + optional default value.
|
|
281
|
-
*
|
|
282
|
-
* Producer: server's push handler at push time, derived from
|
|
283
|
-
* `activeStackItem.contextSpec`. Consumer: iframe-runtime, which (at
|
|
284
|
-
* boot) synthesizes one `React.createContext(default)` per entry and
|
|
285
|
-
* registers it under `globalThis.__ggui__.contexts[contextName]`. The
|
|
286
|
-
* boilerplate destructures the registered Contexts so the LLM has
|
|
287
|
-
* them in scope without any import line.
|
|
288
|
-
*
|
|
289
|
-
* Absent → empty list; the runtime synthesizes no Contexts; the
|
|
290
|
-
* `globalThis.__ggui__.contexts` registry is `{}`.
|
|
291
|
-
*
|
|
292
|
-
* @public
|
|
293
|
-
*/
|
|
294
|
-
readonly contextSlots?: ReadonlyArray<{
|
|
295
|
-
/** Slot key — camelCase JS identifier from `contextSpec`. */
|
|
296
|
-
readonly name: string;
|
|
297
|
-
/** PascalCase Context name auto-derived from `name`. The runtime
|
|
298
|
-
* uses this as the key in `globalThis.__ggui__.contexts`. The
|
|
299
|
-
* boilerplate uses it in destructuring lines. */
|
|
300
|
-
readonly contextName: string;
|
|
301
|
-
/** JsonSchema for the slot value — used by the runtime observer
|
|
302
|
-
* to validate Provider values before posting `ui/update-model-context`. */
|
|
303
|
-
readonly schema: JsonSchema;
|
|
304
|
-
/** Initial value for the slot's React Context Provider. Always
|
|
305
|
-
* populated by the server via {@link deriveContextDefault}: the
|
|
306
|
-
* authored `entry.default` if present, otherwise a schema-typed
|
|
307
|
-
* fallback (`''` / `0` / `false` / `[]` / `{}` / `null`). The
|
|
308
|
-
* runtime owns useState per slot, so the Provider seed is load-
|
|
309
|
-
* bearing — `undefined` here would mean
|
|
310
|
-
* the iframe boots with an indeterminate Provider value. */
|
|
311
|
-
readonly default: JsonValue;
|
|
312
|
-
/** Per-slot debounce override in milliseconds. Omitted → runtime
|
|
313
|
-
* applies `DEFAULT_CONTEXT_DEBOUNCE_MS` (300). `0` = immediate. */
|
|
314
|
-
readonly debounceMs?: number;
|
|
315
|
-
}>;
|
|
316
|
-
/**
|
|
317
|
-
* Optional stack-item pin. When present, the renderer binds to a
|
|
318
|
-
* single `StackItem` (identified by `id`) instead of the full session
|
|
319
|
-
* stack — enables per-card iframes (per-item session-resource
|
|
320
|
-
* endpoint + renderer single-item mode).
|
|
321
|
-
*
|
|
322
|
-
* Absent → renderer renders the whole session stack (default).
|
|
323
|
-
* Present → renderer filters to `session.stack.find(i => i.id === stackItemId)`
|
|
324
|
-
* and ignores the rest. Subsequent live-channel updates for other stack
|
|
325
|
-
* ids are delivered but not rendered.
|
|
326
|
-
*
|
|
327
|
-
* Resource URI convention: per-item shells are served at
|
|
328
|
-
* `ggui://session/<sessionId>/item/<stackItemId>`; whole-session
|
|
329
|
-
* shells remain at `ggui://session/<sessionId>`.
|
|
330
|
-
*
|
|
331
|
-
* The `stackItemId` is the opaque string MCP hosts receive on a
|
|
332
|
-
* `ggui_push` tool result's `stackItemId` field (or equivalently the
|
|
333
|
-
* `StackItem.id` in session state). Renderers that don't recognize
|
|
334
|
-
* the field SHOULD ignore it (falls back to whole-session rendering)
|
|
335
|
-
* — shape-preserving extensibility is the contract.
|
|
336
|
-
*
|
|
337
|
-
* @public
|
|
338
|
-
*/
|
|
339
|
-
readonly stackItemId?: string;
|
|
340
|
-
/**
|
|
341
|
-
* Content-addressable URL the runtime fetches the compiled ES module
|
|
342
|
-
* from. The discriminator for **static-component** boot mode (2026-05-13
|
|
343
|
-
* — the inline base64 `componentCode` channel was retired in favor of
|
|
344
|
-
* always-by-URL delivery).
|
|
345
|
-
*
|
|
346
|
-
* Producer: server. Computes `sha256(componentCode)`, writes the bytes
|
|
347
|
-
* to its `CodeStore`, then emits the URL here. URL shape is
|
|
348
|
-
* `<publicBaseUrl>/code/<hash>.js` for OSS or `<cdn>/code/<hash>.js`
|
|
349
|
-
* for hosted cloud — the renderer doesn't care which origin,
|
|
350
|
-
* `fetch(codeUrl)` works the same.
|
|
351
|
-
*
|
|
352
|
-
* Consumer: iframe runtime. Fetches + dynamic-imports the URL. The
|
|
353
|
-
* response is `Cache-Control: immutable`, so subsequent pushes with
|
|
354
|
-
* identical code hit the browser cache (and any CDN edge).
|
|
355
|
-
*
|
|
356
|
-
* Mutually exclusive at the discriminator level with `wsUrl+token`
|
|
357
|
-
* (live mode) and `kind` (system-card mode). When `codeStore` isn't
|
|
358
|
-
* wired, push falls back to live-mode (wsUrl+token) for delivery via
|
|
359
|
-
* the live-channel stack update.
|
|
360
|
-
*
|
|
361
|
-
* @public
|
|
362
|
-
*/
|
|
363
|
-
readonly codeUrl?: string;
|
|
364
|
-
/**
|
|
365
|
-
* Hex-encoded sha256 of the code bytes the URL serves.
|
|
366
|
-
* Surfaced separately from `codeUrl` so consumers can verify
|
|
367
|
-
* content integrity (the URL already encodes it, but parsing the
|
|
368
|
-
* URL is fragile across CDN configurations) and so the agent can
|
|
369
|
-
* inspect deduplication signals across pushes without parsing the
|
|
370
|
-
* URL.
|
|
371
|
-
*
|
|
372
|
-
* Always paired with `codeUrl`: present together or absent together.
|
|
373
|
-
*
|
|
374
|
-
* @public
|
|
375
|
-
*/
|
|
376
|
-
readonly codeHash?: string;
|
|
377
|
-
/**
|
|
378
|
-
* Discriminator for **system-card** boot mode. Stable identifier the
|
|
379
|
-
* runtime maps via the system-card registry to a built-in component
|
|
380
|
-
* (no ESM source on the wire). When present, the view does NOT open
|
|
381
|
-
* a WebSocket — mounting is purely registry-lookup.
|
|
382
|
-
*
|
|
383
|
-
* Mutually exclusive at the discriminator level with `codeUrl`
|
|
384
|
-
* (static-component mode) and `wsUrl+token` (live mode).
|
|
385
|
-
*
|
|
386
|
-
* @public
|
|
387
|
-
*/
|
|
388
|
-
readonly kind?: string;
|
|
389
|
-
/**
|
|
390
|
-
* Theme preset id forwarded to the renderer (`getTheme(id)`).
|
|
391
|
-
* Optional; absent → renderer uses its baked default theme. Used
|
|
392
|
-
* by the self-contained shell so `ggui.json#theme` takes effect
|
|
393
|
-
* across both the WS-driven and self-contained paths.
|
|
394
|
-
*
|
|
395
|
-
* @public
|
|
396
|
-
*/
|
|
397
|
-
readonly themeId?: string;
|
|
398
|
-
/**
|
|
399
|
-
* Theme color mode (`'light'` | `'dark'`) forwarded to the renderer.
|
|
400
|
-
* The runtime resolves the dark variant of {@link themeId} via
|
|
401
|
-
* `getTheme(id, 'dark')` when set; absent / unknown value falls
|
|
402
|
-
* back to `'light'`.
|
|
403
|
-
*
|
|
404
|
-
* @public
|
|
405
|
-
*/
|
|
406
|
-
readonly themeMode?: 'light' | 'dark';
|
|
407
|
-
/**
|
|
408
|
-
* Pre-serialized props for the rendered component (JSON string).
|
|
409
|
-
* Optional; absent → renderer falls back to empty props. Carried
|
|
410
|
-
* as a string to sidestep XSS-defensive escape concerns when the
|
|
411
|
-
* bootstrap is inlined as a JS literal in the self-contained shell.
|
|
412
|
-
*
|
|
413
|
-
* @public
|
|
414
|
-
*/
|
|
415
|
-
readonly propsJson?: string;
|
|
416
|
-
/**
|
|
417
|
-
* Permissions-Policy directive list derived from the active stack item's
|
|
418
|
-
* `DataContract.clientCapabilities.gadgets[*].permission` field.
|
|
419
|
-
* Browser-capability names (`'camera'`, `'microphone'`,
|
|
420
|
-
* `'geolocation'`, `'clipboard-write'`, `'clipboard-read'`,
|
|
421
|
-
* `'notifications'`, …) or arbitrary identifiers for custom
|
|
422
|
-
* platforms; the host union-deduplicates and emits these as the
|
|
423
|
-
* iframe's `Permissions-Policy` HTTP header (public-render path)
|
|
424
|
-
* or `_meta.ui.permissions` (MCP-Apps embedded path).
|
|
425
|
-
*
|
|
426
|
-
* The bootstrap MIRRORS the same list inline so the iframe-runtime
|
|
427
|
-
* can surface the requested set to in-iframe debug overlays /
|
|
428
|
-
* permission-aware UI. The BROWSER-enforced gate, however, comes
|
|
429
|
-
* from the parent-page transport (HTTP header or iframe attribute)
|
|
430
|
-
* — the iframe-runtime itself cannot change Permissions-Policy
|
|
431
|
-
* post-load; it can only react to what the parent already granted.
|
|
432
|
-
*
|
|
433
|
-
* Absent / empty → no permissions requested (default-deny posture).
|
|
434
|
-
*
|
|
435
|
-
* @public
|
|
436
|
-
*/
|
|
437
|
-
readonly permissionsPolicy?: readonly string[];
|
|
438
|
-
/**
|
|
439
|
-
* Mirror of
|
|
440
|
-
* `handshakeOutput.serverCapabilities.streamWebSocketLocalTools` on
|
|
441
|
-
* the bootstrap envelope, so the iframe-runtime's per-channel
|
|
442
|
-
* transport router can decide WS-subscribe vs iframe-polling for
|
|
443
|
-
* each `streamSpec[ch].source.tool` without re-querying the
|
|
444
|
-
* handshake.
|
|
445
|
-
*
|
|
446
|
-
* Producer: the server's push handler at push time, sourced from
|
|
447
|
-
* `GguiPushHandlerDeps.streamWebSocketLocalTools` (which mirrors the
|
|
448
|
-
* resolver on the handshake handler). Consumer: iframe-runtime's
|
|
449
|
-
* channel-transport module, which:
|
|
450
|
-
*
|
|
451
|
-
* - For each `streamSpec[ch]` with `source.tool` declared AND that
|
|
452
|
-
* tool name is in this list → fire a `channel_subscribe` WS
|
|
453
|
-
* frame; wait for `channel_payload` deliveries.
|
|
454
|
-
* - Otherwise → start a per-channel iframe polling loop (default
|
|
455
|
-
* 10s cadence) that invokes `tools/call` directly through the
|
|
456
|
-
* MCP host proxy.
|
|
457
|
-
*
|
|
458
|
-
* Absent ⇒ universal iframe-polling fallback (no channel uses the
|
|
459
|
-
* WS-subscribe path). Present + empty array ⇒ same behavior; the
|
|
460
|
-
* empty list still says "the WS-subscribe path is supported but no
|
|
461
|
-
* tool is local".
|
|
462
|
-
*
|
|
463
|
-
* @public
|
|
464
|
-
*/
|
|
465
|
-
readonly streamWebSocketLocalTools?: readonly string[];
|
|
466
|
-
/**
|
|
467
|
-
* When `true`, this bootstrap describes a
|
|
468
|
-
* SESSION-SCOPED canvas iframe (one per session) rather than a
|
|
469
|
-
* per-stack-item iframe. The canvas:
|
|
470
|
-
*
|
|
471
|
-
* - Subscribes session-wide on the live channel (no `stackItemId` filter).
|
|
472
|
-
* - Renders a navigable stack of items, not a single pinned entry.
|
|
473
|
-
* - Owns its own chrome — the ggui animator pill + navbar.
|
|
474
|
-
* - Requests `pip` / `fullscreen` display modes from the host based
|
|
475
|
-
* on stack state.
|
|
476
|
-
*
|
|
477
|
-
* Mutually exclusive with `stackItemId`: a canvas iframe never pins
|
|
478
|
-
* to a single item. Defensive parsers SHOULD reject bootstraps with
|
|
479
|
-
* both fields set, but the protocol does not require them to.
|
|
480
|
-
*
|
|
481
|
-
* Absent / false ⇒ existing inline iframe behavior. Required to be
|
|
482
|
-
* explicit (rather than overloading absent-`stackItemId`) because
|
|
483
|
-
* legacy multi-item mode (Studio/Portal/console) ALSO uses
|
|
484
|
-
* absent-`stackItemId`; the explicit flag disambiguates.
|
|
485
|
-
*/
|
|
486
|
-
readonly canvasMode?: boolean;
|
|
487
|
-
/**
|
|
488
|
-
* Resolved gadget catalog the iframe-runtime dynamically
|
|
489
|
-
* imports at boot to populate `globalThis.__ggui__.gadgets`.
|
|
490
|
-
*
|
|
491
|
-
* One entry per registered gadget **package** (GG.8.2 — the channel
|
|
492
|
-
* is per-package, not per-hook: a package's whole module namespace
|
|
493
|
-
* is loaded once and stored under `__ggui__.gadgets[package]`, so
|
|
494
|
-
* every hook AND component export the package ships is reachable).
|
|
495
|
-
* STDLIB exports (the 7 first-party browser-capability hooks shipped
|
|
496
|
-
* by `@ggui-ai/gadgets`) are seeded unconditionally and need NOT
|
|
497
|
-
* appear here — only operator-registered 3rd-party packages (Leaflet,
|
|
498
|
-
* Mapbox, …) do. This list is what makes registered packages
|
|
499
|
-
* reachable inside the iframe.
|
|
500
|
-
*
|
|
501
|
-
* Producer: server's bootstrap builder, sourced from
|
|
502
|
-
* `App.gadgets`. Consumer: iframe-runtime's
|
|
503
|
-
* `loadGadgetRegistry()` which `await import(target)`s each
|
|
504
|
-
* package once and stores the module namespace under the
|
|
505
|
-
* package-name slot.
|
|
506
|
-
*
|
|
507
|
-
* Absent or empty → only STDLIB exports are reachable. Generated
|
|
508
|
-
* components that import an unregistered gadget package fail at the
|
|
509
|
-
* iframe's ESM module-eval (the rewriter has no shim for it).
|
|
510
|
-
*
|
|
511
|
-
* @public
|
|
512
|
-
*/
|
|
513
|
-
readonly gadgets?: ReadonlyArray<{
|
|
514
|
-
/** Bare npm package name (e.g. `@my-org/leaflet`). REQUIRED — it
|
|
515
|
-
* is the registry key the iframe-runtime stores the loaded module
|
|
516
|
-
* namespace under at `globalThis.__ggui__.gadgets[package]`, and
|
|
517
|
-
* the bare-specifier load source when `bundleUrl` is absent. */
|
|
518
|
-
readonly package: string;
|
|
519
|
-
/** ggui-hosted ESM bundle URL — preferred load source when present
|
|
520
|
-
* (same-origin posture, CSP-friendly). The iframe
|
|
521
|
-
* `await import(this)`; absent → the iframe imports the bare
|
|
522
|
-
* `package` specifier. */
|
|
523
|
-
readonly bundleUrl?: string;
|
|
524
|
-
/** SHA-384 SRI hash of the bundle (`sha384-<base64>`).
|
|
525
|
-
* When present alongside `bundleUrl`, iframe-runtime routes the
|
|
526
|
-
* load through a `<link rel="modulepreload" integrity>` gate so
|
|
527
|
-
* the browser refuses execution on hash mismatch. Absent → fall
|
|
528
|
-
* back to integrity-less dynamic `import()` (back-compat for
|
|
529
|
-
* in-tree packages and hand-authored ggui.json refs). */
|
|
530
|
-
readonly bundleSri?: string;
|
|
531
|
-
}>;
|
|
532
|
-
/**
|
|
533
|
-
* Public env values the iframe-runtime installs at
|
|
534
|
-
* `globalThis.__ggui__.publicEnv` for wrapper hooks to read via
|
|
535
|
-
* `getPublicEnv(key)`. Keys MUST match
|
|
536
|
-
* `PUBLIC_ENV_APP_KEY_RE` (`^GGUI_PUBLIC_APP_[A-Z0-9_]+$` — exported
|
|
537
|
-
* from `@ggui-ai/protocol`). The prefix is the security boundary:
|
|
538
|
-
* "public" means visible to anyone with iframe-source access.
|
|
539
|
-
*
|
|
540
|
-
* Filtered by the producer (push handler) to the **union of
|
|
541
|
-
* `wrapper.requires` across declared wrappers** — minimum-disclosure
|
|
542
|
-
* principle. Keys an iframe's wrappers don't ask for never reach
|
|
543
|
-
* the iframe.
|
|
544
|
-
*
|
|
545
|
-
* Producer: server's bootstrap builder, sourced from
|
|
546
|
-
* `App.publicEnv` cross-referenced against the declared wrappers'
|
|
547
|
-
* `requires`. Consumer: iframe-runtime's `installGlobalRegistry`
|
|
548
|
-
* which plants the map verbatim at `__ggui__.publicEnv`.
|
|
549
|
-
*
|
|
550
|
-
* Defensive parse: `parseBootstrap` re-validates every key against
|
|
551
|
-
* the regex; one bad key collapses the whole field to `undefined`
|
|
552
|
-
* (matches the `gadgets` / `contextSlots` parser posture).
|
|
553
|
-
*
|
|
554
|
-
* Absent or empty → no wrapper-readable env values; only wrappers
|
|
555
|
-
* with no `requires` declarations can mount.
|
|
556
|
-
*
|
|
557
|
-
* @public
|
|
558
|
-
*/
|
|
559
|
-
readonly publicEnv?: Readonly<Record<string, string>>;
|
|
560
|
-
/**
|
|
561
|
-
* Precompiled, eval-free runtime validators for the active stack
|
|
562
|
-
* item's contract specs. See {@link CompiledContractValidators}.
|
|
563
|
-
*
|
|
564
|
-
* Why precompiled: the renderer iframe runs under a strict CSP with
|
|
565
|
-
* no `'unsafe-eval'`, so it cannot compile JSON Schemas at runtime
|
|
566
|
-
* (`ajv.compile` builds validators via `new Function`, which the CSP
|
|
567
|
-
* blocks with `EvalError`). Compilation happens server-side at push
|
|
568
|
-
* time — where the contract schema is fixed and `eval` is legal —
|
|
569
|
-
* and the iframe loads each module via a `blob:` dynamic import
|
|
570
|
-
* (governed by `script-src`, not `unsafe-eval`) and only ever RUNS
|
|
571
|
-
* the validator.
|
|
572
|
-
*
|
|
573
|
-
* Producer: server push handler, via `compileValidatorModule` from
|
|
574
|
-
* `@ggui-ai/protocol`. Consumer: iframe-runtime's wire + channel
|
|
575
|
-
* validation layer.
|
|
576
|
-
*
|
|
577
|
-
* Absent → no precompiled validators shipped; the iframe falls back
|
|
578
|
-
* to the server as the sole contract authority (the push-time
|
|
579
|
-
* `assertActionContract` gate still enforces outbound actions).
|
|
580
|
-
*
|
|
581
|
-
* @public
|
|
582
|
-
*/
|
|
583
|
-
readonly compiledValidators?: CompiledContractValidators;
|
|
584
|
-
}
|
|
585
79
|
/**
|
|
586
80
|
* Precompiled, eval-free validators for a contract's runtime-validated
|
|
587
|
-
* specs —
|
|
81
|
+
* specs — served at `_meta["ai.ggui/contract"].validatorsUrl` as the
|
|
82
|
+
* `default` export of a content-addressable ES module.
|
|
588
83
|
*
|
|
589
84
|
* Each value is the SOURCE TEXT of an ES module whose `default` export
|
|
590
85
|
* is an Ajv validator function (`(data) => boolean`, carrying
|
|
@@ -627,19 +122,260 @@ export interface CompiledContractValidators {
|
|
|
627
122
|
*/
|
|
628
123
|
export declare function deriveContextName(slotKey: string): string;
|
|
629
124
|
/**
|
|
630
|
-
*
|
|
631
|
-
*
|
|
632
|
-
*
|
|
633
|
-
*
|
|
634
|
-
*
|
|
635
|
-
*
|
|
636
|
-
|
|
637
|
-
|
|
125
|
+
* `_meta` key carrying the full render slice. Single source of truth
|
|
126
|
+
* post-Phase-B: identity, boot wiring, live-channel auth, capability
|
|
127
|
+
* advertisements, current render state, contract pointer, and component-
|
|
128
|
+
* mode discriminator.
|
|
129
|
+
*
|
|
130
|
+
* @public
|
|
131
|
+
*/
|
|
132
|
+
export declare const MCP_APP_AI_GGUI_RENDER_META_KEY: "ai.ggui/render";
|
|
133
|
+
/**
|
|
134
|
+
* Single entry in the {@link McpAppAiGguiRenderMeta.gadgets} catalog.
|
|
135
|
+
* One per registered gadget package — the iframe-runtime
|
|
136
|
+
* dynamic-imports each at boot and stores the loaded namespace under
|
|
137
|
+
* `globalThis.__ggui__.gadgets[package]`.
|
|
138
|
+
*
|
|
139
|
+
* @public
|
|
638
140
|
*/
|
|
639
|
-
export interface
|
|
640
|
-
|
|
641
|
-
|
|
642
|
-
|
|
141
|
+
export interface McpAppGadgetRef {
|
|
142
|
+
/** Bare npm package name (e.g. `@my-org/leaflet`). REQUIRED — it
|
|
143
|
+
* is the registry key the iframe-runtime stores the loaded module
|
|
144
|
+
* namespace under at `globalThis.__ggui__.gadgets[package]`, and
|
|
145
|
+
* the bare-specifier load source when `bundleUrl` is absent. */
|
|
146
|
+
readonly package: string;
|
|
147
|
+
/** ggui-hosted ESM bundle URL — preferred load source when present
|
|
148
|
+
* (same-origin posture, CSP-friendly). The iframe `await import(this)`;
|
|
149
|
+
* absent → the iframe imports the bare `package` specifier. */
|
|
150
|
+
readonly bundleUrl?: string;
|
|
151
|
+
/** SHA-384 SRI hash of the bundle (`sha384-<base64>`).
|
|
152
|
+
* When present alongside `bundleUrl`, iframe-runtime routes the
|
|
153
|
+
* load through a `<link rel="modulepreload" integrity>` gate so
|
|
154
|
+
* the browser refuses execution on hash mismatch. */
|
|
155
|
+
readonly bundleSri?: string;
|
|
156
|
+
}
|
|
157
|
+
/**
|
|
158
|
+
* Single entry in {@link McpAppAiGguiRenderMeta.contextSlots}.
|
|
159
|
+
* One per `contextSpec` slot — the iframe-runtime synthesizes one
|
|
160
|
+
* `React.createContext(default)` per entry at boot.
|
|
161
|
+
*
|
|
162
|
+
* @public
|
|
163
|
+
*/
|
|
164
|
+
export interface McpAppContextSlot {
|
|
165
|
+
/** Slot key — camelCase JS identifier from `contextSpec`. */
|
|
166
|
+
readonly name: string;
|
|
167
|
+
/** PascalCase Context name auto-derived from `name`. Used as the
|
|
168
|
+
* registry key in `globalThis.__ggui__.contexts`. */
|
|
169
|
+
readonly contextName: string;
|
|
170
|
+
/** JsonSchema for the slot value — used by the runtime observer to
|
|
171
|
+
* validate Provider values before posting `ui/update-model-context`. */
|
|
172
|
+
readonly schema: JsonSchema;
|
|
173
|
+
/** Initial Provider value. Always populated by the server. */
|
|
174
|
+
readonly default: JsonValue;
|
|
175
|
+
/** Per-slot debounce override in milliseconds. Omitted → runtime
|
|
176
|
+
* applies `DEFAULT_CONTEXT_DEBOUNCE_MS` (300). `0` = immediate. */
|
|
177
|
+
readonly debounceMs?: number;
|
|
178
|
+
}
|
|
179
|
+
/**
|
|
180
|
+
* The full render slice — flat post-Phase-B. Identity + boot wiring +
|
|
181
|
+
* live-channel auth + capability advertisements + render state +
|
|
182
|
+
* contract pointer + component-mode discriminator.
|
|
183
|
+
*
|
|
184
|
+
* **Identity.** `renderId` is the value an iframe's bootstrap meta and
|
|
185
|
+
* every wire reference (props_update, consume, update) keys by. The
|
|
186
|
+
* value is the same one stack items carried as `stackItemId` pre-Phase-B;
|
|
187
|
+
* the rename reflects the conceptual collapse (no enclosing vessel).
|
|
188
|
+
*
|
|
189
|
+
* **Live-channel auth.** `wsUrl` + `wsToken` are paired — both present
|
|
190
|
+
* or both absent; `expiresAt` is informational. `wsToken` is the opaque
|
|
191
|
+
* WS auth credential the iframe threads on the WebSocket upgrade as
|
|
192
|
+
* `?wsToken=<encoded>` and inside `SubscribePayload.wsToken`.
|
|
193
|
+
*
|
|
194
|
+
* **Polling-fallback URL.** When WS is blocked at the host CSP layer
|
|
195
|
+
* the iframe polls `pollingUrl` (server-stamped post-Phase-B as
|
|
196
|
+
* `/api/renders/<renderId>/events?wsToken=<token>`). The iframe-runtime
|
|
197
|
+
* composes per-tick `&sinceSequence=<cursor>&limit=<N>` against this
|
|
198
|
+
* base; companion to `lastSequence` which seeds the initial cursor.
|
|
199
|
+
*
|
|
200
|
+
* **Mode discriminator.** At least one of `{ codeUrl, kind, wsUrl-with-token }`
|
|
201
|
+
* MUST be present for the iframe to mount. `kind` and `codeUrl` are
|
|
202
|
+
* mutually exclusive (kind = system-card mode; codeUrl = static-component
|
|
203
|
+
* mode; live-channel = absent both).
|
|
204
|
+
*
|
|
205
|
+
* @public
|
|
206
|
+
*/
|
|
207
|
+
export interface McpAppAiGguiRenderMeta {
|
|
208
|
+
readonly renderId: string;
|
|
209
|
+
readonly appId: string;
|
|
210
|
+
readonly runtimeUrl: string;
|
|
211
|
+
readonly wsUrl?: string;
|
|
212
|
+
readonly wsToken?: string;
|
|
213
|
+
readonly expiresAt?: string;
|
|
214
|
+
readonly pollingUrl?: string;
|
|
215
|
+
readonly themeId?: string;
|
|
216
|
+
readonly themeMode?: 'light' | 'dark';
|
|
217
|
+
readonly gadgets?: ReadonlyArray<McpAppGadgetRef>;
|
|
218
|
+
readonly publicEnv?: Readonly<Record<string, string>>;
|
|
219
|
+
readonly streamWebSocketLocalTools?: readonly string[];
|
|
220
|
+
readonly appCallableTools?: readonly string[];
|
|
221
|
+
readonly permissionsPolicy?: readonly string[];
|
|
222
|
+
/**
|
|
223
|
+
* Monotonic sequence number of the most-recent event applied to
|
|
224
|
+
* this render's event ledger. Stamped on every emission (render,
|
|
225
|
+
* update, `GET /api/renders/:id/state` read, MCP `resources/read`
|
|
226
|
+
* of `ui://ggui/render/<id>`). Consumers use it to initialize
|
|
227
|
+
* polling cursors aligned with the event ledger — see the
|
|
228
|
+
* `/api/renders/:id/events?sinceSequence=N` endpoint that reads
|
|
229
|
+
* from a cursor.
|
|
230
|
+
*/
|
|
231
|
+
readonly lastSequence?: number;
|
|
232
|
+
readonly propsJson?: string;
|
|
233
|
+
readonly actionNextSteps?: Readonly<Record<string, string>>;
|
|
234
|
+
readonly contextSlots?: ReadonlyArray<McpAppContextSlot>;
|
|
235
|
+
readonly contractHash?: string;
|
|
236
|
+
readonly validatorsUrl?: string;
|
|
237
|
+
readonly codeUrl?: string;
|
|
238
|
+
readonly codeHash?: string;
|
|
239
|
+
readonly kind?: string;
|
|
240
|
+
}
|
|
241
|
+
/**
|
|
242
|
+
* Discriminated result of {@link parseMcpAppAiGguiRenderMeta}. The parser
|
|
243
|
+
* does structural slice-shape validation only — `MALFORMED_RENDER`
|
|
244
|
+
* surfaces a structurally-invalid slice (wrong type, missing required
|
|
245
|
+
* identity, paired fields half-present, mutually-exclusive fields both
|
|
246
|
+
* present). Missing key entirely is NOT a failure; the "is the slice
|
|
247
|
+
* required for THIS consumer" gate lives in consumer-side mount check.
|
|
248
|
+
*
|
|
249
|
+
* @public
|
|
250
|
+
*/
|
|
251
|
+
export type ParseMcpAppAiGguiRenderMetaResult = {
|
|
252
|
+
readonly ok: true;
|
|
253
|
+
readonly meta?: McpAppAiGguiRenderMeta;
|
|
254
|
+
} | {
|
|
255
|
+
readonly ok: false;
|
|
256
|
+
readonly reason: 'MALFORMED_RENDER';
|
|
257
|
+
};
|
|
258
|
+
/**
|
|
259
|
+
* Read the `ai.ggui/render` slice off a parsed JSON-RPC `_meta` object.
|
|
260
|
+
*
|
|
261
|
+
* Structural validation only. Missing key returns `{ok: true, meta: undefined}`
|
|
262
|
+
* — not a failure. Required-fields gate (renderId / appId / runtimeUrl)
|
|
263
|
+
* fires only when the key is present. Field-level optional-field
|
|
264
|
+
* defensive parsing (e.g. context-slot schema narrowing, expiresAt date
|
|
265
|
+
* parse) lives downstream in the iframe-runtime's `validateMeta`.
|
|
266
|
+
*
|
|
267
|
+
* @public
|
|
268
|
+
*/
|
|
269
|
+
export declare function parseMcpAppAiGguiRenderMeta(meta: unknown): ParseMcpAppAiGguiRenderMetaResult;
|
|
270
|
+
/**
|
|
271
|
+
* Emitter convenience — wrap a server-built {@link McpAppAiGguiRenderMeta}
|
|
272
|
+
* slice as the wire `_meta` envelope under the canonical key constant.
|
|
273
|
+
*
|
|
274
|
+
* @public
|
|
275
|
+
*/
|
|
276
|
+
export declare function toMcpAppEnvelope(render: McpAppAiGguiRenderMeta): Record<string, unknown>;
|
|
277
|
+
/**
|
|
278
|
+
* `_meta` key carrying host-supplied conversation-grouping metadata on
|
|
279
|
+
* every inbound `tools/call` request. Captured ONCE on the first call
|
|
280
|
+
* that materializes a ggui render row and persisted as opt-in identity
|
|
281
|
+
* for later rehydration.
|
|
282
|
+
*
|
|
283
|
+
* Hosts that don't set this key produce one-shot renders — they work
|
|
284
|
+
* fine for a single chat turn but cannot be re-listed or restored after
|
|
285
|
+
* the host closes the conversation surface. Opt-in is the whole design:
|
|
286
|
+
* hosts that want resume thread their conversation id here; hosts that
|
|
287
|
+
* don't get the simple write-only path.
|
|
288
|
+
*
|
|
289
|
+
* @public
|
|
290
|
+
*/
|
|
291
|
+
export declare const MCP_APP_AI_GGUI_HOST_SESSION_META_KEY: "ai.ggui/host-session";
|
|
292
|
+
/**
|
|
293
|
+
* Host-supplied conversation-grouping slice. Sent on the request `_meta`
|
|
294
|
+
* of the first `ggui_*` tool call that creates a ggui render; subsequent
|
|
295
|
+
* calls naming the same render ignore the field — set-at-creation,
|
|
296
|
+
* immutable.
|
|
297
|
+
*
|
|
298
|
+
* Opaque grouping key, NOT a credential. Auth still comes from the
|
|
299
|
+
* caller's identity (API key, OAuth bearer, cookie). `hostSessionId`
|
|
300
|
+
* scopes which renders the authenticated caller can rehydrate; it
|
|
301
|
+
* does NOT itself authorize access.
|
|
302
|
+
*
|
|
303
|
+
* Both fields are required when the slice is present. A slice with a
|
|
304
|
+
* missing/empty field is treated as absent (degrades to one-shot).
|
|
305
|
+
*
|
|
306
|
+
* @public
|
|
307
|
+
*/
|
|
308
|
+
export interface McpAppAiGguiHostSessionMeta {
|
|
309
|
+
/**
|
|
310
|
+
* Stable host identifier — e.g. `'sample'`, `'claude.ai'`, `'chatgpt'`.
|
|
311
|
+
* Used to partition `hostSessionId` namespace so the same chat-id
|
|
312
|
+
* across two different hosts cannot alias.
|
|
313
|
+
*/
|
|
314
|
+
readonly hostName: string;
|
|
315
|
+
/**
|
|
316
|
+
* Host's grouping key for "this conversation" — opaque to ggui.
|
|
317
|
+
* Typically: claude.ai thread id, ChatGPT chat id, sample-agent
|
|
318
|
+
* chatSessionId. The server treats it as an opaque string.
|
|
319
|
+
*/
|
|
320
|
+
readonly hostSessionId: string;
|
|
321
|
+
}
|
|
322
|
+
/**
|
|
323
|
+
* Discriminated result of {@link parseMcpAppAiGguiHostSessionMeta}.
|
|
324
|
+
*
|
|
325
|
+
* Three outcomes:
|
|
326
|
+
* - `ok: true, hostSession: <slice>` — slice present + well-formed.
|
|
327
|
+
* - `ok: true, hostSession: undefined` — slice absent (host opted out
|
|
328
|
+
* of rehydration). Caller proceeds without it; the render it
|
|
329
|
+
* creates is one-shot.
|
|
330
|
+
* - `ok: false` — slice present but structurally invalid. Caller's
|
|
331
|
+
* choice whether to reject the request or proceed as "absent".
|
|
332
|
+
* The handler MAY log + proceed; this is host implementor error,
|
|
333
|
+
* not a security boundary.
|
|
334
|
+
*
|
|
335
|
+
* @public
|
|
336
|
+
*/
|
|
337
|
+
export type ParseMcpAppAiGguiHostSessionMetaResult = {
|
|
338
|
+
readonly ok: true;
|
|
339
|
+
readonly hostSession?: McpAppAiGguiHostSessionMeta;
|
|
340
|
+
} | {
|
|
341
|
+
readonly ok: false;
|
|
342
|
+
readonly reason: 'MALFORMED_HOST_SESSION';
|
|
343
|
+
};
|
|
344
|
+
/**
|
|
345
|
+
* Read the `ai.ggui/host-session` slice off a parsed inbound `_meta`
|
|
346
|
+
* object. Structural validation only — `hostName` + `hostSessionId`
|
|
347
|
+
* both required and non-empty. Both missing entirely returns
|
|
348
|
+
* `{ok: true, hostSession: undefined}` (the documented opt-out path).
|
|
349
|
+
*
|
|
350
|
+
* @public
|
|
351
|
+
*/
|
|
352
|
+
export declare function parseMcpAppAiGguiHostSessionMeta(meta: unknown): ParseMcpAppAiGguiHostSessionMetaResult;
|
|
353
|
+
/**
|
|
354
|
+
* Wire shape of one row in `ggui_list_renders` output. Mirrors the
|
|
355
|
+
* handler's Zod-described `renders[*]`. Surfaced at the protocol level
|
|
356
|
+
* so non-handler consumers (sample-agent's `/chat/restore` server, future
|
|
357
|
+
* host SDK helpers) can import a single typed shape instead of
|
|
358
|
+
* redeclaring it — preventing drift if the handler ever grows fields.
|
|
359
|
+
*
|
|
360
|
+
* `wsToken` + `wsTokenExpiresAt` are populated when the deployment
|
|
361
|
+
* wired a `mintWsToken` seam on the handler — otherwise the lean
|
|
362
|
+
* summary path returns them absent.
|
|
363
|
+
*
|
|
364
|
+
* Post-Phase-B: `sessionId` → `renderId`; the old `stackItemCount` is
|
|
365
|
+
* dropped (every render is exactly one item — Phase B collapsed the
|
|
366
|
+
* vessel).
|
|
367
|
+
*
|
|
368
|
+
* @public
|
|
369
|
+
*/
|
|
370
|
+
export interface RenderSummaryWire {
|
|
371
|
+
readonly renderId: string;
|
|
372
|
+
readonly hostName?: string;
|
|
373
|
+
readonly hostSessionId?: string;
|
|
374
|
+
readonly createdAt: string;
|
|
375
|
+
readonly lastActivityAt: string;
|
|
376
|
+
readonly status: string;
|
|
377
|
+
readonly wsToken?: string;
|
|
378
|
+
readonly wsTokenExpiresAt?: string;
|
|
643
379
|
}
|
|
644
380
|
/**
|
|
645
381
|
* CSP metadata copied from an MCP Apps resource declaration.
|
|
@@ -673,7 +409,7 @@ export interface McpAppsContainerDimensions {
|
|
|
673
409
|
/**
|
|
674
410
|
* Locator for the source of an embedded MCP App.
|
|
675
411
|
*
|
|
676
|
-
* Persists STABLE identity (not a raw URL) so
|
|
412
|
+
* Persists STABLE identity (not a raw URL) so render state survives
|
|
677
413
|
* source-server endpoint changes. The runtime `ConnectorRegistry`
|
|
678
414
|
* resolves `connectorId` to the actual endpoint at render time.
|
|
679
415
|
*/
|
|
@@ -688,22 +424,21 @@ export interface McpAppsSource {
|
|
|
688
424
|
readonly resourceUri: string;
|
|
689
425
|
}
|
|
690
426
|
/**
|
|
691
|
-
*
|
|
427
|
+
* Render variant: an embedded third-party MCP App iframe.
|
|
692
428
|
*
|
|
693
429
|
* **Locator-oriented, not content-oriented.** Persisted state carries
|
|
694
430
|
* `source` (connector identity) + declared CSP/permissions/dimensions
|
|
695
|
-
* metadata; resource BYTES are not stored in
|
|
431
|
+
* metadata; resource BYTES are not stored in render state by default.
|
|
696
432
|
* The `@ggui-ai/mcp-server` resource-proxy route fetches the bytes
|
|
697
433
|
* on-demand via `resources/read` against the source server.
|
|
698
434
|
*
|
|
699
|
-
* **Union safety.** Fields that exist on the {@link
|
|
700
|
-
*
|
|
701
|
-
*
|
|
702
|
-
*
|
|
703
|
-
*
|
|
704
|
-
* "structurally absent" guarantee.
|
|
435
|
+
* **Union safety.** Fields that exist on the {@link ComponentRender}
|
|
436
|
+
* variant are declared here as `?: never` so consumers that access them
|
|
437
|
+
* via optional chaining on `Render` still typecheck cleanly. Those
|
|
438
|
+
* fields semantically DO NOT exist on McpAppsRender — the `?: never`
|
|
439
|
+
* typing encodes the "structurally absent" guarantee.
|
|
705
440
|
*/
|
|
706
|
-
export interface
|
|
441
|
+
export interface McpAppsRender {
|
|
707
442
|
/** Discriminator — required on this variant. */
|
|
708
443
|
readonly type: 'mcpApps';
|
|
709
444
|
readonly id: string;
|
|
@@ -717,9 +452,9 @@ export interface McpAppsStackItem {
|
|
|
717
452
|
readonly containerDimensions?: McpAppsContainerDimensions;
|
|
718
453
|
/**
|
|
719
454
|
* Optional integrity pin — sha256 of the resource bytes computed at
|
|
720
|
-
*
|
|
721
|
-
* content against this hash; a mismatch breaks the
|
|
722
|
-
*
|
|
455
|
+
* render time. The resource-proxy route verifies the re-fetched
|
|
456
|
+
* content against this hash; a mismatch breaks the render LOUDLY
|
|
457
|
+
* rather than silently serving mutated content.
|
|
723
458
|
*/
|
|
724
459
|
readonly resourceHash?: string;
|
|
725
460
|
/**
|
|
@@ -745,18 +480,18 @@ export interface McpAppsStackItem {
|
|
|
745
480
|
readonly clientCapabilities?: never;
|
|
746
481
|
}
|
|
747
482
|
/**
|
|
748
|
-
* Type guard: narrows a `
|
|
749
|
-
*
|
|
483
|
+
* Type guard: narrows a `Render` (or unknown) to {@link McpAppsRender}.
|
|
484
|
+
* Uses the discriminator.
|
|
750
485
|
*/
|
|
751
|
-
export declare function
|
|
486
|
+
export declare function isMcpAppsRender(entry: unknown): entry is McpAppsRender;
|
|
752
487
|
/**
|
|
753
|
-
* Structural validator for an `
|
|
488
|
+
* Structural validator for an `McpAppsRender` — not a Zod schema
|
|
754
489
|
* so we don't force a Zod dependency here. Returns null on failure
|
|
755
490
|
* (caller maps to an appropriate error code). Required when accepting
|
|
756
491
|
* one over the wire from an agent: the discriminator alone isn't
|
|
757
492
|
* enough.
|
|
758
493
|
*/
|
|
759
|
-
export declare function
|
|
494
|
+
export declare function validateMcpAppsRender(input: unknown): McpAppsRender | null;
|
|
760
495
|
/**
|
|
761
496
|
* Lifecycle states the renderer transitions through inside an MCP Apps
|
|
762
497
|
* iframe. Closed union — adding a new state is a protocol-version-
|
|
@@ -788,7 +523,7 @@ export declare function validateMcpAppsStackItem(input: unknown): McpAppsStackIt
|
|
|
788
523
|
* A host that observes only `mounting` and never a follow-up state
|
|
789
524
|
* has a renderer that crashed before posting code-ready/error.
|
|
790
525
|
* - `code-ready` — happy-path terminal state. Bundle evaluated, React
|
|
791
|
-
* tree mounted, WS connected, first
|
|
526
|
+
* tree mounted, WS connected, first render ack folded. Equivalent of
|
|
792
527
|
* the in-iframe `data-ggui-status="connected"`.
|
|
793
528
|
* - `error` — terminal failure. Pairs with the existing
|
|
794
529
|
* `ggui:bootstrap-failed` postMessage envelope which carries the
|
|
@@ -809,9 +544,9 @@ export type McpAppLifecycleState = 'mounting' | 'code-ready' | 'error' | 'discon
|
|
|
809
544
|
*
|
|
810
545
|
* Fields:
|
|
811
546
|
* - `state` — required. The lifecycle state being entered.
|
|
812
|
-
* - `
|
|
813
|
-
*
|
|
814
|
-
*
|
|
547
|
+
* - `renderId` — optional. When present, the lifecycle pertains to a
|
|
548
|
+
* specific render (per-card iframes via single-item mode).
|
|
549
|
+
* Absent → whole-renderer lifecycle.
|
|
815
550
|
* - `error` — optional, only meaningful when `state === 'error'`.
|
|
816
551
|
* Mirrors the `ggui:bootstrap-failed` postMessage envelope's
|
|
817
552
|
* `reason` + `message` so a single `ggui:lifecycle` listener can
|
|
@@ -830,7 +565,7 @@ export type McpAppLifecycleState = 'mounting' | 'code-ready' | 'error' | 'discon
|
|
|
830
565
|
*/
|
|
831
566
|
export interface McpAppLifecycleEvent {
|
|
832
567
|
readonly state: McpAppLifecycleState;
|
|
833
|
-
readonly
|
|
568
|
+
readonly renderId?: string;
|
|
834
569
|
readonly error?: {
|
|
835
570
|
readonly code: string;
|
|
836
571
|
readonly message: string;
|
|
@@ -914,24 +649,13 @@ export declare const MCP_APP_LIFECYCLE_STATES: readonly McpAppLifecycleState[];
|
|
|
914
649
|
* - Outer envelope is an object with `type === 'ggui:lifecycle'`.
|
|
915
650
|
* - `event` is an object with `state` matching {@link
|
|
916
651
|
* MCP_APP_LIFECYCLE_STATES}.
|
|
917
|
-
* - If `
|
|
652
|
+
* - If `renderId` is present, it is a non-empty string.
|
|
918
653
|
* - If `error` is present, it is an object with string `code` +
|
|
919
654
|
* `message`.
|
|
920
655
|
*
|
|
921
656
|
* @public
|
|
922
657
|
*/
|
|
923
658
|
export declare function isMcpAppLifecycleMessage(message: unknown): message is McpAppLifecycleMessage;
|
|
924
|
-
/**
|
|
925
|
-
* Type guard for recognizing a `ggui_push` result `_meta` that carries a
|
|
926
|
-
* bootstrap block. Handy for views / tests that receive the raw result.
|
|
927
|
-
*
|
|
928
|
-
* Validates the three-mode discriminator: a well-formed bootstrap MUST
|
|
929
|
-
* carry `runtimeUrl` + `sessionId` + `appId` AND at least one of
|
|
930
|
-
* `{wsUrl, codeUrl, kind}` as a non-empty string. Live mode
|
|
931
|
-
* additionally requires `token` to pair with `wsUrl` — half-live
|
|
932
|
-
* (one without the other) is rejected as MALFORMED.
|
|
933
|
-
*/
|
|
934
|
-
export declare function hasPushBootstrapMeta(meta: unknown): meta is PushResultMeta;
|
|
935
659
|
/**
|
|
936
660
|
* Discriminator for the user-action envelope delivered via
|
|
937
661
|
* `ggui_runtime_submit_action` over the MCP Apps host-relay path
|
|
@@ -957,7 +681,7 @@ export declare function hasPushBootstrapMeta(meta: unknown): meta is PushResultM
|
|
|
957
681
|
* Audit is **fail-soft** at the client: if the `tools/call` envelope
|
|
958
682
|
* fails to deliver (host rejects, postMessage on detached parent), the
|
|
959
683
|
* primary host effect MUST still proceed. The audit miss surfaces as a
|
|
960
|
-
* diagnostic on the operator side (gap in the
|
|
684
|
+
* diagnostic on the operator side (gap in the RenderInspector activity
|
|
961
685
|
* row), not as a user-facing failure. This mirrors today's `dispatch`
|
|
962
686
|
* audit-fire posture so semantics stay uniform.
|
|
963
687
|
*
|
|
@@ -1023,21 +747,26 @@ export type SubmitActionEnvelope = {
|
|
|
1023
747
|
* Per-kind semantics:
|
|
1024
748
|
*
|
|
1025
749
|
* - `kind === 'dispatch'`: server appends a consume-entry onto the
|
|
1026
|
-
*
|
|
750
|
+
* render-keyed pending-events pipe (`{type:'action', renderId,
|
|
1027
751
|
* intent, actionData, uiContext, actionId, firedAt}`) so the agent's
|
|
1028
|
-
* `ggui_consume` long-poll unblocks in the same chat turn.
|
|
1029
|
-
*
|
|
1030
|
-
*
|
|
1031
|
-
*
|
|
1032
|
-
*
|
|
1033
|
-
*
|
|
752
|
+
* `ggui_consume` long-poll unblocks in the same chat turn. The
|
|
753
|
+
* handler's response carries `consumerPresent` — whether a
|
|
754
|
+
* `ggui_consume` long-poll is currently listening on this render's
|
|
755
|
+
* pipe. When `consumerPresent === false` (no loop is listening —
|
|
756
|
+
* e.g. the agent's persistent consume loop ended after a page
|
|
757
|
+
* reload), the iframe-runtime ALSO emits a `ui/message` doorbell
|
|
758
|
+
* carrying `content[0]._meta["ai.ggui/userAction"]` (see
|
|
759
|
+
* {@link GguiUserActionMeta}) so a fresh agent turn calls
|
|
760
|
+
* `ggui_consume({renderId})` to drain the just-enqueued gesture.
|
|
761
|
+
* The doorbell is a PURE POINTER — the gesture stays solely on the
|
|
762
|
+
* pipe, making the action exactly-once.
|
|
1034
763
|
* - `kind ∈ {'openLink','requestDisplayMode'}`: pure audit — the
|
|
1035
764
|
* user-visible host effect already fired iframe-side via
|
|
1036
765
|
* `ui/open-link` / `ui/request-display-mode`. The server records
|
|
1037
|
-
* the gesture for the
|
|
766
|
+
* the gesture for the RenderInspector feed.
|
|
1038
767
|
*
|
|
1039
768
|
* Required fields:
|
|
1040
|
-
* - `
|
|
769
|
+
* - `renderId` / `appId`: bootstrap-issued; server cross-checks.
|
|
1041
770
|
* - `actionId`: 8-hex correlation hash (FNV-1a of intent + data + firedAt
|
|
1042
771
|
* for `dispatch`, kind + payload + firedAt for the host-control kinds).
|
|
1043
772
|
* Lets the host LLM cross-verify a `[ggui:pending-action]` context entry
|
|
@@ -1050,17 +779,7 @@ export type SubmitActionEnvelope = {
|
|
|
1050
779
|
* shape — see {@link SubmitActionEnvelope}.
|
|
1051
780
|
*/
|
|
1052
781
|
export type GguiSubmitActionInput = SubmitActionEnvelope & {
|
|
1053
|
-
readonly
|
|
1054
|
-
/**
|
|
1055
|
-
* Active stack item id. Optional because the iframe-runtime boots
|
|
1056
|
-
* into a stack-item context only when the host minted one via
|
|
1057
|
-
* `ggui_push` — boot scenarios like system-cards or pre-push
|
|
1058
|
-
* provisional previews don't carry one. Required when
|
|
1059
|
-
* `kind === 'dispatch'` (the kind that needs to land in the
|
|
1060
|
-
* stackItem-keyed pending-event pipe); the server-side handler
|
|
1061
|
-
* rejects dispatch envelopes missing this field.
|
|
1062
|
-
*/
|
|
1063
|
-
readonly stackItemId?: string;
|
|
782
|
+
readonly renderId: string;
|
|
1064
783
|
readonly appId: string;
|
|
1065
784
|
readonly actionId: string;
|
|
1066
785
|
readonly firedAt: string;
|
|
@@ -1085,134 +804,58 @@ export declare const SUBMIT_ACTION_KINDS: readonly ["dispatch", "openLink", "req
|
|
|
1085
804
|
*/
|
|
1086
805
|
export declare function isGguiSubmitActionInput(value: unknown): value is GguiSubmitActionInput;
|
|
1087
806
|
/**
|
|
1088
|
-
*
|
|
1089
|
-
*
|
|
1090
|
-
*
|
|
1091
|
-
*
|
|
1092
|
-
*
|
|
1093
|
-
*
|
|
1094
|
-
*
|
|
1095
|
-
*
|
|
1096
|
-
*
|
|
1097
|
-
*
|
|
1098
|
-
*
|
|
1099
|
-
*
|
|
1100
|
-
*
|
|
1101
|
-
*
|
|
1102
|
-
*
|
|
1103
|
-
*
|
|
1104
|
-
*
|
|
1105
|
-
*
|
|
1106
|
-
*
|
|
1107
|
-
* the
|
|
1108
|
-
*
|
|
1109
|
-
*
|
|
1110
|
-
*
|
|
1111
|
-
*
|
|
1112
|
-
*
|
|
1113
|
-
*
|
|
1114
|
-
*
|
|
1115
|
-
*
|
|
1116
|
-
*
|
|
1117
|
-
|
|
1118
|
-
|
|
1119
|
-
|
|
1120
|
-
*
|
|
1121
|
-
*
|
|
807
|
+
* `content[0]._meta["ai.ggui/userAction"]` — a PURE DOORBELL.
|
|
808
|
+
*
|
|
809
|
+
* Spec-canonical extension point: MCP Apps closes `params._meta` via
|
|
810
|
+
* `additionalProperties: false`, but each content block has its own
|
|
811
|
+
* open `_meta` record (per the base MCP spec). The `ai.ggui/*` key
|
|
812
|
+
* prefix matches our other protocol extensions
|
|
813
|
+
* (`ai.ggui/render`, `ai.ggui/bootstrap`, etc.).
|
|
814
|
+
*
|
|
815
|
+
* Stamped by the iframe-runtime on a `ui/message` envelope when a user
|
|
816
|
+
* gesture needs to wake the agent because no `ggui_consume` long-poll is
|
|
817
|
+
* currently listening (the agent's persistent consume loop has ended —
|
|
818
|
+
* e.g. after a page reload). The gesture itself was ALREADY enqueued onto
|
|
819
|
+
* the render's server-side pending-event pipe by the iframe's
|
|
820
|
+
* `ggui_runtime_submit_action` call (relayed by the host) BEFORE this
|
|
821
|
+
* notification fired; this slice's only job is to make a fresh agent turn
|
|
822
|
+
* call `ggui_consume({renderId})` to drain it.
|
|
823
|
+
*
|
|
824
|
+
* SINGLE SOURCE OF TRUTH: the pending-event queue. This slice carries ONLY
|
|
825
|
+
* a pointer to the render whose queue holds the gesture — never the action
|
|
826
|
+
* payload. The agent retrieves the action EXCLUSIVELY via `ggui_consume`.
|
|
827
|
+
* Carrying the payload here would let the agent both act on it AND drain
|
|
828
|
+
* the queue = a double-trigger; the pointer-only shape makes the action
|
|
829
|
+
* exactly-once.
|
|
830
|
+
*
|
|
831
|
+
* `intent` is metadata (which `actionSpec[*]` entry fired) — NOT the
|
|
832
|
+
* actionable data. The agent can't react meaningfully on `intent` alone
|
|
833
|
+
* (it lacks the `actionData` payload), so its presence doesn't tempt a
|
|
834
|
+
* pre-consume action.
|
|
835
|
+
*
|
|
836
|
+
* **The directive lives in the `ui/message` TEXT, not here.** The
|
|
837
|
+
* iframe-runtime authors a `ui/message` whose human-readable text
|
|
838
|
+
* carries the full "call `ggui_consume`" directive — that text is what
|
|
839
|
+
* EVERY host (claude.ai, chatgpt.com, ggui-aware SDKs) forwards to the
|
|
840
|
+
* model. This `_meta` slice is the OPTIONAL structured mirror for
|
|
841
|
+
* ggui-aware programmatic consumers; an `_meta`-agnostic host ignores
|
|
842
|
+
* it and acts on the text alone. No part of the loop depends on a
|
|
843
|
+
* server-side parse of this slice.
|
|
1122
844
|
*
|
|
1123
845
|
* @public
|
|
1124
846
|
*/
|
|
1125
|
-
export interface
|
|
1126
|
-
readonly kind: '
|
|
1127
|
-
/**
|
|
1128
|
-
* Human-readable one-liner summary for logs / SDK debug surfaces.
|
|
1129
|
-
* Mirrors the chat-visible text but as a structured field so
|
|
1130
|
-
* consumers don't need to parse natural language.
|
|
1131
|
-
*/
|
|
847
|
+
export interface GguiUserActionMeta {
|
|
848
|
+
readonly kind: 'user-action';
|
|
1132
849
|
readonly description: string;
|
|
1133
|
-
|
|
1134
|
-
readonly stackItemId: string;
|
|
1135
|
-
/** 8-hex FNV-1a correlation id of the gesture. */
|
|
850
|
+
readonly renderId: string;
|
|
1136
851
|
readonly actionId: string;
|
|
1137
|
-
/** ISO 8601 UTC timestamp of the gesture (iframe local clock). */
|
|
1138
852
|
readonly submittedAt: string;
|
|
1139
|
-
/** Which `actionSpec[*]` entry the iframe dispatched against. */
|
|
1140
853
|
readonly intent: string;
|
|
1141
|
-
/**
|
|
1142
|
-
* Prepared tool call the agent SHOULD dispatch verbatim. Embeds the
|
|
1143
|
-
* `stackItemId` so the SDK doesn't have to thread it manually —
|
|
1144
|
-
* reduces "wrong/missing args" failure modes.
|
|
1145
|
-
*/
|
|
1146
854
|
readonly nextStep: {
|
|
1147
855
|
readonly tool: 'ggui_consume';
|
|
1148
856
|
readonly args: {
|
|
1149
|
-
readonly
|
|
857
|
+
readonly renderId: string;
|
|
1150
858
|
};
|
|
1151
859
|
};
|
|
1152
860
|
}
|
|
1153
|
-
/**
|
|
1154
|
-
* `_meta.ggui.userAction` variant — pipe is gone; action + ui context
|
|
1155
|
-
* delivered inline. Agent acts on `payload` directly; MUST NOT call
|
|
1156
|
-
* `ggui_consume` for `stackItemId` (no pipe to drain).
|
|
1157
|
-
*
|
|
1158
|
-
* `nextStep` is optional — when the original `actionSpec[intent]` declared
|
|
1159
|
-
* a `nextStep` (the bound agent tool), it's surfaced here as a string
|
|
1160
|
-
* hint so the LLM has a strong steer toward the right tool. When the
|
|
1161
|
-
* contract author left `nextStep` undeclared, the agent is fully free
|
|
1162
|
-
* to choose how to react.
|
|
1163
|
-
*
|
|
1164
|
-
* @public
|
|
1165
|
-
*/
|
|
1166
|
-
export interface InlineUserActionMeta {
|
|
1167
|
-
readonly kind: 'inline';
|
|
1168
|
-
/**
|
|
1169
|
-
* Human-readable one-liner summary for logs / SDK debug surfaces.
|
|
1170
|
-
*/
|
|
1171
|
-
readonly description: string;
|
|
1172
|
-
/** Stack item the gesture targeted. */
|
|
1173
|
-
readonly stackItemId: string;
|
|
1174
|
-
/** 8-hex FNV-1a correlation id of the gesture. */
|
|
1175
|
-
readonly actionId: string;
|
|
1176
|
-
/** ISO 8601 UTC timestamp of the gesture (iframe local clock). */
|
|
1177
|
-
readonly submittedAt: string;
|
|
1178
|
-
/** Which `actionSpec[*]` entry the iframe dispatched against. */
|
|
1179
|
-
readonly intent: string;
|
|
1180
|
-
/**
|
|
1181
|
-
* Both halves of the gesture, captured atomically at gesture time:
|
|
1182
|
-
*
|
|
1183
|
-
* - `actionData` — typed payload satisfying `actionSpec[intent].schema`.
|
|
1184
|
-
* `null` for no-payload gestures (bare button click).
|
|
1185
|
-
* - `uiContext` — snapshot of the iframe's contextSpec values at
|
|
1186
|
-
* the moment the user fired the gesture. Typed by
|
|
1187
|
-
* the contract's `contextSpec`.
|
|
1188
|
-
*
|
|
1189
|
-
* The pair is the SEMANTIC UNIT — what the user did AND what they
|
|
1190
|
-
* were looking at when they did it. Captured at gesture time (not
|
|
1191
|
-
* drain time) for honest history.
|
|
1192
|
-
*/
|
|
1193
|
-
readonly payload: {
|
|
1194
|
-
readonly actionData: JsonValue | null;
|
|
1195
|
-
readonly uiContext: JsonObject;
|
|
1196
|
-
};
|
|
1197
|
-
/**
|
|
1198
|
-
* Optional hint: the agent tool the original `actionSpec[intent].nextStep`
|
|
1199
|
-
* declared. Present when the contract bound this intent to a specific
|
|
1200
|
-
* tool; absent when the author left it free. The agent reads this as
|
|
1201
|
-
* a strong suggestion, not a binding directive (in the inline case
|
|
1202
|
-
* the LLM composes the call, including any context-derived args).
|
|
1203
|
-
*/
|
|
1204
|
-
readonly nextStep?: string;
|
|
1205
|
-
}
|
|
1206
|
-
/**
|
|
1207
|
-
* Type guard for {@link GguiUserActionMeta}. Validates the
|
|
1208
|
-
* discriminated shape on a `ui/message` envelope's
|
|
1209
|
-
* `params._meta.ggui.userAction` field.
|
|
1210
|
-
*
|
|
1211
|
-
* Designed for `_meta.ggui.userAction`-aware consumers (sample agent
|
|
1212
|
-
* dispatcher, e2e assertions, future SDKs) to route deterministically
|
|
1213
|
-
* without speculative shape coercion.
|
|
1214
|
-
*
|
|
1215
|
-
* @public
|
|
1216
|
-
*/
|
|
1217
|
-
export declare function isGguiUserActionMeta(meta: unknown): meta is GguiUserActionMeta;
|
|
1218
861
|
//# sourceMappingURL=mcp-apps.d.ts.map
|