@ggui-ai/iframe-runtime 0.1.0-rc.3 → 0.2.0-alpha.3

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.
Files changed (83) hide show
  1. package/README.md +7 -6
  2. package/dist/channel-transport.d.ts +15 -15
  3. package/dist/channel-transport.js +32 -35
  4. package/dist/channels/channel-payload.d.ts +2 -2
  5. package/dist/channels/channel-payload.js +1 -1
  6. package/dist/channels/data.d.ts +14 -19
  7. package/dist/channels/data.js +13 -31
  8. package/dist/channels/drain-ack.d.ts +2 -2
  9. package/dist/channels/drain-ack.js +1 -1
  10. package/dist/channels/feedback.d.ts +1 -1
  11. package/dist/channels/index.d.ts +1 -1
  12. package/dist/channels/index.js +1 -1
  13. package/dist/channels/props-update.d.ts +33 -37
  14. package/dist/channels/props-update.js +30 -106
  15. package/dist/channels/render-frame.d.ts +52 -0
  16. package/dist/channels/render-frame.js +53 -0
  17. package/dist/channels/system.d.ts +1 -1
  18. package/dist/compiled-validators.d.ts +15 -0
  19. package/dist/compiled-validators.js +31 -0
  20. package/dist/context-observer.d.ts +68 -8
  21. package/dist/context-observer.js +25 -45
  22. package/dist/events-polling.d.ts +95 -0
  23. package/dist/events-polling.js +143 -0
  24. package/dist/gadget-loader.d.ts +1 -1
  25. package/dist/globals.d.ts +4 -4
  26. package/dist/host-context-emitter.d.ts +84 -20
  27. package/dist/host-context-emitter.js +159 -29
  28. package/dist/iframe-runtime.js +98 -59
  29. package/dist/index.d.ts +18 -13
  30. package/dist/index.js +1 -1
  31. package/dist/lifecycle.d.ts +2 -2
  32. package/dist/lifecycle.js +5 -5
  33. package/dist/mcp-app-iframe-host.d.ts +9 -9
  34. package/dist/mcp-app-iframe-host.js +16 -17
  35. package/dist/meta-parse.d.ts +44 -0
  36. package/dist/meta-parse.js +390 -0
  37. package/dist/observability.d.ts +6 -6
  38. package/dist/protocol-error.d.ts +7 -7
  39. package/dist/provisional-renderer.d.ts +1 -1
  40. package/dist/provisional-renderer.js +1 -1
  41. package/dist/react-renderer.d.ts +10 -11
  42. package/dist/react-renderer.js +8 -8
  43. package/dist/registry-subscribe.d.ts +27 -9
  44. package/dist/registry-subscribe.js +32 -27
  45. package/dist/render-item.d.ts +83 -0
  46. package/dist/{stack-item-renderer.js → render-item.js} +106 -106
  47. package/dist/runtime.d.ts +183 -131
  48. package/dist/runtime.js +1264 -1330
  49. package/dist/status-dom.d.ts +52 -47
  50. package/dist/status-dom.js +51 -34
  51. package/dist/system-cards/NoCredentialsCard.d.ts +1 -1
  52. package/dist/system-cards/NoCredentialsCard.js +1 -1
  53. package/dist/system-cards/ProtocolProbeCard.js +2 -2
  54. package/dist/system-cards/index.d.ts +4 -4
  55. package/dist/system-cards/index.js +4 -4
  56. package/dist/types.d.ts +70 -34
  57. package/dist/types.js +1 -1
  58. package/dist/validation.d.ts +2 -2
  59. package/dist/validation.js +2 -2
  60. package/dist/wire-config.d.ts +54 -82
  61. package/dist/wire-config.js +61 -93
  62. package/package.json +9 -8
  63. package/dist/bootstrap.d.ts +0 -72
  64. package/dist/bootstrap.js +0 -500
  65. package/dist/canvas/animator/animator-host.d.ts +0 -63
  66. package/dist/canvas/animator/animator-host.js +0 -105
  67. package/dist/canvas/animator/animator-pill.d.ts +0 -42
  68. package/dist/canvas/animator/animator-pill.js +0 -119
  69. package/dist/canvas/animator/state-machine.d.ts +0 -106
  70. package/dist/canvas/animator/state-machine.js +0 -150
  71. package/dist/canvas/canvas-shell.d.ts +0 -77
  72. package/dist/canvas/canvas-shell.js +0 -117
  73. package/dist/canvas/display-mode-policy.d.ts +0 -104
  74. package/dist/canvas/display-mode-policy.js +0 -98
  75. package/dist/canvas/mount.d.ts +0 -150
  76. package/dist/canvas/mount.js +0 -202
  77. package/dist/canvas/nav-stack.d.ts +0 -133
  78. package/dist/canvas/nav-stack.js +0 -172
  79. package/dist/channels/push.d.ts +0 -47
  80. package/dist/channels/push.js +0 -54
  81. package/dist/stack-item-renderer.d.ts +0 -103
  82. package/dist/stack.d.ts +0 -90
  83. package/dist/stack.js +0 -118
