@ggui-ai/iframe-runtime 0.14.0 → 0.16.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.
Files changed (42) hide show
  1. package/dist/channel-transport.d.ts +3 -3
  2. package/dist/channels/channel-payload.d.ts +1 -1
  3. package/dist/channels/data.d.ts +1 -1
  4. package/dist/channels/drain-ack.d.ts +1 -1
  5. package/dist/channels/props-update.d.ts +1 -1
  6. package/dist/channels/render-frame.d.ts +1 -1
  7. package/dist/compiled-validators.d.ts +1 -1
  8. package/dist/connection.d.ts +34 -12
  9. package/dist/connection.js +40 -12
  10. package/dist/context-observer.d.ts +1 -1
  11. package/dist/context-observer.js +1 -1
  12. package/dist/events-polling.d.ts +1 -1
  13. package/dist/host-context-emitter.d.ts +23 -2
  14. package/dist/host-context-emitter.js +38 -5
  15. package/dist/host-fonts.d.ts +10 -0
  16. package/dist/host-fonts.js +113 -0
  17. package/dist/host-palette-bridge.js +20 -15
  18. package/dist/iframe-runtime.js +97 -108
  19. package/dist/iframe-runtime.meta.json +1 -1
  20. package/dist/index.d.ts +1 -1
  21. package/dist/mcp-app-iframe-host.d.ts +15 -0
  22. package/dist/mcp-app-iframe-host.js +5 -1
  23. package/dist/meta-parse.d.ts +9 -0
  24. package/dist/meta-parse.js +15 -3
  25. package/dist/observability.d.ts +73 -14
  26. package/dist/provisional-renderer.d.ts +1 -1
  27. package/dist/react-renderer.d.ts +19 -37
  28. package/dist/react-renderer.js +64 -72
  29. package/dist/registry-subscribe.d.ts +1 -1
  30. package/dist/render-item.d.ts +7 -14
  31. package/dist/runtime-telemetry.js +1 -1
  32. package/dist/runtime.d.ts +48 -56
  33. package/dist/runtime.js +213 -115
  34. package/dist/system-cards/NoCredentialsCard.js +13 -13
  35. package/dist/system-cards/ProtocolProbeCard.js +11 -11
  36. package/dist/system-cards/QrCode.js +1 -1
  37. package/dist/system-cards/index.js +2 -2
  38. package/dist/types.d.ts +1 -1
  39. package/dist/validation.d.ts +1 -1
  40. package/dist/validation.js +1 -1
  41. package/dist/wire-config.d.ts +3 -3
  42. package/package.json +7 -7
package/dist/runtime.d.ts CHANGED
@@ -25,7 +25,7 @@
25
25
  * wrapper, which routes it to the host's `onError` callback.
26
26
  */
27
27
  import './zod-jitless.js';
28
- import type { DrainAckPayload, JsonValue, JsonObject, GguiSession } from '@ggui-ai/protocol';
28
+ import type { DrainAckPayload, JsonValue, JsonObject, GguiSession } from '@ggui-ai/protocol/wire';
29
29
  import type { WebSocketMessage } from '@ggui-ai/protocol/transport/websocket';
30
30
  import { type McpAppAiGguiRenderMeta, type McpAppBootstrapFailedMessage, type McpAppRendererReadyMessage } from '@ggui-ai/protocol/integrations/mcp-apps';
31
31
  import type { GguiSessionSeedInput } from './types.js';
@@ -97,36 +97,32 @@ export declare function setCurrentApp(app: App): void;
97
97
  * `color-scheme` via `applyDocumentTheme` — nothing in the token
98
98
  * pipeline keys off it, so a dark host painted the light ladder
99
99
  * (host-fit 46.0 vs 87.7 with the dark ladder, probe `cc728a8eb`).
100
- * `buildOpts` consumes this ONLY when the slice stamps no `themeMode`:
101
- * every operator layer (live provider > per-render override > app
102
- * default > server default) still wins by being stamped; the host
103
- * fills the gap where no operator had an opinion.
100
+ * Under ggui#987 §4 the embedding host OWNS runtime mode: when it
101
+ * announces one, `buildOpts` paints it over every stamped opinion; the
102
+ * stamped layers decide only when the host is silent. Read live from
103
+ * the App's pre-merged hostContext, so a `hostcontextchanged` flip
104
+ * re-resolves on the next `buildOpts`.
104
105
  *
