@ggui-ai/iframe-runtime 0.1.0-rc.3 → 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.
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
@@ -7,7 +7,7 @@
7
7
  * protocol violation or transport error).
8
8
  * - `ObservabilityEvent` fires on HAPPY PATHS + failures alike
9
9
  * (telemetry + inspection signal the host can display in a
10
- * SessionInspector-style view).
10
+ * RenderInspector-style view).
11
11
  *
12
12
  * The host wrapper passes events through to `onObserve` opaquely.
13
13
  *
@@ -30,7 +30,7 @@
30
30
  export type ObservabilityEvent = WiredToolInvokedEvent | ContractErrorEmittedEvent | SchemaVersionMismatchEvent | SubscribeFailedEvent | AuthRequiredEvent | ChannelTransportPickedEvent | ChannelTransportFallbackEvent | ChannelTransportResubscribedEvent | UnknownObservabilityEvent;
31
31
  /**
32
32
  * Fired when a wired action successfully dispatched to a registered
33
- * MCP tool. Hosts surface this as a dispatch row in SessionInspector.
33
+ * MCP tool. Hosts surface this as a dispatch row in RenderInspector.
34
34
  * Emitted by the server-side router (session-channel.ts) via
35
35
  * postMessage relay through the renderer.
36
36
  *
@@ -137,14 +137,14 @@ export interface AuthRequiredEvent {
137
137
  /**
138
138
  * Fired by the channel-transport router when it picks a transport
139
139
  * for a `streamSpec[ch].source.tool` channel. Hosts can inspect the
140
- * WS-vs-poll decision per channel in the SessionInspector activity
140
+ * WS-vs-poll decision per channel in the RenderInspector activity
141
141
  * feed.
142
142
  *
143
143
  * @public
144
144
  */
145
145
  export interface ChannelTransportPickedEvent {
146
146
  readonly kind: 'channel-transport-picked';
147
- readonly stackItemId: string;
147
+ readonly renderId: string;
148
148
  readonly channelName: string;
149
149
  readonly transport: 'ws' | 'poll';
150
150
  }
