@ggui-ai/iframe-runtime 0.2.0-alpha.4 → 0.4.0-rc.0
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/dist/channel-transport.d.ts +20 -30
- package/dist/channel-transport.js +24 -24
- package/dist/channels/data.d.ts +5 -20
- package/dist/channels/data.js +4 -52
- package/dist/channels/index.d.ts +0 -1
- package/dist/channels/index.js +0 -1
- package/dist/channels/props-update.d.ts +9 -9
- package/dist/channels/props-update.js +9 -9
- package/dist/channels/render-frame.d.ts +5 -5
- package/dist/channels/render-frame.js +5 -5
- package/dist/compiled-validators.d.ts +2 -2
- package/dist/compiled-validators.js +1 -1
- package/dist/context-observer.d.ts +4 -4
- package/dist/context-observer.js +3 -3
- package/dist/events-polling.d.ts +7 -7
- package/dist/events-polling.js +5 -5
- package/dist/globals.d.ts +1 -1
- package/dist/host-context-emitter.d.ts +4 -4
- package/dist/host-context-emitter.js +9 -9
- package/dist/iframe-runtime.js +68 -78
- package/dist/iframe-runtime.meta.json +1 -0
- package/dist/index.d.ts +4 -4
- package/dist/index.js +1 -1
- package/dist/lifecycle.d.ts +3 -3
- package/dist/lifecycle.js +29 -6
- package/dist/mcp-app-iframe-host.d.ts +8 -8
- package/dist/mcp-app-iframe-host.js +4 -4
- package/dist/meta-parse.d.ts +3 -4
- package/dist/meta-parse.js +17 -44
- package/dist/observability.d.ts +14 -36
- package/dist/observability.js +2 -1
- package/dist/protocol-error.d.ts +38 -48
- package/dist/protocol-error.js +5 -23
- package/dist/provisional-renderer.js +3 -3
- package/dist/react-renderer.d.ts +16 -6
- package/dist/react-renderer.js +22 -40
- package/dist/registry-subscribe.d.ts +7 -7
- package/dist/registry-subscribe.js +28 -14
- package/dist/render-item.d.ts +22 -15
- package/dist/render-item.js +7 -14
- package/dist/runtime.d.ts +79 -105
- package/dist/runtime.js +219 -269
- package/dist/status-dom.d.ts +12 -12
- package/dist/status-dom.js +11 -11
- package/dist/system-cards/NoCredentialsCard.d.ts +1 -1
- package/dist/system-cards/NoCredentialsCard.js +1 -1
- package/dist/system-cards/ProtocolProbeCard.d.ts +13 -17
- package/dist/system-cards/ProtocolProbeCard.js +21 -25
- package/dist/system-cards/index.d.ts +5 -5
- package/dist/system-cards/index.js +5 -5
- package/dist/types.d.ts +29 -34
- package/dist/validation.d.ts +4 -5
- package/dist/validation.js +5 -6
- package/dist/wire-config.d.ts +43 -106
- package/dist/wire-config.js +29 -195
- package/package.json +14 -12
- package/dist/channels/feedback.d.ts +0 -14
- package/dist/channels/feedback.js +0 -19
package/dist/index.d.ts
CHANGED
|
@@ -19,7 +19,7 @@
|
|
|
19
19
|
* postMessage envelope shape parents observe on boot failure.
|
|
20
20
|
* - `ProtocolError` — canonical typed union for every failure
|
|
21
21
|
* the renderer classifies outward, wired to the postMessage
|
|
22
|
-
* envelope
|
|
22
|
+
* envelope.
|
|
23
23
|
* - `BootstrapFailureReason` — extensibly-closed union of every
|
|
24
24
|
* reason the 'bootstrap' variant can carry. Consolidates
|
|
25
25
|
* parse-time and post-parse codes in one place. (Wire-visible
|
|
@@ -34,10 +34,10 @@
|
|
|
34
34
|
*/
|
|
35
35
|
export type { McpAppAiGguiMetaParseFailureReason, McpAppAiGguiMetaParseResult, } from './types.js';
|
|
36
36
|
export { parseMetaFromGlobal, parseMetaFromToolResult, validateMeta, } from './meta-parse.js';
|
|
37
|
-
export type { RendererBootFailureReason, RendererBootFailedMessage,
|
|
37
|
+
export type { RendererBootFailureReason, RendererBootFailedMessage, } from './runtime.js';
|
|
38
38
|
export type { ProtocolError, ProtocolErrorEmitter, BootstrapFailureReason, } from './protocol-error.js';
|
|
39
|
-
export { defaultProtocolErrorEmitter, fromBootstrapFailure, fromClientContractViolation,
|
|
40
|
-
export type { ObservabilityEvent, ObservabilityEmitter, ObservabilityMessage,
|
|
39
|
+
export { defaultProtocolErrorEmitter, fromBootstrapFailure, fromClientContractViolation, fromTransportFailure, fromAuthFailure, fromUpgradeRequired, fromUnknown, } from './protocol-error.js';
|
|
40
|
+
export type { ObservabilityEvent, ObservabilityEmitter, ObservabilityMessage, SchemaVersionMismatchEvent, SubscribeFailedEvent, AuthRequiredEvent, ChannelTransportPickedEvent, ChannelTransportFallbackEvent, ChannelTransportResubscribedEvent, UnknownObservabilityEvent, } from './observability.js';
|
|
41
41
|
export { postObservabilityToParent } from './observability.js';
|
|
42
42
|
export type { LifecycleEmitter } from './lifecycle.js';
|
|
43
43
|
export { postLifecycleToParent, makeLifecycleEvent } from './lifecycle.js';
|
package/dist/index.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
1
|
export { parseMetaFromGlobal, parseMetaFromToolResult, validateMeta, } from './meta-parse.js';
|
|
2
|
-
export { defaultProtocolErrorEmitter, fromBootstrapFailure, fromClientContractViolation,
|
|
2
|
+
export { defaultProtocolErrorEmitter, fromBootstrapFailure, fromClientContractViolation, fromTransportFailure, fromAuthFailure, fromUpgradeRequired, fromUnknown, } from './protocol-error.js';
|
|
3
3
|
export { postObservabilityToParent } from './observability.js';
|
|
4
4
|
export { postLifecycleToParent, makeLifecycleEvent } from './lifecycle.js';
|
package/dist/lifecycle.d.ts
CHANGED
|
@@ -20,7 +20,7 @@
|
|
|
20
20
|
*
|
|
21
21
|
* @public
|
|
22
22
|
*/
|
|
23
|
-
import type
|
|
23
|
+
import { type McpAppLifecycleEvent, type McpAppLifecycleState } from '@ggui-ai/protocol/integrations/mcp-apps';
|
|
24
24
|
/**
|
|
25
25
|
* Caller sink for lifecycle events. Mirrors the
|
|
26
26
|
* {@link import('./observability.js').ObservabilityEmitter} posture —
|
|
@@ -52,13 +52,13 @@ export declare function postLifecycleToParent(event: McpAppLifecycleEvent): void
|
|
|
52
52
|
* Build a lifecycle event payload. Centralises the `state` →
|
|
53
53
|
* `McpAppLifecycleEvent` projection so call sites in
|
|
54
54
|
* `runtime.ts` don't repeat the `{state, ...optional}` shape and so
|
|
55
|
-
* the additive-extension semantic of `
|
|
55
|
+
* the additive-extension semantic of `sessionId` / `error` is
|
|
56
56
|
* single-sourced.
|
|
57
57
|
*
|
|
58
58
|
* @public
|
|
59
59
|
*/
|
|
60
60
|
export declare function makeLifecycleEvent(state: McpAppLifecycleState, options?: {
|
|
61
|
-
readonly
|
|
61
|
+
readonly sessionId?: string;
|
|
62
62
|
readonly error?: {
|
|
63
63
|
readonly code: string;
|
|
64
64
|
readonly message: string;
|
package/dist/lifecycle.js
CHANGED
|
@@ -1,3 +1,26 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Renderer-side emission of MCP App lifecycle events — the postMessage
|
|
3
|
+
* envelope the iframe posts to its parent on every mount-state
|
|
4
|
+
* transition. Mirrors the `<McpAppIframe>` host's outer-DOM mirror
|
|
5
|
+
* (`data-ggui-mcp-app-iframe-lifecycle="<state>"`); together they form
|
|
6
|
+
* a first-class wire surface so observers (E2E tests, accessibility
|
|
7
|
+
* scanners, third-party hosts, console inspectors) can read mount
|
|
8
|
+
* state without reaching into iframe-internal DOM.
|
|
9
|
+
*
|
|
10
|
+
* Lives here (not in `runtime.ts`) so spec-level tests can import the
|
|
11
|
+
* helper without dragging in the runtime's autostart side effects.
|
|
12
|
+
*
|
|
13
|
+
* **Producer obligations** (locked by
|
|
14
|
+
* {@link import('@ggui-ai/protocol/integrations/mcp-apps').McpAppLifecycleMessage}):
|
|
15
|
+
* - `mounting` MUST fire before bundle eval / first WS attempt.
|
|
16
|
+
* - Exactly one terminal state (`code-ready` or `error`) MUST follow
|
|
17
|
+
* for every successful boot attempt.
|
|
18
|
+
* - `disconnected` MAY follow `code-ready` on WS close; a subsequent
|
|
19
|
+
* `code-ready` MAY follow on successful reconnect (not required).
|
|
20
|
+
*
|
|
21
|
+
* @public
|
|
22
|
+
*/
|
|
23
|
+
import { MCP_APP_LIFECYCLE_TYPE, } from '@ggui-ai/protocol/integrations/mcp-apps';
|
|
1
24
|
/**
|
|
2
25
|
* Default emitter — posts a {@link McpAppLifecycleMessage} envelope to
|
|
3
26
|
* `window.parent`. Mirrors `postObservabilityToParent` /
|
|
@@ -13,7 +36,7 @@
|
|
|
13
36
|
export function postLifecycleToParent(event) {
|
|
14
37
|
if (typeof window === 'undefined' || window.parent === null)
|
|
15
38
|
return;
|
|
16
|
-
const message = { type:
|
|
39
|
+
const message = { type: MCP_APP_LIFECYCLE_TYPE, event };
|
|
17
40
|
try {
|
|
18
41
|
window.parent.postMessage(message, '*');
|
|
19
42
|
}
|
|
@@ -26,7 +49,7 @@ export function postLifecycleToParent(event) {
|
|
|
26
49
|
* Build a lifecycle event payload. Centralises the `state` →
|
|
27
50
|
* `McpAppLifecycleEvent` projection so call sites in
|
|
28
51
|
* `runtime.ts` don't repeat the `{state, ...optional}` shape and so
|
|
29
|
-
* the additive-extension semantic of `
|
|
52
|
+
* the additive-extension semantic of `sessionId` / `error` is
|
|
30
53
|
* single-sourced.
|
|
31
54
|
*
|
|
32
55
|
* @public
|
|
@@ -34,15 +57,15 @@ export function postLifecycleToParent(event) {
|
|
|
34
57
|
export function makeLifecycleEvent(state, options) {
|
|
35
58
|
// Avoid emitting `undefined` keys — the protocol's structural lock
|
|
36
59
|
// (mcp-apps.test.ts "shape lock") asserts a populated event has
|
|
37
|
-
// exactly state +
|
|
60
|
+
// exactly state + sessionId + error keys, not an Object.keys-
|
|
38
61
|
// observable `undefined` field.
|
|
39
62
|
if (options === undefined)
|
|
40
63
|
return { state };
|
|
41
|
-
const {
|
|
64
|
+
const { sessionId, error } = options;
|
|
42
65
|
return {
|
|
43
66
|
state,
|
|
44
|
-
...(typeof
|
|
45
|
-
? {
|
|
67
|
+
...(typeof sessionId === 'string' && sessionId.length > 0
|
|
68
|
+
? { sessionId }
|
|
46
69
|
: {}),
|
|
47
70
|
...(error !== undefined ? { error } : {}),
|
|
48
71
|
};
|
|
@@ -15,10 +15,10 @@
|
|
|
15
15
|
*
|
|
16
16
|
* Adapter-boundary rule (ENFORCED):
|
|
17
17
|
* - MCP Apps lifecycle messages from the embedded iframe do NOT
|
|
18
|
-
* mutate the outer ggui
|
|
19
|
-
* never forwards them to ggui-server as
|
|
18
|
+
* mutate the outer ggui render state / contracts — the host
|
|
19
|
+
* never forwards them to ggui-server as render-level events.
|
|
20
20
|
* - `ui/initialize` intentionally DOES NOT forward outer ggui
|
|
21
|
-
*
|
|
21
|
+
* render state (actionSpec, streamSpec) to the iframe.
|
|
22
22
|
*
|
|
23
23
|
* On detach, a host-initiated `ui/resource-teardown` notification
|
|
24
24
|
* is posted to the iframe BEFORE the element leaves the DOM so the
|
|
@@ -30,16 +30,16 @@
|
|
|
30
30
|
* iframe can in turn render a `ui://ggui/render` resource via its
|
|
31
31
|
* own host implementation. That recursion stays bounded because
|
|
32
32
|
* each nested iframe runs the same adapter-boundary rule — outer
|
|
33
|
-
* ggui state never leaks into the inner renderer's
|
|
33
|
+
* ggui state never leaks into the inner renderer's render through
|
|
34
34
|
* this host path. Host-to-host sandboxing is browser-enforced by
|
|
35
35
|
* the iframe's `sandbox="allow-scripts"` attribute.
|
|
36
36
|
*/
|
|
37
|
-
import type {
|
|
37
|
+
import type { McpAppsGguiSession } from '@ggui-ai/protocol/integrations/mcp-apps';
|
|
38
38
|
export interface McpAppIframeMountOptions {
|
|
39
|
-
readonly render:
|
|
40
|
-
/**
|
|
39
|
+
readonly render: McpAppsGguiSession;
|
|
40
|
+
/** GguiSession id — threaded into the proxy URL so server-side
|
|
41
41
|
* connector scoping works. */
|
|
42
|
-
readonly
|
|
42
|
+
readonly sessionId: string;
|
|
43
43
|
/**
|
|
44
44
|
* Base URL of the ggui server (origin). The host appends
|
|
45
45
|
* `/mcp-apps/resource` + `/mcp-apps/tools-call` to this. Should
|
|
@@ -12,7 +12,7 @@ const DEFAULT_THEME = {
|
|
|
12
12
|
function composeResourceUrl(opts) {
|
|
13
13
|
const base = opts.serverBaseUrl.replace(/\/$/, '');
|
|
14
14
|
const qs = new URLSearchParams({
|
|
15
|
-
|
|
15
|
+
session: opts.sessionId,
|
|
16
16
|
item: opts.itemId,
|
|
17
17
|
});
|
|
18
18
|
return `${base}/mcp-apps/resource?${qs.toString()}`;
|
|
@@ -58,7 +58,7 @@ export function mountMcpAppIframe(container, opts) {
|
|
|
58
58
|
const serverBaseUrl = opts.serverBaseUrl ?? '';
|
|
59
59
|
const resourceUrl = composeResourceUrl({
|
|
60
60
|
serverBaseUrl,
|
|
61
|
-
|
|
61
|
+
sessionId: opts.sessionId,
|
|
62
62
|
itemId: opts.render.id,
|
|
63
63
|
});
|
|
64
64
|
const toolsCallUrl = composeToolsCallUrl(serverBaseUrl);
|
|
@@ -70,7 +70,7 @@ export function mountMcpAppIframe(container, opts) {
|
|
|
70
70
|
};
|
|
71
71
|
const iframe = container.ownerDocument.createElement('iframe');
|
|
72
72
|
iframe.setAttribute('data-ggui-mcp-apps', 'iframe');
|
|
73
|
-
iframe.setAttribute('data-ggui-
|
|
73
|
+
iframe.setAttribute('data-ggui-session-id', opts.render.id);
|
|
74
74
|
iframe.setAttribute('data-ggui-connector-id', opts.render.source.connectorId);
|
|
75
75
|
iframe.src = resourceUrl;
|
|
76
76
|
iframe.title = opts.render.description ?? 'MCP App';
|
|
@@ -139,7 +139,7 @@ export function mountMcpAppIframe(container, opts) {
|
|
|
139
139
|
method: 'POST',
|
|
140
140
|
headers: { 'Content-Type': 'application/json' },
|
|
141
141
|
body: JSON.stringify({
|
|
142
|
-
|
|
142
|
+
session: opts.sessionId,
|
|
143
143
|
item: opts.render.id,
|
|
144
144
|
tool,
|
|
145
145
|
arguments: args,
|
package/dist/meta-parse.d.ts
CHANGED
|
@@ -32,10 +32,9 @@ export declare function validateMeta(meta: McpAppAiGguiRenderMeta): McpAppAiGgui
|
|
|
32
32
|
export declare function parseMetaFromGlobal(): McpAppAiGguiMetaParseResult;
|
|
33
33
|
/**
|
|
34
34
|
* Parse the render slice from a `ui/notifications/tool-result`
|
|
35
|
-
* postMessage params payload.
|
|
36
|
-
*
|
|
37
|
-
*
|
|
38
|
-
* Connector use the first; first-party hosts the second.
|
|
35
|
+
* postMessage params payload. `params` is a `CallToolResult` per the
|
|
36
|
+
* MCP-Apps spec, so the slice envelope lives at the spec-canonical
|
|
37
|
+
* top-level `params._meta` — the only accepted location.
|
|
39
38
|
*
|
|
40
39
|
* Used by the runtime's autostart resolver as the second / third boot
|
|
41
40
|
* source (drained from the pre-runtime-load buffer or caught by the
|
package/dist/meta-parse.js
CHANGED
|
@@ -14,12 +14,10 @@
|
|
|
14
14
|
* JSON-RPC round-trip.
|
|
15
15
|
* 2. `ui/notifications/tool-result` postMessage — spec-canonical
|
|
16
16
|
* per MCP-Apps SEP-1865. `params` is a `CallToolResult` per the
|
|
17
|
-
* spec, so slices live under `params._meta`.
|
|
18
|
-
*
|
|
19
|
-
*
|
|
20
|
-
*
|
|
21
|
-
* `@mcp-ui/client`, ChatGPT MCP-Apps connector, claude.ai)
|
|
22
|
-
* deliver slice meta through this channel exclusively.
|
|
17
|
+
* spec, so slices live under `params._meta`. Hosts (`<AppRenderer>`
|
|
18
|
+
* from `@mcp-ui/client`, the RN `<McpAppIframe>`, ChatGPT MCP-Apps
|
|
19
|
+
* connector, claude.ai) deliver slice meta through this channel
|
|
20
|
+
* exclusively.
|
|
23
21
|
*
|
|
24
22
|
* Each extractor reaches the `_meta` object and calls the protocol's
|
|
25
23
|
* {@link parseMcpAppAiGguiRenderMeta} to produce a typed slice.
|
|
@@ -27,8 +25,7 @@
|
|
|
27
25
|
* {@link validateMeta} layers in mode-discriminator + expiresAt
|
|
28
26
|
* expiration checks and field-level defensive parsing for the
|
|
29
27
|
* optional containers (`contextSlots`, `gadgets`, `publicEnv`,
|
|
30
|
-
* `permissionsPolicy`, `
|
|
31
|
-
* `streamWebSocketLocalTools`).
|
|
28
|
+
* `permissionsPolicy`, `streamWebSocketLocalTools`).
|
|
32
29
|
*
|
|
33
30
|
* Reading-B (`result.toolOutput._meta` on the `ui/initialize`
|
|
34
31
|
* response) was retired in Phase 1.19b.3 — the
|
|
@@ -183,10 +180,9 @@ function projectPublicEnv(raw) {
|
|
|
183
180
|
* in the past AND the slice carries static content (codeUrl / kind),
|
|
184
181
|
* DEGRADE to static-only (drop `wsUrl` / `wsToken` / `expiresAt`);
|
|
185
182
|
* otherwise return EXPIRED_BOOTSTRAP.
|
|
186
|
-
* - `gadgets` / `publicEnv` / `
|
|
187
|
-
* `
|
|
188
|
-
*
|
|
189
|
-
* validation; malformed → defaulted.
|
|
183
|
+
* - `gadgets` / `publicEnv` / `permissionsPolicy` /
|
|
184
|
+
* `streamWebSocketLocalTools` / `contextSlots` get defensive
|
|
185
|
+
* entry-level validation; malformed → defaulted.
|
|
190
186
|
*/
|
|
191
187
|
function projectMeta(meta, hasStaticContent) {
|
|
192
188
|
let expiresAt = meta.expiresAt;
|
|
@@ -208,11 +204,6 @@ function projectMeta(meta, hasStaticContent) {
|
|
|
208
204
|
}
|
|
209
205
|
const gadgets = projectGadgets(meta.gadgets);
|
|
210
206
|
const publicEnv = projectPublicEnv(meta.publicEnv);
|
|
211
|
-
const appCallableToolsRaw = meta.appCallableTools;
|
|
212
|
-
const appCallableTools = Array.isArray(appCallableToolsRaw) &&
|
|
213
|
-
appCallableToolsRaw.every((s) => typeof s === 'string')
|
|
214
|
-
? appCallableToolsRaw
|
|
215
|
-
: [];
|
|
216
207
|
const permissionsPolicyRaw = meta.permissionsPolicy;
|
|
217
208
|
const permissionsPolicy = Array.isArray(permissionsPolicyRaw) &&
|
|
218
209
|
permissionsPolicyRaw.every((s) => typeof s === 'string' && s.length > 0)
|
|
@@ -231,18 +222,11 @@ function projectMeta(meta, hasStaticContent) {
|
|
|
231
222
|
const themeMode = themeModeRaw === 'light' || themeModeRaw === 'dark'
|
|
232
223
|
? themeModeRaw
|
|
233
224
|
: undefined;
|
|
234
|
-
const actionNextStepsRaw = meta.actionNextSteps;
|
|
235
|
-
let actionNextSteps;
|
|
236
|
-
if (isPlainObject(actionNextStepsRaw) &&
|
|
237
|
-
Object.values(actionNextStepsRaw).every((v) => typeof v === 'string')) {
|
|
238
|
-
actionNextSteps = actionNextStepsRaw;
|
|
239
|
-
}
|
|
240
225
|
const contextSlots = projectContextSlots(meta.contextSlots);
|
|
241
226
|
const projected = {
|
|
242
|
-
|
|
227
|
+
sessionId: meta.sessionId,
|
|
243
228
|
appId: meta.appId,
|
|
244
229
|
runtimeUrl: meta.runtimeUrl,
|
|
245
|
-
appCallableTools,
|
|
246
230
|
...(dropLiveCreds
|
|
247
231
|
? {}
|
|
248
232
|
: {
|
|
@@ -256,6 +240,9 @@ function projectMeta(meta, hasStaticContent) {
|
|
|
256
240
|
: {}),
|
|
257
241
|
...(meta.themeId !== undefined ? { themeId: meta.themeId } : {}),
|
|
258
242
|
...(themeMode !== undefined ? { themeMode } : {}),
|
|
243
|
+
// Per-app theme overlay carried through verbatim. Already validated
|
|
244
|
+
// via `appThemeSchema` by the wire parser, so no re-validation here.
|
|
245
|
+
...(meta.theme !== undefined ? { theme: meta.theme } : {}),
|
|
259
246
|
...(gadgets !== undefined ? { gadgets } : {}),
|
|
260
247
|
...(publicEnv !== undefined ? { publicEnv } : {}),
|
|
261
248
|
...(streamWebSocketLocalTools !== undefined
|
|
@@ -264,7 +251,6 @@ function projectMeta(meta, hasStaticContent) {
|
|
|
264
251
|
...(permissionsPolicy !== undefined ? { permissionsPolicy } : {}),
|
|
265
252
|
...(meta.lastSequence !== undefined ? { lastSequence: meta.lastSequence } : {}),
|
|
266
253
|
...(meta.propsJson !== undefined ? { propsJson: meta.propsJson } : {}),
|
|
267
|
-
...(actionNextSteps !== undefined ? { actionNextSteps } : {}),
|
|
268
254
|
...(contextSlots !== undefined ? { contextSlots } : {}),
|
|
269
255
|
...(meta.contractHash !== undefined && meta.validatorsUrl !== undefined
|
|
270
256
|
? {
|
|
@@ -349,10 +335,9 @@ export function parseMetaFromGlobal() {
|
|
|
349
335
|
}
|
|
350
336
|
/**
|
|
351
337
|
* Parse the render slice from a `ui/notifications/tool-result`
|
|
352
|
-
* postMessage params payload.
|
|
353
|
-
*
|
|
354
|
-
*
|
|
355
|
-
* Connector use the first; first-party hosts the second.
|
|
338
|
+
* postMessage params payload. `params` is a `CallToolResult` per the
|
|
339
|
+
* MCP-Apps spec, so the slice envelope lives at the spec-canonical
|
|
340
|
+
* top-level `params._meta` — the only accepted location.
|
|
356
341
|
*
|
|
357
342
|
* Used by the runtime's autostart resolver as the second / third boot
|
|
358
343
|
* source (drained from the pre-runtime-load buffer or caught by the
|
|
@@ -362,23 +347,11 @@ export function parseMetaFromToolResult(params) {
|
|
|
362
347
|
if (!isPlainObject(params)) {
|
|
363
348
|
return { ok: false, reason: 'MISSING_TOOL_OUTPUT' };
|
|
364
349
|
}
|
|
365
|
-
let meta;
|
|
366
350
|
const topMeta = params['_meta'];
|
|
367
|
-
if (isPlainObject(topMeta)) {
|
|
368
|
-
meta = topMeta;
|
|
369
|
-
}
|
|
370
|
-
else {
|
|
371
|
-
const toolOutput = params['toolOutput'];
|
|
372
|
-
if (isPlainObject(toolOutput)) {
|
|
373
|
-
const nestedMeta = toolOutput['_meta'];
|
|
374
|
-
if (isPlainObject(nestedMeta)) {
|
|
375
|
-
meta = nestedMeta;
|
|
376
|
-
}
|
|
377
|
-
}
|
|
378
|
-
}
|
|
379
|
-
if (meta === undefined) {
|
|
351
|
+
if (!isPlainObject(topMeta)) {
|
|
380
352
|
return { ok: false, reason: 'MISSING_META_GGUI_BOOTSTRAP' };
|
|
381
353
|
}
|
|
354
|
+
const meta = topMeta;
|
|
382
355
|
const parsed = parseMcpAppAiGguiRenderMeta(meta);
|
|
383
356
|
if (!parsed.ok) {
|
|
384
357
|
return { ok: false, reason: 'MALFORMED_BOOTSTRAP' };
|
package/dist/observability.d.ts
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { MCP_APP_OBSERVE_TYPE } from '@ggui-ai/protocol/integrations/mcp-apps';
|
|
1
2
|
/**
|
|
2
3
|
* Observability events emitted by the renderer iframe → surfaced to
|
|
3
4
|
* the MCP Apps host via the `<McpAppIframe>` wrapper's `onObserve`
|
|
@@ -23,39 +24,16 @@
|
|
|
23
24
|
* - `@ggui-ai/iframe-runtime` owns this union, NOT `@ggui-ai/protocol`.
|
|
24
25
|
* Observability events are a renderer ↔ host implementation seam,
|
|
25
26
|
* not a wire-format contract between arbitrary protocol peers, so
|
|
26
|
-
* they stay out of the protocol package.
|
|
27
|
+
* they stay out of the protocol package. The ENVELOPE TAG
|
|
28
|
+
* (`ggui:observe`) IS protocol-owned, though — it belongs to the
|
|
29
|
+
* renderer → host postMessage envelope family
|
|
30
|
+
* (`MCP_APP_OBSERVE_TYPE` in
|
|
31
|
+
* `@ggui-ai/protocol/integrations/mcp-apps`), so hosts on every
|
|
32
|
+
* platform classify it from one vocabulary.
|
|
27
33
|
*
|
|
28
34
|
* @public
|
|
29
35
|
*/
|
|
30
|
-
export type ObservabilityEvent =
|
|
31
|
-
/**
|
|
32
|
-
* Fired when a wired action successfully dispatched to a registered
|
|
33
|
-
* MCP tool. Hosts surface this as a dispatch row in RenderInspector.
|
|
34
|
-
* Emitted by the server-side router (session-channel.ts) via
|
|
35
|
-
* postMessage relay through the renderer.
|
|
36
|
-
*
|
|
37
|
-
* @public
|
|
38
|
-
*/
|
|
39
|
-
export interface WiredToolInvokedEvent {
|
|
40
|
-
readonly kind: 'wired-tool-invoked';
|
|
41
|
-
readonly toolName: string;
|
|
42
|
-
readonly actionName?: string;
|
|
43
|
-
readonly dispatchedAt: string;
|
|
44
|
-
readonly latencyMs?: number;
|
|
45
|
-
}
|
|
46
|
-
/**
|
|
47
|
-
* Fired when the server emits a `_ggui:contract-error` envelope on
|
|
48
|
-
* the live channel. Mirrors the envelope's `code` field for host-side
|
|
49
|
-
* inspection without requiring the host to parse channel frames.
|
|
50
|
-
*
|
|
51
|
-
* @public
|
|
52
|
-
*/
|
|
53
|
-
export interface ContractErrorEmittedEvent {
|
|
54
|
-
readonly kind: 'contract-error-emitted';
|
|
55
|
-
readonly code: string;
|
|
56
|
-
readonly toolName: string;
|
|
57
|
-
readonly actionName?: string;
|
|
58
|
-
}
|
|
36
|
+
export type ObservabilityEvent = SchemaVersionMismatchEvent | SubscribeFailedEvent | AuthRequiredEvent | ChannelTransportPickedEvent | ChannelTransportFallbackEvent | ChannelTransportResubscribedEvent | UnknownObservabilityEvent;
|
|
59
37
|
/**
|
|
60
38
|
* Fired when the protocol-version handshake rejects the connection.
|
|
61
39
|
* Parallel to `UpgradeRequiredError` — the host can choose to render
|
|
@@ -72,7 +50,7 @@ export interface SchemaVersionMismatchEvent {
|
|
|
72
50
|
}
|
|
73
51
|
/**
|
|
74
52
|
* Fired when subscribe fails for any reason the renderer classifies
|
|
75
|
-
* as non-fatal to the
|
|
53
|
+
* as non-fatal to the render (e.g., transient network jitter that
|
|
76
54
|
* the reconnect ladder is handling). Terminal subscribe failures
|
|
77
55
|
* still route through `ProtocolError`.
|
|
78
56
|
*
|
|
@@ -86,7 +64,7 @@ export interface SubscribeFailedEvent {
|
|
|
86
64
|
/**
|
|
87
65
|
* Fired when the server emits a `system` frame with
|
|
88
66
|
* `action: 'auth_required'` — the agent needs the end-user to authorize
|
|
89
|
-
* an OAuth service (Google, Slack, etc.) before
|
|
67
|
+
* an OAuth service (Google, Slack, etc.) before its tool call can
|
|
90
68
|
* proceed. The host listens via `<McpAppIframe onObserve>` and renders
|
|
91
69
|
* a consent overlay OUTSIDE the iframe, then redirects the user to
|
|
92
70
|
* `authUrl` to complete the OAuth flow.
|
|
@@ -144,7 +122,7 @@ export interface AuthRequiredEvent {
|
|
|
144
122
|
*/
|
|
145
123
|
export interface ChannelTransportPickedEvent {
|
|
146
124
|
readonly kind: 'channel-transport-picked';
|
|
147
|
-
readonly
|
|
125
|
+
readonly sessionId: string;
|
|
148
126
|
readonly channelName: string;
|
|
149
127
|
readonly transport: 'ws' | 'poll';
|
|
150
128
|
}
|
|
@@ -159,7 +137,7 @@ export interface ChannelTransportPickedEvent {
|
|
|
159
137
|
*/
|
|
160
138
|
export interface ChannelTransportFallbackEvent {
|
|
161
139
|
readonly kind: 'channel-transport-fallback';
|
|
162
|
-
readonly
|
|
140
|
+
readonly sessionId: string;
|
|
163
141
|
readonly channelName: string;
|
|
164
142
|
readonly reason: 'ws-disconnect' | 'channel-not-local';
|
|
165
143
|
}
|
|
@@ -171,7 +149,7 @@ export interface ChannelTransportFallbackEvent {
|
|
|
171
149
|
*/
|
|
172
150
|
export interface ChannelTransportResubscribedEvent {
|
|
173
151
|
readonly kind: 'channel-transport-resubscribed';
|
|
174
|
-
readonly
|
|
152
|
+
readonly sessionId: string;
|
|
175
153
|
readonly channelName: string;
|
|
176
154
|
}
|
|
177
155
|
/**
|
|
@@ -194,7 +172,7 @@ export interface UnknownObservabilityEvent {
|
|
|
194
172
|
* @public
|
|
195
173
|
*/
|
|
196
174
|
export interface ObservabilityMessage {
|
|
197
|
-
readonly type:
|
|
175
|
+
readonly type: typeof MCP_APP_OBSERVE_TYPE;
|
|
198
176
|
readonly event: ObservabilityEvent;
|
|
199
177
|
}
|
|
200
178
|
/**
|
package/dist/observability.js
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { MCP_APP_OBSERVE_TYPE } from '@ggui-ai/protocol/integrations/mcp-apps';
|
|
1
2
|
/**
|
|
2
3
|
* Default emitter — posts an {@link ObservabilityMessage} to
|
|
3
4
|
* `window.parent`. Mirrors `postBootFailure` / `postRendererReady`
|
|
@@ -17,7 +18,7 @@ export function postObservabilityToParent(event) {
|
|
|
17
18
|
// install.
|
|
18
19
|
if (typeof window === 'undefined' || window.parent === null)
|
|
19
20
|
return;
|
|
20
|
-
const message = { type:
|
|
21
|
+
const message = { type: MCP_APP_OBSERVE_TYPE, event };
|
|
21
22
|
try {
|
|
22
23
|
window.parent.postMessage(message, '*');
|
|
23
24
|
}
|
package/dist/protocol-error.d.ts
CHANGED
|
@@ -1,16 +1,19 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* `ProtocolError` typed union — canonical shape for every error the
|
|
3
|
-
* renderer surfaces OUTWARD: to the host page via postMessage
|
|
4
|
-
*
|
|
5
|
-
* in-iframe status DOM.
|
|
3
|
+
* renderer surfaces OUTWARD: to the host page via postMessage and to
|
|
4
|
+
* the in-iframe status DOM.
|
|
6
5
|
*
|
|
7
6
|
* The transport wiring around this union:
|
|
8
7
|
*
|
|
9
|
-
* -
|
|
10
|
-
*
|
|
11
|
-
*
|
|
8
|
+
* - Boot-path failures reach the host page via the protocol-owned
|
|
9
|
+
* `postMessage({type:'ggui:bootstrap-failed', reason, message})`
|
|
10
|
+
* envelope (`MCP_APP_BOOTSTRAP_FAILED_TYPE`). There is NO
|
|
11
|
+
* `ggui:protocol-error`
|
|
12
|
+
* postMessage envelope — the typed union itself travels only
|
|
13
|
+
* through the in-process {@link ProtocolErrorEmitter} seam.
|
|
12
14
|
* - `<McpAppIframe onError={(err: ProtocolError) => …}>` surfaces
|
|
13
|
-
* these to embedding apps
|
|
15
|
+
* these to embedding apps (the host wrapper bridges the emitter
|
|
16
|
+
* seam / the bootstrap-failed envelope to its `onError` prop).
|
|
14
17
|
*
|
|
15
18
|
* The union is the failure-mode axis of `WireConfig`'s contract:
|
|
16
19
|
* named parties = renderer ↔ host; obligation = the renderer emits a
|
|
@@ -25,7 +28,6 @@
|
|
|
25
28
|
* fields) is fully closed — drift there is a real shape change, not
|
|
26
29
|
* a new code.
|
|
27
30
|
*/
|
|
28
|
-
import type { ContractErrorPayload } from '@ggui-ai/protocol';
|
|
29
31
|
import { ClientContractViolationError } from '@ggui-ai/wire';
|
|
30
32
|
/**
|
|
31
33
|
* Closed-or-extensibly-closed union of bootstrap-failure reasons.
|
|
@@ -43,7 +45,7 @@ import { ClientContractViolationError } from '@ggui-ai/wire';
|
|
|
43
45
|
* / `WS_HANDSHAKE_FAILED`. Matches `RendererBootFailureReason`.
|
|
44
46
|
*
|
|
45
47
|
* 3. **Transport-observable failures** — `BUNDLE_FETCH_FAILED` /
|
|
46
|
-
* `CSP_VIOLATION` / `
|
|
48
|
+
* `CSP_VIOLATION` / `SESSION_NOT_FOUND` / `AUTH_REJECTED`. These
|
|
47
49
|
* fire before the renderer parse path runs, so they're authored
|
|
48
50
|
* here rather than inside `McpAppAiGguiMetaParseFailureReason`.
|
|
49
51
|
*
|
|
@@ -51,7 +53,14 @@ import { ClientContractViolationError } from '@ggui-ai/wire';
|
|
|
51
53
|
* codes can land without a client bump, while IDE autocomplete
|
|
52
54
|
* surfaces the canonical set.
|
|
53
55
|
*/
|
|
54
|
-
export type BootstrapFailureReason = 'MISSING_TOOL_OUTPUT' | 'MISSING_META_GGUI_BOOTSTRAP' | 'BOOTSTRAP_META_MISSING' | 'MALFORMED_BOOTSTRAP' | 'EXPIRED_BOOTSTRAP' | 'UI_INITIALIZE_FAILED' | 'WS_HANDSHAKE_FAILED' | 'UPGRADE_REQUIRED' | 'BUNDLE_FETCH_FAILED' | 'CSP_VIOLATION' | '
|
|
56
|
+
export type BootstrapFailureReason = 'MISSING_TOOL_OUTPUT' | 'MISSING_META_GGUI_BOOTSTRAP' | 'BOOTSTRAP_META_MISSING' | 'MALFORMED_BOOTSTRAP' | 'EXPIRED_BOOTSTRAP' | 'UI_INITIALIZE_FAILED' | 'WS_HANDSHAKE_FAILED' | 'UPGRADE_REQUIRED' | 'BUNDLE_FETCH_FAILED' | 'CSP_VIOLATION' | 'SESSION_NOT_FOUND' | 'AUTH_REJECTED' | (string & {});
|
|
57
|
+
/**
|
|
58
|
+
* Pre-ack auth-class error codes — the codes first-party servers
|
|
59
|
+
* ACTUALLY emit on the WS error frame before `ack` (SPEC §12.2.3).
|
|
60
|
+
* The phantom `AUTH_REJECTED` / `TOKEN_EXPIRED` arms (no first-party
|
|
61
|
+
* server ever minted them) were deleted in draft-2026-06-12.
|
|
62
|
+
*/
|
|
63
|
+
export type AuthFailureCode = 'SESSION_NOT_FOUND' | 'BOOTSTRAP_EXPIRED' | 'BOOTSTRAP_INVALID' | 'BOOTSTRAP_SESSION_MISMATCH' | 'BOOTSTRAP_APP_MISMATCH' | 'UNAUTHENTICATED';
|
|
55
64
|
/**
|
|
56
65
|
* Every error the renderer surfaces outward flows through this union.
|
|
57
66
|
*
|
|
@@ -60,18 +69,20 @@ export type BootstrapFailureReason = 'MISSING_TOOL_OUTPUT' | 'MISSING_META_GGUI_
|
|
|
60
69
|
* - `transport` — WebSocket-level failure (DISCONNECTED / TIMEOUT).
|
|
61
70
|
* Includes `retryable` so hosts can decide to reconnect
|
|
62
71
|
* vs. give up.
|
|
63
|
-
* - `auth` — session / token rejection pre-
|
|
64
|
-
*
|
|
65
|
-
* `
|
|
66
|
-
*
|
|
67
|
-
*
|
|
72
|
+
* - `auth` — session / ws-token rejection pre-ack, carrying
|
|
73
|
+
* the server's REAL §12.2.3 vocabulary:
|
|
74
|
+
* `SESSION_NOT_FOUND`, the `BOOTSTRAP_*` ws-token
|
|
75
|
+
* family, and `UNAUTHENTICATED`. `BOOTSTRAP_EXPIRED`
|
|
76
|
+
* has a refresh path (`ggui_runtime_refresh_ws_token`);
|
|
77
|
+
* the rest are terminal — hosts typically escalate
|
|
78
|
+
* to a re-render / login flow.
|
|
79
|
+
* - `protocol` — envelope / render-mismatch failures post-handshake.
|
|
68
80
|
* Extensibly-closed on `code` because servers may
|
|
69
81
|
* introduce new codes without a client bump.
|
|
70
82
|
* `details` carries opaque structured context.
|
|
71
|
-
*
|
|
72
|
-
*
|
|
73
|
-
*
|
|
74
|
-
* unchanged through this variant.
|
|
83
|
+
* Client-side contract violations ride this variant
|
|
84
|
+
* as `code: 'CLIENT_CONTRACT_VIOLATION'` (see
|
|
85
|
+
* {@link fromClientContractViolation}).
|
|
75
86
|
* - `bootstrap` — anything in {@link BootstrapFailureReason}.
|
|
76
87
|
* - `version` — `UPGRADE_REQUIRED` path (handshake rejection).
|
|
77
88
|
* `serverVersion` may be absent on the wire; callers
|
|
@@ -87,16 +98,13 @@ export type ProtocolError = {
|
|
|
87
98
|
readonly message?: string;
|
|
88
99
|
} | {
|
|
89
100
|
readonly kind: 'auth';
|
|
90
|
-
readonly code:
|
|
101
|
+
readonly code: AuthFailureCode;
|
|
91
102
|
readonly message?: string;
|
|
92
103
|
} | {
|
|
93
104
|
readonly kind: 'protocol';
|
|
94
105
|
readonly code: 'SESSION_MISMATCH' | 'APP_MISMATCH' | 'MALFORMED_ENVELOPE' | (string & {});
|
|
95
106
|
readonly message?: string;
|
|
96
107
|
readonly details?: unknown;
|
|
97
|
-
} | {
|
|
98
|
-
readonly kind: 'contract';
|
|
99
|
-
readonly payload: ContractErrorPayload;
|
|
100
108
|
} | {
|
|
101
109
|
readonly kind: 'bootstrap';
|
|
102
110
|
readonly reason: BootstrapFailureReason;
|
|
@@ -115,9 +123,7 @@ export type ProtocolError = {
|
|
|
115
123
|
*
|
|
116
124
|
* This is OPTIONAL on every emitter site — the default is a tagged
|
|
117
125
|
* `console.warn` so operators see the error in dev. The
|
|
118
|
-
* `<McpAppIframe>` host wrapper bridges this to its `onError` prop
|
|
119
|
-
* session-bound variants are ALSO routed to the
|
|
120
|
-
* `_ggui:contract-error` envelope over the WebSocket.
|
|
126
|
+
* `<McpAppIframe>` host wrapper bridges this to its `onError` prop.
|
|
121
127
|
*
|
|
122
128
|
* Handlers MUST NOT throw — the emitter already fired the renderer's
|
|
123
129
|
* own fallback path; a throwing handler would mask the real error.
|
|
@@ -127,7 +133,7 @@ export type ProtocolError = {
|
|
|
127
133
|
export type ProtocolErrorEmitter = (err: ProtocolError) => void;
|
|
128
134
|
/**
|
|
129
135
|
* Default emitter — operator-visible `console.warn` with a grep-
|
|
130
|
-
* friendly tag. Identical posture to `
|
|
136
|
+
* friendly tag. Identical posture to `GguiRender.surfaceContractViolation`'s
|
|
131
137
|
* console.warn fallback. Callers override by injecting their own.
|
|
132
138
|
*/
|
|
133
139
|
export declare function defaultProtocolErrorEmitter(err: ProtocolError): void;
|
|
@@ -137,28 +143,12 @@ export declare function defaultProtocolErrorEmitter(err: ProtocolError): void;
|
|
|
137
143
|
* outbound action, or when an inbound stream / props payload fails the
|
|
138
144
|
* local validator) onto a `ProtocolError`.
|
|
139
145
|
*
|
|
140
|
-
*
|
|
141
|
-
*
|
|
142
|
-
* is tool-failure-scoped and authored by the session-channel router.
|
|
143
|
-
* A client-side violation is a DIFFERENT category: nothing reached the
|
|
144
|
-
* tool. We surface it as `{kind: 'protocol'; code:
|
|
146
|
+
* A client-side violation never left the renderer — nothing reached
|
|
147
|
+
* the server. We surface it as `{kind: 'protocol'; code:
|
|
145
148
|
* 'CLIENT_CONTRACT_VIOLATION'}` with structured `details` so hosts can
|
|
146
149
|
* render the same summary that `console.warn` would have produced.
|
|
147
|
-
*
|
|
148
|
-
* Server-emitted `ContractErrorPayload` landing on
|
|
149
|
-
* `_ggui:contract-error` rides the separate `{kind: 'contract'}`
|
|
150
|
-
* variant — that path activates when the renderer observes the
|
|
151
|
-
* reserved channel.
|
|
152
150
|
*/
|
|
153
151
|
export declare function fromClientContractViolation(err: ClientContractViolationError): ProtocolError;
|
|
154
|
-
/**
|
|
155
|
-
* Wrap a server-emitted `ContractErrorPayload` (received over the
|
|
156
|
-
* reserved `_ggui:contract-error` channel) as the 'contract' variant.
|
|
157
|
-
* Trivial wrapper — exists so call sites produce the union via a
|
|
158
|
-
* factory rather than an object literal (keeps emission uniform and
|
|
159
|
-
* makes grep for creation sites exact).
|
|
160
|
-
*/
|
|
161
|
-
export declare function fromContractErrorPayload(payload: ContractErrorPayload): ProtocolError;
|
|
162
152
|
/**
|
|
163
153
|
* Build a `ProtocolError` of kind 'version' from the existing
|
|
164
154
|
* `UpgradeRequiredError` class. Matches the `liftUpgradeRequiredFromError`
|
|
@@ -193,7 +183,7 @@ export declare function fromUnknown(raw: unknown): ProtocolError;
|
|
|
193
183
|
*/
|
|
194
184
|
export declare function fromTransportFailure(code: 'DISCONNECTED' | 'TIMEOUT', retryable: boolean, message?: string): ProtocolError;
|
|
195
185
|
/**
|
|
196
|
-
* Build a `ProtocolError` of kind 'auth' — session / token rejection
|
|
197
|
-
* pre-
|
|
186
|
+
* Build a `ProtocolError` of kind 'auth' — session / ws-token rejection
|
|
187
|
+
* pre-ack (see {@link AuthFailureCode}).
|
|
198
188
|
*/
|
|
199
|
-
export declare function fromAuthFailure(code:
|
|
189
|
+
export declare function fromAuthFailure(code: AuthFailureCode, message?: string): ProtocolError;
|