105
106
  * @internal — exported for unit tests (`setCurrentApp` injects the
106
107
  * App); production caller is `buildOpts` inside `bootSequence`.
107
108
  */
108
109
  export declare function hostAnnouncedThemeMode(): 'light' | 'dark' | undefined;
109
110
  /**
110
- * Resolve the mount's base-stylesheet color mode from the slice, with
111
- * the host as the final fallback (ggui#589, completing the ggui#551
112
- * precedence ruling — "slice wins, host is the fallback, absent ≠
113
- * light"):
111
+ * Resolve the mount's color mode (ggui#987 §4 — the embedding host owns
112
+ * runtime mode; the app theme's `mode` is a DEFAULT, never a pin):
114
113
  *
115
- * stamped `themeMode` > `theme.mode` (the slice theme OBJECT) >
116
- * host-announced theme > undefined
114
+ * host-announced theme > stamped `themeMode` > `theme.mode`
115
+ * (the app theme's default) > undefined
117
116
  *
118
- * The middle leg is the ggui#589 fix: a render envelope carrying a
119
- * per-app theme (`theme: {mode, cssVariables}`) but no top-level
120
- * `themeMode` is still a slice-stamped mode opinion — before this,
121
- * the base token ladder ignored it and painted the LIGHT token set
122
- * under a dark overlay whenever the host (correctly, per the
123
- * adapter-boundary doctrine in the native host helpers) announced no
124
- * `hostContext.theme`: a light skeleton in a dark skin. The theme
125
- * object's own `mode` already drove `color-scheme`; now it also
126
- * selects the ladder.
117
+ * A card that ignored the host's announced mode painted a light
118
+ * skeleton inside a dark chat — the host's word is what the user
119
+ * actually sees around the card. The stamped layers (live provider >
120
+ * per-render override > app default > server default) decide only when
121
+ * the host is silent, which is the adapter-boundary case (a host
122
+ * helper that announces no `hostContext.theme`).
127
123
  *
128
124
  * `undefined` still means "no opinion anywhere" — the renderer's
129
- * default applies; never coerced to `'light'`.
125
+ * default applies; never coerced to `'light'` here (ggui#551).
130
126
  *
131
127
  * @internal — exported for unit tests; production caller is
132
128
  * `buildOpts` inside `bootSequence`.
@@ -134,36 +130,24 @@ export declare function hostAnnouncedThemeMode(): 'light' | 'dark' | undefined;
134
130
  export declare function resolveMountThemeMode(meta: {
135
131
  readonly themeMode?: 'light' | 'dark';
136
132
  readonly theme?: {
137
- readonly mode: 'light' | 'dark';
138
- readonly cssVariables?: Record<string, string>;
133
+ readonly mode?: 'light' | 'dark';
139
134
  };
140
135
  }): 'light' | 'dark' | undefined;
141
136
  /**
142
- * Resolve the mount's base-theme id from the slice (ggui#589 ask 3):
137
+ * Resolve the mount's compiled-ladder id from the slice (ggui#987 D2):
143
138
  *
144
- * stamped `themeId` > `theme.name` (the slice theme OBJECT's name)
145
- * > undefined (renderer default ladder)
139
+ * stamped `themeId` > undefined (renderer default ladder)
146
140
  *
147
- * The name leg makes a slice theme whose `name` matches a REGISTERED
148
- * theme id select that theme as the BASE token ladder — the overlay's
149
- * cssVariables still apply above it, so the base carries the full
150
- * brand ramp coverage a sparse overlay cannot (the "only the mapped
151
- * vars carry brand" store-frame class). An unregistered name is
152
- * harmless by construction: `getScopedThemeCss` falls back to the
153
- * default theme, which is byte-identical to the no-themeId path
154
- * (`getScopedCssTokens` ≡ `getScopedThemeCss(default)`), so a slice
155
- * naming its theme "My Custom" renders exactly as before.
141
+ * The app theme's `name` is a LABEL the author's tooling reads back —
142
+ * it never selects a ladder. An app theme carries its own full
143
+ * projection (`overlays`, exactly the manifest minus the floor), so
144
+ * there is no coverage gap for a compiled theme to fill beneath it.
156
145
  *
157
146
  * @internal — exported for unit tests; production caller is
158
147
  * `buildOpts` inside `bootSequence`.
159
148
  */