@@ -159,7 +159,7 @@ export interface ChannelTransportPickedEvent {
159
159
  */
160
160
  export interface ChannelTransportFallbackEvent {
161
161
  readonly kind: 'channel-transport-fallback';
162
- readonly stackItemId: string;
162
+ readonly renderId: string;
163
163
  readonly channelName: string;
164
164
  readonly reason: 'ws-disconnect' | 'channel-not-local';
165
165
  }
@@ -171,7 +171,7 @@ export interface ChannelTransportFallbackEvent {
171
171
  */
172
172
  export interface ChannelTransportResubscribedEvent {
173
173
  readonly kind: 'channel-transport-resubscribed';
174
- readonly stackItemId: string;
174
+ readonly renderId: string;
175
175
  readonly channelName: string;
176
176
  }
177
177
  /**
@@ -33,7 +33,7 @@ import { ClientContractViolationError } from '@ggui-ai/wire';
33
33
  * Consolidates three sources:
34
34
  *
35
35
  * 1. **Parse-time failures** — every member of
36
- * `BootstrapParseFailureReason` (`MISSING_TOOL_OUTPUT` /
36
+ * `McpAppAiGguiMetaParseFailureReason` (`MISSING_TOOL_OUTPUT` /
37
37
  * `MISSING_META_GGUI_BOOTSTRAP` / `MALFORMED_BOOTSTRAP` /
38
38
  * `EXPIRED_BOOTSTRAP`). `MISSING_META_GGUI_BOOTSTRAP` and
39
39
  * `BOOTSTRAP_META_MISSING` are synonyms — the latter is the
@@ -43,15 +43,15 @@ import { ClientContractViolationError } from '@ggui-ai/wire';
43
43
  * / `WS_HANDSHAKE_FAILED`. Matches `RendererBootFailureReason`.
44
44
  *
45
45
  * 3. **Transport-observable failures** — `BUNDLE_FETCH_FAILED` /
46
- * `CSP_VIOLATION` / `SESSION_NOT_FOUND` / `AUTH_REJECTED`. These
46
+ * `CSP_VIOLATION` / `RENDER_NOT_FOUND` / `AUTH_REJECTED`. These
47
47
  * fire before the renderer parse path runs, so they're authored
48
- * here rather than inside `BootstrapParseFailureReason`.
48
+ * here rather than inside `McpAppAiGguiMetaParseFailureReason`.
49
49
  *
50
50
  * `(string & {})` tail keeps the union extensibly-closed — new wire
51
51
  * codes can land without a client bump, while IDE autocomplete
52
52
  * surfaces the canonical set.
53
53
  */
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' | 'SESSION_NOT_FOUND' | 'AUTH_REJECTED' | (string & {});
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' | 'RENDER_NOT_FOUND' | 'AUTH_REJECTED' | (string & {});
55
55
  /**
56
56
  * Every error the renderer surfaces outward flows through this union.
57
57
  *
@@ -61,7 +61,7 @@ export type BootstrapFailureReason = 'MISSING_TOOL_OUTPUT' | 'MISSING_META_GGUI_
61
61
  * Includes `retryable` so hosts can decide to reconnect
62
62
  * vs. give up.
63
63
  * - `auth` — session / token rejection pre-handshake.
64
- * `SESSION_NOT_FOUND` / `TOKEN_EXPIRED` /
64
+ * `RENDER_NOT_FOUND` / `TOKEN_EXPIRED` /
65
65
  * `AUTH_REJECTED` — all terminal; hosts typically
66
66
  * escalate to a login flow.
67
67
  * - `protocol` — envelope / session-mismatch failures post-handshake.
@@ -87,7 +87,7 @@ export type ProtocolError = {
87
87
  readonly message?: string;
88
88
  } | {
89
89
  readonly kind: 'auth';
90
- readonly code: 'SESSION_NOT_FOUND' | 'TOKEN_EXPIRED' | 'AUTH_REJECTED';
90
+ readonly code: 'RENDER_NOT_FOUND' | 'TOKEN_EXPIRED' | 'AUTH_REJECTED';
91
91
  readonly message?: string;
92
92
  } | {
93
93
  readonly kind: 'protocol';
@@ -196,4 +196,4 @@ export declare function fromTransportFailure(code: 'DISCONNECTED' | 'TIMEOUT', r
196
196
  * Build a `ProtocolError` of kind 'auth' — session / token rejection
197
197
  * pre-handshake.
198
198
  */
199
- export declare function fromAuthFailure(code: 'SESSION_NOT_FOUND' | 'TOKEN_EXPIRED' | 'AUTH_REJECTED', message?: string): ProtocolError;
199
+ export declare function fromAuthFailure(code: 'RENDER_NOT_FOUND' | 'TOKEN_EXPIRED' | 'AUTH_REJECTED', message?: string): ProtocolError;
@@ -25,7 +25,7 @@
25
25
  * loading fallback until A2UI's `root` fragment arrives; then
26
26
  * the adjacency tree paints.
27
27
  * - `suspend()` hides the surface (used when the authoritative
28
- * render takes over — the stack-item dispatcher in Commit 4
28
+ * render takes over — the render dispatcher in Commit 4
29
29
  * toggles this on ReactComponentRenderer mount).
30
30
  * - `resume()` re-shows. Envelopes keep accumulating in state
31
31
  * while suspended, matching host-SDK behavior.
@@ -25,7 +25,7 @@
25
25
  * loading fallback until A2UI's `root` fragment arrives; then
26
26
  * the adjacency tree paints.
27
27
  * - `suspend()` hides the surface (used when the authoritative
28
- * render takes over — the stack-item dispatcher in Commit 4
28
+ * render takes over — the render dispatcher in Commit 4
29
29
  * toggles this on ReactComponentRenderer mount).
30
30
  * - `resume()` re-shows. Envelopes keep accumulating in state
31
31
  * while suspended, matching host-SDK behavior.
@@ -10,12 +10,12 @@
10
10
  *
11
11
  * Lifecycle seams:
12
12
  *
13
- * - `mount(container, stackItem)` — replace the container's
13
+ * - `mount(container, render)` — replace the container's
14
14
  * children with a React root rendering the compiled component.
15
15
  * Returns a `ReactRootMount` handle; callers use `update()` to
16
16
  * swap props without re-evaluating the module, `unmount()` to
17
17
  * tear down.
18
- * - `update(stackItem)` — swap props (and/or componentCode). When
18
+ * - `update(render)` — swap props (and/or componentCode). When
19
19
  * componentCode changes, re-evaluates; when only props change,
20
20
  * re-renders the existing component tree with new props.
21
21
  * - `unmount()` — `root.unmount()` + detach observability.
@@ -23,8 +23,8 @@
23
23
  * Error handling — ErrorBoundary with auto-retry (AUTO_RETRY_LIMIT +
24
24
  * AUTO_RETRY_DELAY) matches the `ReactComponentRenderer` contract.
25
25
  * On terminal error, the callback `onRequestRepair` fires; the
26
- * stack-item dispatcher wires this to the `ggui:request-repair`
27
- * live-channel envelope `StackItemRenderer` dispatches.
26
+ * render dispatcher (`render-item.ts`) wires this to the
27
+ * `ggui:request-repair` live-channel envelope it dispatches.
28
28
  *
29
29
  * No JSX in this file — the mount-root code uses
30
30
  * `React.createElement` + `React.Fragment` directly so the renderer's
@@ -33,13 +33,12 @@
33
33
  import React, { type ReactNode } from 'react';
34
34
  export interface ReactRootMountOptions {
35
35
  /**
36
- * Stack item whose componentCode + props drive the mount. Only
36
+ * Render whose componentCode + props drive the mount. Only
37
37
  * `componentCode` + `props` are read here; the caller is
38
38
  * responsible for wrapping this with a `<GguiWireProvider>` at the
39
- * higher-level dispatcher (see `stack-item-renderer.ts` in Commit
40
- * 4).
39
+ * higher-level dispatcher (see `render-item.ts`).
41
40
  */
42
- readonly stackItem: {
41
+ readonly render: {
43
42
  readonly id?: string;
44
43
  readonly componentCode: string;
45
44
  readonly props?: Record<string, unknown>;
@@ -56,9 +55,9 @@ export interface ReactRootMountOptions {
56
55
  readonly onRequestRepair?: (error: Error) => void;
57
56
  /**
58
57
  * Children injected BETWEEN the mount DOM (scope + CSS) and the
59
- * evaluated component element. The caller's stack-item renderer
58
+ * evaluated component element. The caller's render dispatcher
60
59
  * uses this to wrap the eval'd component in `<GguiWireProvider>`
61
- * so wire hooks resolve per-item contracts.
60
+ * so wire hooks resolve per-render contracts.
62
61
  *
63
62
  * When `undefined`, the evaluated component renders directly.
64
63
  */
@@ -87,7 +86,7 @@ export interface ReactRootMount {
87
86
  readonly currentCode: string | null;
88
87
  }
89
88
  /**
90
- * Mount a React root inside `container` and render `stackItem`'s
89
+ * Mount a React root inside `container` and render `render`'s
91
90
  * compiled componentCode. The container is fully owned after this
92
91
  * call (existing children are replaced).
93
92
  */
@@ -10,12 +10,12 @@
10
10
  *
11
11
  * Lifecycle seams:
12
12
  *
13
- * - `mount(container, stackItem)` — replace the container's
13
+ * - `mount(container, render)` — replace the container's
14
14
  * children with a React root rendering the compiled component.
15
15
  * Returns a `ReactRootMount` handle; callers use `update()` to
16
16
  * swap props without re-evaluating the module, `unmount()` to
17
17
  * tear down.
18
- * - `update(stackItem)` — swap props (and/or componentCode). When
18
+ * - `update(render)` — swap props (and/or componentCode). When
19
19
  * componentCode changes, re-evaluates; when only props change,
20
20
  * re-renders the existing component tree with new props.
21
21
  * - `unmount()` — `root.unmount()` + detach observability.
@@ -23,8 +23,8 @@
23
23
  * Error handling — ErrorBoundary with auto-retry (AUTO_RETRY_LIMIT +
24
24
  * AUTO_RETRY_DELAY) matches the `ReactComponentRenderer` contract.
25
25
  * On terminal error, the callback `onRequestRepair` fires; the
26
- * stack-item dispatcher wires this to the `ggui:request-repair`
27
- * live-channel envelope `StackItemRenderer` dispatches.
26
+ * render dispatcher (`render-item.ts`) wires this to the
27
+ * `ggui:request-repair` live-channel envelope it dispatches.
28
28
  *
29
29
  * No JSX in this file — the mount-root code uses
30
30
  * `React.createElement` + `React.Fragment` directly so the renderer's
@@ -183,7 +183,7 @@ class RcrErrorBoundary extends Component {
183
183
  }
184
184
  }
185
185
  /**
186
- * Mount a React root inside `container` and render `stackItem`'s
186
+ * Mount a React root inside `container` and render `render`'s
187
187
  * compiled componentCode. The container is fully owned after this
188
188
  * call (existing children are replaced).
189
189
  */
@@ -257,7 +257,7 @@ export async function mountReactRoot(container, opts) {
257
257
  }
258
258
  const componentElement = currentComponent === null
259
259
  ? null
260
- : createElement(currentComponent, opts.stackItem.props ?? {});
260
+ : createElement(currentComponent, opts.render.props ?? {});
261
261
  const wrapped = opts.renderWrapper && componentElement !== null
262
262
  ? opts.renderWrapper(componentElement)
263
263
  : componentElement;
@@ -271,7 +271,7 @@ export async function mountReactRoot(container, opts) {
271
271
  }, createElement(Fragment, null, wrapped))));
272
272
  }
273
273
  async function initialEvaluate() {
274
- const code = currentOpts.stackItem.componentCode ?? '';
274
+ const code = currentOpts.render.componentCode ?? '';
275
275
  if (code.trim().length === 0) {
276
276
  currentComponent = null;
277
277
  currentCode = null;
@@ -305,7 +305,7 @@ export async function mountReactRoot(container, opts) {
305
305
  async update(next) {
306
306
  const prevCode = currentCode;
307
307
  currentOpts = next;
308
- const nextCode = next.stackItem.componentCode ?? '';
308
+ const nextCode = next.render.componentCode ?? '';
309
309
  // Props-only change — skip module eval. Re-render with new props.
310
310
  if (nextCode === prevCode && currentComponent !== null) {
311
311
  renderTree(currentOpts);
@@ -1,15 +1,15 @@
1
1
  import type { AckPayload, ContractErrorPayload } from '@ggui-ai/protocol';
2
2
  import type { ConnectionStatus } from '@ggui-ai/protocol/transport/websocket';
3
- import type { GguiBootstrapMeta } from '@ggui-ai/protocol/integrations/mcp-apps';
4
- import type { ChannelRegistry, WsTransportHandle } from '@ggui-ai/channel-client';
3
+ import type { McpAppAiGguiRenderMeta } from '@ggui-ai/protocol/integrations/mcp-apps';
4
+ import type { ChannelRegistry, RegistryPollingOptions, WsTransportHandle } from '@ggui-ai/live-channel';
5
5
  import { type ProtocolErrorEmitter } from './protocol-error.js';
6
6
  import type { ObservabilityEmitter } from './observability.js';
7
7
  /**
8
8
  * Result of a successful handshake — the live typed transport handle
9
9
  * plus the resolved `AckPayload` from the first ack frame. The caller
10
- * attaches `handle` to the triad's outbound send surface (via the
11
- * BufferedManagerShim flush) and applies `ack.stack` to the stack
12
- * model.
10
+ * attaches `handle` to the renderer's outbound send surface (via the
11
+ * BufferedManagerShim flush) and applies `ack.render` to the mounted
12
+ * render slot.
13
13
  */
14
14
  export interface RegistrySubscribeHandle {
15
15
  readonly handle: WsTransportHandle;
@@ -20,7 +20,14 @@ export interface RegistrySubscribeHandle {
20
20
  * concrete implementations; tests pass mocks via the `connectFn` seam.
21
21
  */
22
22
  export interface ConnectViaRegistryOptions {
23
- readonly bootstrap: GguiBootstrapMeta;
23
+ /**
24
+ * Render slice (`McpAppAiGguiRenderMeta`) — the live-channel
25
+ * credentials this subscribe path actually needs: `renderId`,
26
+ * `appId`, `wsUrl`, `wsToken`. The rest of the slice (codeUrl /
27
+ * propsJson / contextSlots / etc.) is load-bearing only for the
28
+ * renderer / mount layer.
29
+ */
30
+ readonly meta: McpAppAiGguiRenderMeta;
24
31
  readonly registry: ChannelRegistry;
25
32
  /**
26
33
  * Fires on every transport-status transition mapped onto the
@@ -61,17 +68,28 @@ export interface ConnectViaRegistryOptions {
61
68
  * Optional post-handshake ack callback. Fires for every ack frame
62
69
  * AFTER the initial handshake settled — i.e. on every reconnect's
63
70
  * subscribe ack. The runtime uses this to reapply the server's
64
- * authoritative `stack` snapshot so a push or update that landed
71
+ * authoritative `render` snapshot so a render or update that landed
65
72
  * during a WS dropout window flows back into the iframe on
66
73
  * reconnect.
67
74
  *
68
- * Idempotent at the runtime layer: `stackModel.setAll(ack.stack)`
69
- * is a snapshot replace. Channels re-subscribed by
75
+ * Idempotent at the runtime layer: `applyRender(ack.render)` patches
76
+ * in place. Channels re-subscribed by
70
77
  * `ChannelTransportRouter.onWsStatusChange('connected')` upstream
71
78
  * of this callback, so the per-channel transport state is already
72
79
  * coherent by the time the resubscribe-ack arrives.
73
80
  */
74
81
  readonly onResubscribeAck?: (ack: AckPayload) => void;
82
+ /**
83
+ * Registry-level polling descriptor (R6). When supplied, the live-
84
+ * channel's `FailoverHandle` uses this as its polling fallback when
85
+ * `WSTransport` reaches `'failed'`. Absent → no polling fallback
86
+ * (WS-only mode; once WS is exhausted, handlers stay inert).
87
+ *
88
+ * Composed at the runtime layer from `meta.pollingUrl` +
89
+ * `meta.lastSequence` (cursor seed) — the live-channel itself is
90
+ * URL- and shape-agnostic.
91
+ */
92
+ readonly polling?: RegistryPollingOptions;
75
93
  }
76
94
  /**
77
95
  * Open the WS via the registry, send subscribe (composed by the
@@ -1,21 +1,21 @@
1
1
  /**
2
2
  * Registry-driven subscribe orchestration — owns the renderer's
3
- * "I have a parsed bootstrap, now drive the live-channel handshake to
4
- * first ack" flow on top of `ChannelRegistry.bind()`.
3
+ * "I have a parsed render-meta, now drive the live-channel handshake
4
+ * to first ack" flow on top of `ChannelRegistry.bind()`.
5
5
  *
6
6
  * Replaces the pre-B3b `subscribe()` helper + the `RendererWebSocketManager`
7
7
  * lifecycle class. The two retired modules paired a hand-rolled WS
8
8
  * manager with a separate handshake helper; B3b unifies both behind
9
- * the `@ggui-ai/channel-client` library so every WS frame type (push,
9
+ * the `@ggui-ai/live-channel` library so every WS frame type (push,
10
10
  * data, props_update, drain_ack, channel_payload, channel_error,
11
11
  * system, feedback) flows through one registry-owned transport.
12
12
  *
13
13
  * Composition:
14
14
  *
15
15
  * 1. Caller-supplied `ChannelRegistry` (populated by `runtime.ts`'s
16
- * triad-wiring `setup()` with every handler the iframe routes).
17
- * 2. Compose the WS URL with the bootstrap token threaded as a
18
- * `?bootstrap=<encoded>` query string (mirrors the pre-B3b
16
+ * renderer-hooks `setup()` with every handler the iframe routes).
17
+ * 2. Compose the WS URL with the WS auth token threaded as a
18
+ * `?wsToken=<encoded>` query string (mirrors the pre-B3b
19
19
  * `composeWsUrl` shape so server-side upgrade-path auth keeps
20
20
  * working).
21
21
  * 3. Register `ack` + `error` handlers on the registry BEFORE bind
@@ -23,7 +23,9 @@
23
23
  * handshake-resolver closure.
24
24
  * 4. Call `registry.bind({bootstrap, onStatusChange})` and capture
25
25
  * the returned typed `WsTransportHandle` (narrow on
26
- * `handle.kind === 'ws'`).
26
+ * `handle.kind === 'ws'`). The live-channel's `bootstrap`
27
+ * argument is its own internal name; the field on the wire is
28
+ * `wsToken`.
27
29
  *
28
30
  * The handshake resolves on the first `ack` (with the symmetric
29
31
  * `CLIENT_SUPPORTED_VERSIONS` check) or rejects on:
@@ -43,15 +45,15 @@ import { CLIENT_SUPPORTED_VERSIONS, UPGRADE_REQUIRED } from '@ggui-ai/protocol/v
43
45
  import { UpgradeRequiredError } from '@ggui-ai/protocol/errors/version-mismatch';
44
46
  import { fromAuthFailure, fromTransportFailure, fromUpgradeRequired, } from './protocol-error.js';
45
47
  /**
46
- * Compose the WebSocket URL with the bootstrap token threaded as a
48
+ * Compose the WebSocket URL with the WS auth token threaded as a
47
49
  * query parameter. Servers that authenticate the upgrade path read
48
50
  * the token from here; the same value also rides inside the subscribe
49
51
  * payload so server-side handlers that consume credentials on the
50
52
  * subscribe frame see it too.
51
53
  */
52
- function composeWsUrl(base, bootstrap) {
54
+ function composeWsUrl(base, wsToken) {
53
55
  const sep = base.indexOf('?') >= 0 ? '&' : '?';
54
- return `${base}${sep}bootstrap=${encodeURIComponent(bootstrap)}`;
56
+ return `${base}${sep}wsToken=${encodeURIComponent(wsToken)}`;
55
57
  }
56
58
  /**
57
59
  * Lift a server-emitted `{type:'error'}` frame into the appropriate
@@ -92,12 +94,12 @@ function normalizeObservedVersion(value) {
92
94
  }
93
95
  /**
94
96
  * Map a pre-ack `error` frame onto a non-UPGRADE_REQUIRED ProtocolError.
95
- * `SESSION_NOT_FOUND` / `AUTH_REJECTED` → `'auth'`; every other code
97
+ * `RENDER_NOT_FOUND` / `AUTH_REJECTED` → `'auth'`; every other code
96
98
  * → `'protocol'` with the extensibly-closed tail carrying the wire code
97
99
  * verbatim.
98
100
  */
99
101
  function classifyPreAckError(payload) {
100
- if (payload.code === 'SESSION_NOT_FOUND' || payload.code === 'AUTH_REJECTED') {
102
+ if (payload.code === 'RENDER_NOT_FOUND' || payload.code === 'AUTH_REJECTED') {
101
103
  return fromAuthFailure(payload.code, payload.message);
102
104
  }
103
105
  if (payload.code === 'TOKEN_EXPIRED') {
@@ -130,14 +132,14 @@ export function connectViaRegistry(opts) {
130
132
  let seenOpen = false;
131
133
  const emitProtocolError = opts.onProtocolError ?? (() => { });
132
134
  const emitObserve = opts.onObserve ?? (() => { });
133
- // Defense-in-depth — live mode requires both wsUrl + token. The
135
+ // Defense-in-depth — live mode requires both wsUrl + wsToken. The
134
136
  // caller validates this before invoking; reject early with a
135
137
  // typed transport failure if either is missing.
136
- const wsUrl = opts.bootstrap.wsUrl;
137
- const token = opts.bootstrap.token;
138
+ const wsUrl = opts.meta.wsUrl;
139
+ const wsToken = opts.meta.wsToken;
138
140
  if (typeof wsUrl !== 'string' || wsUrl.length === 0 ||
139
- typeof token !== 'string' || token.length === 0) {
140
- const err = new Error('connectViaRegistry: bootstrap missing wsUrl/token — live-mode required');
141
+ typeof wsToken !== 'string' || wsToken.length === 0) {
142
+ const err = new Error('connectViaRegistry: render-meta missing wsUrl/wsToken — live-mode required');
141
143
  emitProtocolError(fromTransportFailure('DISCONNECTED', false, err.message));
142
144
  reject(err);
143
145
  return;
@@ -265,29 +267,32 @@ export function connectViaRegistry(opts) {
265
267
  reject(new Error('connectViaRegistry: transport went to failed state before ack'));
266
268
  }
267
269
  };
268
- const composedUrl = composeWsUrl(wsUrl, token);
269
- // `bind()` selects the transport (WS for wsUrl+token bootstraps;
270
- // the registry's selection logic narrows on bootstrap shape) and
271
- // starts it. We override `bootstrap.wsUrl` with the composed URL
272
- // so the registry's WS transport gets the token-threaded variant.
270
+ const composedUrl = composeWsUrl(wsUrl, wsToken);
271
+ // `bind()` selects the transport (WS for wsUrl+wsToken sessions;
272
+ // the live-channel narrows on its `bootstrap` shape) and starts
273
+ // it. We override `session.wsUrl` with the composed URL so the
274
+ // registry's WS transport gets the wsToken-threaded variant. The
275
+ // live-channel's `bootstrap` arg is its internal name; the field
276
+ // on the wire is `wsToken`.
273
277
  void opts.registry
274
278
  .bind({
275
279
  bootstrap: {
276
280
  wsUrl: composedUrl,
277
- token,
278
- sessionId: opts.bootstrap.sessionId,
279
- appId: opts.bootstrap.appId,
281
+ wsToken,
282
+ renderId: opts.meta.renderId,
283
+ appId: opts.meta.appId,
280
284
  },
281
285
  onStatusChange: mappedStatusCallback,
286
+ ...(opts.polling !== undefined ? { polling: opts.polling } : {}),
282
287
  })
283
288
  .then((bound) => {
284
- // bound is `AnyTransportHandle`. Composed URL + token means
289
+ // bound is `AnyTransportHandle`. Composed URL + wsToken means
285
290
  // the registry selected WSTransport — `kind === 'ws'` is the
286
291
  // structural invariant. Narrow defensively.
287
292
  if (bound.kind !== 'ws') {
288
293
  if (!settled) {
289
294
  settled = true;
290
- reject(new Error("connectViaRegistry: registry returned non-ws transport despite wsUrl+token bootstrap"));
295
+ reject(new Error("connectViaRegistry: registry returned non-ws transport despite wsUrl+wsToken render-meta"));
291
296
  }
292
297
  return;
293
298
  }
@@ -0,0 +1,83 @@
1
+ import type { ReactNode } from 'react';
2
+ import type { Render } from '@ggui-ai/protocol';
3
+ import type { RenderSeedInput } from './types.js';
4
+ import { type WireConfig } from '@ggui-ai/wire';
5
+ import type { StreamBus } from './wire-config.js';
6
+ export interface RenderItemOptions {
7
+ /** The render to mount. */
8
+ readonly render: Render | RenderSeedInput;
9
+ /** Wire config for this render — caller pre-scopes via the runtime's
10
+ * `buildScopedConfig(render)` (see
11
+ * `wire-config.ts::RootWireConfigBundle`). `null` ⇒ the component
12
+ * mounts without a wire provider (standalone — matches today's
13
+ * DynamicComponent fallback when no GguiSession parent is present). */
14
+ readonly scopedWireConfig: WireConfig | null;
15
+ /** Shared stream bus — provisional renderer subscribes to
16
+ * `_ggui:preview` for this render. */
17
+ readonly streamBus: StreamBus;
18
+ /** Render id — used by the mcp-apps iframe host for proxy URLs. */
19
+ readonly renderId: string;
20
+ /**
21
+ * 3rd-party gadget package names whose direct imports the rewriter
22
+ * must resolve to per-package shims. FALLBACK source used only when
23
+ * the render carries no `gadgetDescriptors` sidecar — i.e. the static
24
+ * seed mount ({@link RenderSeedInput}, which has no descriptors). The
25
+ * caller threads the bootstrap's `meta.gadgets` package names here so a
26
+ * generated component importing a non-STDLIB gadget on a no-WS host
27
+ * (claude.ai / ChatGPT) still resolves its shim on first paint. A
28
+ * WS-delivered full `Render` carries `gadgetDescriptors` and ignores
29
+ * this. STDLIB `@ggui-ai/gadgets` is always rewritten regardless.
30
+ */
31
+ readonly gadgetPackages?: readonly string[];
32
+ /** ggui-server base URL (for mcp-apps proxy). Empty = same-origin. */
33
+ readonly mcpAppsServerBaseUrl?: string;
34
+ /** Theme id passed to ReactComponentRenderer for scoped CSS. */
35
+ readonly themeId?: string;
36
+ /**
37
+ * Theme color mode. Resolves the dark variant of `themeId` when set
38
+ * to `'dark'`. Falls through to `'light'` if absent — matches the
39
+ * design registry's `getTheme(id, mode?)` default.
40
+ */
41
+ readonly themeMode?: 'light' | 'dark';
42
+ readonly cssOverrides?: string;
43
+ readonly onError?: (err: Error) => void;
44
+ readonly onRequestRepair?: (err: Error) => void;
45
+ /**
46
+ * Optional outer wrapper applied AROUND the GguiWireProvider — used
47
+ * by `bootProduction` to install the `<ContextStateHost>` provider
48
+ * tree. Composition order:
49
+ *
50
+ * wrapOuter(<GguiWireProvider><userComponent/></GguiWireProvider>)
51
+ *
52
+ * `wrapOuter` returns the wrapped node; passing `undefined` is
53
+ * equivalent to the identity function (no extra wrapping). The
54
+ * self-contained boot path inlines its own ContextStateHost via the
55
+ * top-level `renderWrapper`; this seam exists for the WS-driven
56
+ * renderer path where the per-render React mount is the only mount
57
+ * surface.
58
+ */
59
+ readonly wrapOuter?: (mountedTree: ReactNode) => ReactNode;
60
+ }
61
+ /**
62
+ * Discriminator for WHICH renderer is currently mounted under this
63
+ * render handle. Transitions: 'provisional' → 'react' when
64
+ * componentCode lands; 'react' → 'react' on code/prop updates;
65
+ * 'mcpApps' is terminal for its lifetime (mcp-apps renders never
66
+ * become components + vice versa).
67
+ */
68
+ type MountedKind = 'provisional' | 'react' | 'mcpApps' | 'system' | 'none';
69
+ export interface RenderItemHandle {
70
+ /**
71
+ * Apply an update to the render handle. Kind changes (e.g.
72
+ * provisional → react when componentCode lands) tear down the old
73
+ * mount and stand up the replacement; same-kind updates route to
74
+ * the inner renderer's `update()`.
75
+ */
76
+ update(next: RenderItemOptions): Promise<void>;
77
+ /** Tear down the active mount + unsubscribe from the StreamBus. */
78
+ unmount(): void;
79
+ /** Current render kind. Test-only — not load-bearing. */
80
+ readonly kind: MountedKind;
81
+ }
82
+ export declare function mountRender(container: HTMLElement, opts: RenderItemOptions): Promise<RenderItemHandle>;
83
+ export {};