@@ -1,26 +1,12 @@
1
1
  /**
2
- * Per-stack-item `WireConfig` factory for the renderer iframe.
2
+ * Per-render `WireConfig` factory for the renderer iframe.
3
3
  *
4
- * The renderer iframe uses a standalone
5
- * `scopeWireConfig(root, item, internals)` function plus the
6
- * `RootWireConfigBundle.buildScopedConfig(item)` closure returned
7
- * from `buildRootWireConfig` — there is no scoping method on the
8
- * public `WireConfig` shape.
9
- *
10
- * Why per-item scoping:
11
- * - A stacked/chat-style UI keeps older cards rendered beneath the
12
- * top one. A `useAction('submit')` call inside an older card MUST
13
- * emit with THAT card's `stackItemId` + `tool` + `actionSpec` — not the
14
- * top-of-stack's. Without per-item scoping, contract enforcement
15
- * would reject cross-stack dispatches as "wrong contract" OR the
16
- * wrong tool would be invoked server-side.
17
- *
18
- * Construction: the renderer builds ONE top-level bundle at boot
19
- * (`buildRootWireConfig`), which returns `{config, buildScopedConfig}`.
20
- * The runtime's `stackCtx.getScopedWireConfig` calls
21
- * `buildScopedConfig(item)` per stack entry; each
22
- * `<GguiWireProvider>` wraps a stack item with the resulting scoped
23
- * config.
4
+ * The renderer iframe mounts EXACTLY ONE {@link Render} per iframe
5
+ * post-render-identity-collapse (2026-05-27). There is no per-item
6
+ * scoping factory anymore — the WireConfig is built once at boot,
7
+ * keyed by the bootstrap's `renderId`, and the active render's
8
+ * `actionSpec` is wired in via the {@link buildRootWireConfig}'s
9
+ * `getCurrentRender` thunk.
24
10
  *
25
11
  * Outbound dispatch uses `buildActionEnvelope` (from
26
12
  * `@ggui-ai/wire`) + `validateOutboundActionEnvelope` + a direct
@@ -29,16 +15,17 @@
29
15
  * The `WireConfig` shape lives in `@ggui-ai/wire`. This module
30
16
  * implements against that interface.
31
17
  */
32
- import type { ActionSpec, SessionStackEntry, StreamEnvelope } from '@ggui-ai/protocol';
18
+ import type { ActionEnvelope, Render, StreamEnvelope } from '@ggui-ai/protocol';
19
+ import type { RenderSeedInput } from './types.js';
33
20
  import { ClientContractViolationError, type WireConfig } from '@ggui-ai/wire';
34
21
  import type { WebSocketMessage } from '@ggui-ai/protocol/transport/websocket';