160
149
  export declare function resolveMountThemeId(meta: {
161
150
  readonly themeId?: string;
162
- readonly theme?: {
163
- readonly name?: string;
164
- readonly mode?: 'light' | 'dark';
165
- readonly cssVariables?: Record<string, string>;
166
- };
167
151
  }): string | undefined;
168
152
  /**
169
153
  * The host's announced palette, read from the connected App's
@@ -279,10 +263,12 @@ export interface BootSequenceOptions {
279
263
  * entirely (matches the ProtocolError posture; the `<McpAppIframe>`
280
264
  * host wrapper decides whether to bind this via its `onObserve`
281
265
  * prop). Exception: `relay-incapability` events do NOT flow through
282
- * this seam — they originate in module-level gesture-dispatch code
283
- * outside the boot graph (it runs independently of any given boot
284
- * call) and always ride the postMessage-to-parent default, whether
285
- * or not this option is bound.
266
+ * this seam — by rule they always ride the postMessage-to-parent
267
+ * default, whether or not this option is bound: most emission sites
268
+ * are module-level gesture-dispatch code that runs independently of
269
+ * any given boot call, and the one inside the boot graph (the
270
+ * `boot-failed` edge, ggui#830) follows the same rule so a host reads
271
+ * every edge the same way.
286
272
  */
287
273
  readonly onObserve?: ObservabilityEmitter;
288
274
  /**
@@ -704,19 +690,22 @@ export declare function dispatchSubmitAction(args: {
704
690
  /**
705
691
  * Channel-transport router's `tools/call` invoker (ggui#440) —
706
692
  * iframe-polling transport, `tools/call` against the parent MCP host
707
- * via `app.callServerTool` (spec-canonical) when the App handle is
708
- * set; falls back to raw postMessage pre-handshake or in tests.
693
+ * via `app.callServerTool` (spec-canonical); with no App bound the
694
+ * call answers a code-less error envelope — never a raw postMessage.
709
695
  * Direct call (no LLM consent loop). Returns the tool's
710
696
  * structuredContent (or `content[0]` if that's where the payload
711
697
  * landed) as a `JsonValue`. On RPC error we throw — the router
712
698
  * catches and silently retries on the next tick.
713
699
  *
714
- * Once a REAL gesture has confirmed this host can't relay (the latch
715
- * above — set only after {@link dispatchSubmitAction} actually tried
716
- * and failed on a host with no `serverTools` advertised, with
717
- * capabilities already captured, AND the failure was relay-shaped —
718
- * not a well-formed `{ok:false}` result; see `isRelayShapedFailure`),
719
- * every subsequent poll will fail identically.
700
+ * Once this document has confirmed it cannot relay (the latch above —
701
+ * set through the ONE writer on exactly three paths: a REAL gesture
702
+ * that failed relay-shaped on a host that never advertised
703
+ * `serverTools`, with capabilities already captured (`advert-silent`);
704
+ * a declared refusal code answered under a positive advertisement
705
+ * (`confirmed-refusal`); or the host's own failure of the mandatory
706
+ * `ui/initialize` handshake, before any gesture (`boot-failed`,
707
+ * ggui#830) — never a well-formed `{ok:false}` result; see
708
+ * `isRelayShapedFailure`), every subsequent poll will fail identically.
720
709
  *
721
710
  * This guard does NOT stop the router's poll loop, and nothing here
722
711
  * should ever be written so that it could (ggui#443). It does two
@@ -742,10 +731,13 @@ export declare function dispatchSubmitAction(args: {
742
731
  * (ggui's own embed host pre-Task-2 is exactly this case) — silently
743
732
  * killing working channel polls forever. That is "absence of a
744
733
  * capability blocks an attempt", which the fail-safe constraint
745
- * forbids. The latch implies `!hostCanRelayToolCalls()` (it only
746
- * ever sets inside that guard), so testing it alone is sufficient —
747
- * a host that never fails a real gesture never latches, and its
748
- * channels keep working.
734
+ * forbids. The latch is set only by a CONFIRMED OUTCOME — a failed
735
+ * real gesture (the advert-silent leg, or a confirmed refusal under a
736
+ * positive advertisement, ggui#599 cycle-2) or a failed handshake
737
+ * (ggui#830), never by an advertisement's absence — so testing the
738
+ * latch alone is sufficient: a host that answers its handshake and
739
+ * never fails a real gesture never latches, and its channels keep
740
+ * working.
749
741
  *
750
742
  * Extracted to a named export (rather than an inline closure inside
751
743
  * `bootProduction`) so unit tests can drive it directly through the