35
22
  /**
36
23
  * Outbound send surface — the minimal shape wire-config calls on the
37
24
  * WS transport. Pre-B3b this read off `Pick<RendererWebSocketManager,
38
- * 'send'>`; post-B3b the WS lives inside `@ggui-ai/channel-client`,
25
+ * 'send'>`; post-B3b the WS lives inside `@ggui-ai/live-channel`,
39
26
  * but wire-config doesn't import the gadget directly — it consumes
40
27
  * the shape via this local type so tests can stub `{send: vi.fn()}`
41
- * without dragging in the channel-client types.
28
+ * without dragging in the live-channel types.
42
29
  *
43
30
  * Frames sent here are always `{type: 'action', payload: ...}` or
44
31
  * `{type: 'feedback', payload: ...}` envelopes — wire-config never
@@ -61,7 +48,7 @@ import type { ObservabilityEmitter } from './observability.js';
61
48
  * `subscribe()` → first `ack` → render stack items → mount
62
49
  * `mountProvisional` → it then calls `streamBus.subscribe('_ggui:
63
50
  * preview', …)`. Server-side replay frames for `_ggui:preview` (the
64
- * provisional A2UI preamble fired during the agent's `ggui_push`
51
+ * provisional A2UI preamble fired during the agent's `ggui_render`
65
52
  * BEFORE the user's browser navigated to the viewer) arrive over the
66
53
  * WS in the window between ack-resolve and the provisional mount —
67
54
  * they hit `emit()` before any listener has subscribed. Without a
@@ -98,15 +85,16 @@ export declare class StreamBus {
98
85
  emit(envelope: StreamEnvelope): void;
99
86
  }
100
87
  export interface BuildRootWireConfigOptions {
101
- readonly sessionId: string;
88
+ readonly renderId: string;
102
89
  readonly appId: string;
103
90
  /**
104
- * Stack snapshot reader. The config's default (unscoped) `dispatch`
105
- * reads the top of stack; callers route most dispatches through
106
- * `scopeWireConfig(root, item)` for per-item targeting, but the
107
- * default is kept for standalone-render paths.
91
+ * Read the currently-mounted {@link Render}. The config's `dispatch`
92
+ * resolves the active render's `actionSpec` through this thunk so
93
+ * the `tool` + outbound validator stay coherent across props_update
94
+ * patches (which replace the render reference) without rebuilding
95
+ * the WireConfig.
108
96
  */
109
- readonly getStack: () => readonly SessionStackEntry[];
97
+ readonly getCurrentRender: () => Render | RenderSeedInput | null;
110
98
  /** Handle to the renderer's WS manager; used for outbound `action` + `feedback` frames. */
111
99
  readonly manager: RendererSendSurface;
112
100
  /** Shared bus for inbound stream deliveries. */
@@ -154,61 +142,45 @@ export interface BuildRootWireConfigOptions {
154
142
  * (host inspector vs operator telemetry).
155
143
  */
156
144
  readonly onObserve?: ObservabilityEmitter;
157
- }
158
- /**
159
- * Paired root config + scope factory returned by `buildRootWireConfig`.
160
- *
161
- * {@link WireConfig} itself carries no `scope(item)` method — the
162
- * interface is the minimum contract generated code needs, and
163
- * per-item scoping is a RENDERER concern. Callers that need
164
- * per-item scoping — currently only the renderer's `bootProduction`
165
- * via `stackCtx.getScopedWireConfig` — use `buildScopedConfig(item)`
166
- * on the returned `RootWireConfigBundle`.
167
- *
168
- * The scope-factory is PAIRED with the root construction so the
169
- * `dispatchByItem` / `getStack` internals stay encapsulated — they
170
- * aren't exported from this module and never leak into the public
171
- * WireConfig shape.
172
- */
173
- export interface RootWireConfigBundle {
174
- readonly config: WireConfig;
175
145
  /**
176
- * Build a per-stack-item scoped `WireConfig`. Only `dispatch` is
177
- * overridden — every other seam (subscribe, app/session/auth) stays
178
- * shared via spread from the root.
146
+ * Optional outbound action sink. When provided, REPLACES the
147
+ * default WS-frame send (`manager.send({type:'action', payload})`).
148
+ *
149
+ * The WS live-channel exists for streamSpec subscriptions (inbound
150
+ * `ggui_emit` fanout + `props_update` + `push` + `data` + `feedback`
151
+ * + `drain_ack` + `channel_payload`). Outbound user actions belong
152
+ * on a different pipe — per MCP-Apps spec §401, the iframe relays
153
+ * `tools/call:ggui_runtime_submit_action` through the host (the
154
+ * `_meta.ui.visibility:['app']` channel), and the server's MCP
155
+ * handler appends to `pendingEventConsumer` so `ggui_consume`
156
+ * wakes the agent.
157
+ *
158
+ * The default `manager.send({type:'action'})` path writes to the
159
+ * session ledger only — it has no downstream consumer in OSS (no
160
+ * `wiredActionRouter`, no bridge to `pendingEventConsumer`).
161
+ * Production callers MUST supply this option to reach the agent.
162
+ *
163
+ * Called AFTER outbound validation passes. Receives the validated
164
+ * {@link ActionEnvelope}; caller extracts payload.action +
165
+ * payload.data + slice meta to route via the host's tools/call
166
+ * relay (see iframe-runtime's `routeDispatch`).
167
+ *
168
+ * Tests omit this opt to exercise the legacy WS-frame path.
179
169
  */
180
- readonly buildScopedConfig: (item: {
181
- readonly stackItemId?: string;
182
- readonly contractHash?: string;
183
- readonly actionSpec?: ActionSpec;
184
- }) => WireConfig;
170
+ readonly onDispatchEnvelope?: (envelope: ActionEnvelope) => void;
185
171
  }
186
172
  /**
187
- * Build the unscoped root `WireConfig` + its scope-factory closure.
173
+ * Build the per-render `WireConfig` for the iframe runtime.
188
174
  * Bootstraps the renderer's outbound emission + inbound subscription
189
- * seams.
175
+ * seams. Returns a `WireConfig` keyed by the bootstrap's `renderId`;
176
+ * the active render's `actionSpec` is resolved through the
177
+ * {@link BuildRootWireConfigOptions.getCurrentRender} thunk on every
178
+ * dispatch so props_update patches don't require rebuilding the
179
+ * config.
190
180
  *
191
- * Returns a {@link RootWireConfigBundle} rather than a bare
192
- * `WireConfig` — the scope factory is paired with the root so
193
- * `dispatchByItem` internals don't leak onto the public WireConfig
194
- * shape. `scopeWireConfig` (the exported standalone) is the low-level
195
- * primitive; `buildScopedConfig` is the pre-bound convenience.
196
- */
197
- export declare function buildRootWireConfig(opts: BuildRootWireConfigOptions): RootWireConfigBundle;
198
- /**
199
- * Produce a per-item scoped `WireConfig`. The only override is
200
- * `dispatch` — every other seam (subscribe) stays shared.
181
+ * Post-render-identity-collapse (2026-05-27): no per-item scope
182
+ * factory — each iframe mounts exactly one render. The earlier
183
+ * `RootWireConfigBundle` / `scopeWireConfig` / `buildScopedConfig`
184
+ * indirection collapsed to a single `WireConfig`.
201
185
  */
202
- export declare function scopeWireConfig(root: WireConfig, item: {
203
- stackItemId?: string;
204
- contractHash?: string;
205
- actionSpec?: ActionSpec;
206
- }, internals: {
207
- getStack: () => readonly SessionStackEntry[];
208
- dispatchByItem: (actionName: string, data: unknown, ctx: {
209
- stackItemId?: string;
210
- stackIndex: number;
211
- tool?: string;
212
- actionSpec?: ActionSpec;
213
- }) => void;
214
- }): WireConfig;
186
+ export declare function buildRootWireConfig(opts: BuildRootWireConfigOptions): WireConfig;
@@ -36,7 +36,7 @@ const RESERVED_CHANNEL_REPLAY_MAX = 256;
36
36
  * `subscribe()` → first `ack` → render stack items → mount
37
37
  * `mountProvisional` → it then calls `streamBus.subscribe('_ggui:
38
38
  * preview', …)`. Server-side replay frames for `_ggui:preview` (the
39
- * provisional A2UI preamble fired during the agent's `ggui_push`
39
+ * provisional A2UI preamble fired during the agent's `ggui_render`
40
40
  * BEFORE the user's browser navigated to the viewer) arrive over the
41
41
  * WS in the window between ack-resolve and the provisional mount —
42
42
  * they hit `emit()` before any listener has subscribed. Without a
@@ -123,15 +123,18 @@ export class StreamBus {
123
123
  }
124
124
  }
125
125
  /**
126
- * Build the unscoped root `WireConfig` + its scope-factory closure.
126
+ * Build the per-render `WireConfig` for the iframe runtime.
127
127
  * Bootstraps the renderer's outbound emission + inbound subscription
128
- * seams.
128
+ * seams. Returns a `WireConfig` keyed by the bootstrap's `renderId`;
129
+ * the active render's `actionSpec` is resolved through the
130
+ * {@link BuildRootWireConfigOptions.getCurrentRender} thunk on every
131
+ * dispatch so props_update patches don't require rebuilding the
132
+ * config.
129
133
  *
130
- * Returns a {@link RootWireConfigBundle} rather than a bare
131
- * `WireConfig` — the scope factory is paired with the root so
132
- * `dispatchByItem` internals don't leak onto the public WireConfig
133
- * shape. `scopeWireConfig` (the exported standalone) is the low-level
134
- * primitive; `buildScopedConfig` is the pre-bound convenience.
134
+ * Post-render-identity-collapse (2026-05-27): no per-item scope
135
+ * factory — each iframe mounts exactly one render. The earlier
136
+ * `RootWireConfigBundle` / `scopeWireConfig` / `buildScopedConfig`
137
+ * indirection collapsed to a single `WireConfig`.
135
138
  */
136
139
  export function buildRootWireConfig(opts) {
137
140
  const clientSeqBox = { current: 0 };
@@ -162,67 +165,63 @@ export function buildRootWireConfig(opts) {
162
165
  }
163
166
  emitProtocolError(fromClientContractViolation(err));
164
167
  }
165
- function dispatchByItem(actionName, data, ctx) {
166
- const envelope = buildActionEnvelope({
167
- sessionId: opts.sessionId,
168
- type: 'data:submit',
169
- payload: {
170
- action: actionName,
171
- data: data,
172
- ...(ctx.tool ? { tool: ctx.tool } : {}),
173
- },
174
- stackIndex: ctx.stackIndex,
175
- ...(ctx.stackItemId ? { stackItemId: ctx.stackItemId } : {}),
176
- clientSeq: nextSeq(),
177
- });
178
- const result = validateOutboundActionEnvelope(ctx.actionSpec, envelope);
179
- if (!result.valid) {
180
- surfaceViolation(new ClientContractViolationError('outbound-action', result.violations));
181
- return;
182
- }
183
- sendActionEnvelope(opts.manager, envelope);
184
- // Observability: a wired-tool dispatch is a `data:submit` whose
185
- // resolved action has a `tool` binding. Plain agent-routed actions
186
- // (no `tool` on the actionSpec) fire a `dispatch` activity row in
187
- // the console via the session-side ring buffer; only wired tools
188
- // get this observability event. The server separately emits the
189
- // operational telemetry variant on its TelemetrySink.
190
- if (ctx.tool !== undefined) {
191
- emitObserve({
192
- kind: 'wired-tool-invoked',
193
- toolName: ctx.tool,
194
- actionName,
195
- dispatchedAt: new Date().toISOString(),
196
- });
197
- }
198
- }
199
- const root = {
168
+ return {
200
169
  app: { appId: opts.appId, appName: opts.appId },
201
- session: { sessionId: opts.sessionId, isConnected: true },
170
+ render: { renderId: opts.renderId, isConnected: true },
202
171
  auth: { isAuthenticated: false },
203
172
  dispatch: (actionName, data) => {
204
- // Unscoped fallback — resolve from top of stack. Matches
205
- // `GguiSession.wireConfig.dispatch` (the non-scoped path).
206
- const stack = opts.getStack();
207
- const activeItem = stack[stack.length - 1];
208
- const activeIndex = stack.length > 0 ? stack.length - 1 : 0;
209
- // McpApps + system items never carry `actionSpec` (their wire is
210
- // owned by their own host) — narrow before reading.
211
- const activeActionSpec = activeItem !== undefined &&
212
- activeItem.type !== 'mcpApps' &&
213
- activeItem.type !== 'system'
214
- ? activeItem.actionSpec
173
+ // Resolve the active render's actionSpec on every dispatch.
174
+ // Per-render lifecycle: props_update patches replace the render
175
+ // reference; reading through the thunk keeps the tool binding
176
+ // + outbound validator coherent without rebuilding the config.
177
+ const currentRender = opts.getCurrentRender();
178
+ const activeActionSpec = currentRender !== null &&
179
+ currentRender.type !== 'mcpApps' &&
180
+ currentRender.type !== 'system'
181
+ ? currentRender.actionSpec
215
182
  : undefined;
216
183
  const entry = activeActionSpec?.[actionName];
217
184
  const tool = entry?.nextStep;
218
- dispatchByItem(actionName, data, {
219
- stackItemId: activeItem?.id,
220
- stackIndex: activeIndex,
221
- ...(tool !== undefined ? { tool } : {}),
222
- ...(activeActionSpec !== undefined
223
- ? { actionSpec: activeActionSpec }
224
- : {}),
185
+ const envelope = buildActionEnvelope({
186
+ renderId: opts.renderId,
187
+ type: 'data:submit',
188
+ payload: {
189
+ action: actionName,
190
+ data: data,
191
+ ...(tool ? { tool } : {}),
192
+ },
193
+ clientSeq: nextSeq(),
225
194
  });
195
+ const result = validateOutboundActionEnvelope(activeActionSpec, envelope);
196
+ if (!result.valid) {
197
+ surfaceViolation(new ClientContractViolationError('outbound-action', result.violations));
198
+ return;
199
+ }
200
+ if (opts.onDispatchEnvelope !== undefined) {
201
+ // Spec-canonical path — the iframe-runtime's LIVE-mode boot
202
+ // wires this to `routeDispatch`, which postMessages
203
+ // `tools/call:ggui_runtime_submit_action` through the MCP-Apps
204
+ // host relay. The default WS-frame send below is retained for
205
+ // tests + legacy callers that don't relay tools/call.
206
+ opts.onDispatchEnvelope(envelope);
207
+ }
208
+ else {
209
+ sendActionEnvelope(opts.manager, envelope);
210
+ }
211
+ // Observability: a wired-tool dispatch is a `data:submit` whose
212
+ // resolved action has a `tool` binding. Plain agent-routed actions
213
+ // (no `tool` on the actionSpec) fire a `dispatch` activity row in
214
+ // the console via the session-side ring buffer; only wired tools
215
+ // get this observability event. The server separately emits the
216
+ // operational telemetry variant on its TelemetrySink.
217
+ if (tool !== undefined) {
218
+ emitObserve({
219
+ kind: 'wired-tool-invoked',
220
+ toolName: tool,
221
+ actionName,
222
+ dispatchedAt: new Date().toISOString(),
223
+ });
224
+ }
226
225
  },
227
226
  subscribe: (channelName, handler) => {
228
227
  return opts.streamBus.subscribe(channelName, (env) => {
@@ -244,37 +243,6 @@ export function buildRootWireConfig(opts) {
244
243
  // `actionSpec[*].nextStep` (event metadata) and
245
244
  // `streamSpec[*].source.tool` (channel data source).
246
245
  };
247
- // `WireConfig` carries no `scope(item)` method.
248
- // Callers that want a per-item config use `buildScopedConfig(item)`
249
- // on the returned bundle, which bakes in the `dispatchByItem` +
250
- // `getStack` internals so they never leak onto the public
251
- // WireConfig shape.
252
- const buildScopedConfig = (item) => scopeWireConfig(root, item, {
253
- getStack: opts.getStack,
254
- dispatchByItem,
255
- });
256
- return { config: root, buildScopedConfig };
257
- }
258
- /**
259
- * Produce a per-item scoped `WireConfig`. The only override is
260
- * `dispatch` — every other seam (subscribe) stays shared.
261
- */
262
- export function scopeWireConfig(root, item, internals) {
263
- return {
264
- ...root,
265
- dispatch: (actionName, data) => {
266
- const entry = item.actionSpec?.[actionName];
267
- const tool = entry?.nextStep;
268
- const stack = internals.getStack();
269
- const idx = stack.findIndex((s) => s.id === item.stackItemId);
270
- internals.dispatchByItem(actionName, data, {
271
- ...(item.stackItemId !== undefined ? { stackItemId: item.stackItemId } : {}),
272
- stackIndex: idx >= 0 ? idx : 0,
273
- ...(tool !== undefined ? { tool } : {}),
274
- ...(item.actionSpec !== undefined ? { actionSpec: item.actionSpec } : {}),
275
- });
276
- },
277
- };
278
246
  }
279
247
  // =============================================================================
280
248
  // Outbound envelope emission
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@ggui-ai/iframe-runtime",
3
- "version": "0.1.0-rc.3",
4
- "description": "The iframe-local ggui runtime: a self-contained ESM bundle that boots inside an MCP Apps host iframe, runs the protocol-version handshake, opens the live-channel WebSocket, and renders the session stack. Bundles React, ReactDOM, and the ggui design system so generated component code has a single seam to read from.",
3
+ "version": "0.2.0-alpha.3",
4
+ "description": "The iframe-local ggui runtime: a self-contained ESM bundle that boots inside an MCP Apps host iframe, runs the protocol-version handshake, opens the live-channel WebSocket, and mounts the rendered UI. Bundles React, ReactDOM, and the ggui design system so generated component code has a single seam to read from.",
5
5
  "keywords": [
6
6
  "ggui",
7
7
  "mcp",
@@ -34,15 +34,16 @@
34
34
  }
35
35
  },
36
36
  "dependencies": {
37
+ "@modelcontextprotocol/ext-apps": "^1.7.2",
37
38
  "qrcode-generator": "^2.0.4",
38
39
  "react": "^19.0.0",
39
40
  "react-dom": "^19.0.0",
40
- "@ggui-ai/channel-client": "0.1.0-rc.3",
41
- "@ggui-ai/gadgets": "0.1.0-rc.3",
42
- "@ggui-ai/design": "0.1.0-rc.3",
43
- "@ggui-ai/preview-a2ui": "0.1.0-rc.3",
44
- "@ggui-ai/protocol": "0.1.0-rc.3",
45
- "@ggui-ai/wire": "0.1.0-rc.3"
41
+ "@ggui-ai/live-channel": "0.2.0-alpha.3",
42
+ "@ggui-ai/gadgets": "0.2.0-alpha.3",
43
+ "@ggui-ai/design": "0.2.0-alpha.3",
44
+ "@ggui-ai/preview-a2ui": "0.2.0-alpha.3",
45
+ "@ggui-ai/protocol": "0.2.0-alpha.3",
46
+ "@ggui-ai/wire": "0.2.0-alpha.3"
46
47
  },
47
48
  "devDependencies": {
48
49
  "@testing-library/react": "^16.0.0",
@@ -1,72 +0,0 @@
1
- import type { BootstrapParseResult } from './types.js';
2
- /**
3
- * Shared validator core. Accepts an unwrapped bootstrap object (NOT
4
- * the full `_meta.ggui.bootstrap` envelope — the caller has already
5
- * stripped envelopes down to this layer).
6
- *
7
- * Validates the three-mode discriminator + the optional fields per
8
- * the {@link GguiBootstrapMeta} contract. Returns a discriminated
9
- * {@link BootstrapParseResult} the caller surfaces verbatim.
10
- *
11
- * Invariants enforced here (see module docblock for the wire contract):
12
- * - `runtimeUrl` MUST be a non-empty string. All three modes need
13
- * it because the shell dynamic-script-loads the runtime bundle
14
- * from this URL.
15
- * - `sessionId` + `appId` MUST be non-empty strings.
16
- * - At least one of `{wsUrl-with-token, codeUrl, kind}` MUST be
17
- * present as a non-empty string. Half-live (wsUrl without token)
18
- * is MALFORMED.
19
- * - `expiresAt`, when present + relevant (live mode), MUST parse
20
- * to a future timestamp.
21
- */
22
- export declare function validateBootstrapMeta(raw: unknown): BootstrapParseResult;
23
- /**
24
- * Parse a bootstrap from a `ui/initialize` postMessage response. The
25
- * argument is the JSON-RPC `result` field — typically
26
- * `{ toolOutput: { _meta: { ggui: { bootstrap: {...} } }, structuredContent: ... } }`.
27
- *
28
- * Used by the runtime's legacy `bootProduction` path (live-mode WS
29
- * subscription). Spec-canonical for first-party `<McpAppIframe>`
30
- * Reading-B hosts (Studio, Portal, console).
31
- */
32
- export declare function parseBootstrapFromUiInitialize(uiInitializeResult: unknown): BootstrapParseResult;
33
- /**
34
- * Back-compat alias for {@link parseBootstrapFromUiInitialize}.
35
- *
36
- * `parseBootstrap` was the original name when the iframe-runtime
37
- * accepted only the ui/initialize envelope. The parser was later
38
- * generalized to three envelope shapes (ui/initialize,
39
- * `__GGUI_BOOTSTRAP__` global, postMessage tool-result), each with its
40
- * own thin extractor; the shared {@link validateBootstrapMeta} core
41
- * does the per-envelope shape validation. The original function name
42
- * is kept exported so downstream consumers (`@ggui-ai/react`, runtime
43
- * call sites) don't need a sweep — the name still resolves to the
44
- * same behavior.
45
- */
46
- export declare const parseBootstrap: typeof parseBootstrapFromUiInitialize;
47
- /**
48
- * Parse a bootstrap from `globalThis.__GGUI_BOOTSTRAP__` — the
49
- * synchronous self-contained shell delivery channel. Returns
50
- * `MALFORMED_BOOTSTRAP` when the global is absent OR not an object;
51
- * the caller falls through to a postMessage path on either signal.
52
- *
53
- * Used by the runtime's autostart resolver as the highest-priority
54
- * boot source: per-session shells (`/r/<shortCode>`,
55
- * `ui://ggui/session/<sessionId>`) populate this synchronously
56
- * BEFORE the runtime bundle's `<script type="module">` evaluates,
57
- * so the runtime can mount without any postMessage round-trip.
58
- */
59
- export declare function parseBootstrapFromGlobal(): BootstrapParseResult;
60
- /**
61
- * Parse a bootstrap from a `ui/notifications/tool-result` postMessage
62
- * params payload. Looks at BOTH the spec-canonical location
63
- * (`params._meta.ggui.bootstrap`) and the Reading-B `<McpAppIframe>`
64
- * convention (`params.toolOutput._meta.ggui.bootstrap`) — Claude
65
- * Desktop / claude.ai Connector use the first; first-party hosts the
66
- * second.
67
- *
68
- * Used by the runtime's autostart resolver as the second / third
69
- * boot source (drained from the pre-runtime-load buffer or caught by
70
- * the live message listener).
71
- */
72
- export declare function parseBootstrapFromToolResult(params: unknown): BootstrapParseResult;