@ggui-ai/iframe-runtime 0.23.0 → 0.24.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.
@@ -1,7 +1,7 @@
1
1
  import type { ReactNode } from 'react';
2
2
  import type { AppTheme, GguiSession } from '@ggui-ai/protocol/wire';
3
3
  import type { GguiSessionSeedInput } from './types.js';
4
- import { type WireConfig } from '@ggui-ai/wire';
4
+ import { type BuiltWireConfig } from '@ggui-ai/wire';
5
5
  import type { StreamBus } from './wire-config.js';
6
6
  export interface RenderItemOptions {
7
7
  /** The render to mount. */
@@ -10,8 +10,10 @@ export interface RenderItemOptions {
10
10
  * `buildRootWireConfig(...)` (see `wire-config.ts`). `null` ⇒ the
11
11
  * component mounts without a wire provider (standalone — matches
12
12
  * today's DynamicComponent fallback when no GguiRender parent is
13
- * present). */
14
- readonly scopedWireConfig: WireConfig | null;
13
+ * present). The config carries the card's `actionSpent` source
14
+ * (ggui#1223), and the component mounts inside `ActionSpentContext` too,
15
+ * so `useActionSpent` reads the one-shot guard's own spent set. */
16
+ readonly scopedWireConfig: BuiltWireConfig | null;
15
17
  /** Shared stream bus — provisional renderer subscribes to
16
18
  * `_ggui:preview` for this render. */
17
19
  readonly streamBus: StreamBus;
@@ -39,6 +39,7 @@
39
39
  */
40
40
  import { createElement } from 'react';
41
41
  import { GguiWireProvider } from '@ggui-ai/wire';
42
+ import { ActionSpentContext } from '@ggui-ai/wire/internal';
42
43
  import { mountReactRoot } from './react-renderer.js';
43
44
  import { mountProvisional, } from './provisional-renderer.js';
44
45
  import { mountMcpAppIframe, } from './mcp-app-iframe-host.js';
@@ -208,9 +209,13 @@ export async function mountRender(container, opts) {
208
209
  * mount. When absent, the wire provider is the only wrapper.
209
210
  */
210
211
  function wrapInScopedProvider(mountedComponent) {
211
- const wireWrapped = currentOpts.scopedWireConfig === null
212
+ const config = currentOpts.scopedWireConfig;
213
+ const wireWrapped = config === null
212
214
  ? mountedComponent
213
- : createElement(GguiWireProvider, { config: currentOpts.scopedWireConfig, children: mountedComponent });
215
+ : createElement(GguiWireProvider, {
216
+ config,
217
+ children: createElement(ActionSpentContext.Provider, { value: config.actionSpent, children: mountedComponent }),
218
+ });
214
219
  return currentOpts.wrapOuter !== undefined
215
220
  ? currentOpts.wrapOuter(wireWrapped)
216
221
  : wireWrapped;
package/dist/runtime.js CHANGED
@@ -3794,6 +3794,15 @@ async function bootProduction(opts) {
3794
3794
  // through `currentRender`-returning thunks so they always see the
3795
3795
  // latest snapshot without holding stale refs.
3796
3796
  let currentRender = null;
3797
+ // ggui#1223 — who to tell when `currentRender` is replaced, so the card's
3798
+ // spent state (read through `getCurrentGguiSession`) is re-read at once.
3799
+ const renderListeners = new Set();
3800
+ const onRenderChange = (listener) => {
3801
+ renderListeners.add(listener);
3802
+ return () => {
3803
+ renderListeners.delete(listener);
3804
+ };
3805
+ };
3797
3806
  let renderHandle = null;
3798
3807
  const dispatchToolName = resolveDispatchToolName();
3799
3808
  // Native-idiom interceptors — the replacement for the retired
@@ -3841,6 +3850,7 @@ async function bootProduction(opts) {
3841
3850
  sessionId: meta.sessionId,
3842
3851
  appId: meta.appId,
3843
3852
  getCurrentGguiSession: () => currentRender,
3853
+ renderChanges: onRenderChange,
3844
3854
  manager,
3845
3855
  streamBus,
3846
3856
  onDispatchEnvelope: (envelope) => {
@@ -3998,6 +4008,8 @@ async function bootProduction(opts) {
3998
4008
  };
3999
4009
  const applyRender = async (render) => {
4000
4010
  currentRender = render;
4011
+ for (const listener of [...renderListeners])
4012
+ listener();
4001
4013
  if (renderHandle === null) {
4002
4014
  renderHandle = await mountRender(renderInto, buildOpts(render));
4003
4015
  return;
@@ -1 +1 @@
1
- const w = globalThis.__ggui__.wire; export const useAction = w.useAction; export const useStream = w.useStream; export const useAuth = w.useAuth; export const useApp = w.useApp; export const useRender = w.useRender; export const useGguiContext = w.useGguiContext; export const useContract = w.useContract; export const useWireContext = w.useWireContext; export const GguiWireProvider = w.GguiWireProvider; export default w;
1
+ const w = globalThis.__ggui__.wire; export const useAction = w.useAction; export const useStream = w.useStream; export const useAuth = w.useAuth; export const useApp = w.useApp; export const useRender = w.useRender; export const useGguiContext = w.useGguiContext; export const useContract = w.useContract; export const useWireContext = w.useWireContext; export const useActionSpent = w.useActionSpent; export const GguiWireProvider = w.GguiWireProvider; export default w;
@@ -18,7 +18,7 @@
18
18
  */
19
19
  import type { ActionEnvelope, GguiSession } from '@ggui-ai/protocol/wire';
20
20
  import type { GguiSessionSeedInput } from './types.js';
21
- import { ClientContractViolationError, StreamBus, type WireConfig } from '@ggui-ai/wire';
21
+ import { ClientContractViolationError, StreamBus, type BuiltWireConfig } from '@ggui-ai/wire';
22
22
  import type { WebSocketMessage } from '@ggui-ai/protocol/transport/websocket';
23
23
  /**
24
24
  * Outbound send surface — the minimal shape wire-config calls on the
@@ -48,6 +48,12 @@ export interface BuildRootWireConfigOptions {
48
48
  * WireConfig.
49
49
  */
50
50
  readonly getCurrentGguiSession: () => GguiSession | GguiSessionSeedInput | null;
51
+ /**
52
+ * The runtime's notice that the current render was replaced (a props
53
+ * update, a later live frame), so the card's spent state is re-read at
54
+ * once (ggui#1223). Passed through as wire's `spentInputsChanged`.
55
+ */
56
+ readonly renderChanges?: (listener: () => void) => () => void;
51
57
  /** Handle to the renderer's WS manager; used for outbound `action` frames. */
52
58
  readonly manager: RendererSendSurface;
53
59
  /** Shared bus for inbound stream deliveries. */
@@ -124,4 +130,4 @@ export interface BuildRootWireConfigOptions {
124
130
  * `RootWireConfigBundle` / `scopeWireConfig` / `buildScopedConfig`
125
131
  * indirection collapsed to a single `WireConfig`.
126
132
  */
127
- export declare function buildRootWireConfig(opts: BuildRootWireConfigOptions): WireConfig;
133
+ export declare function buildRootWireConfig(opts: BuildRootWireConfigOptions): BuiltWireConfig;
@@ -87,6 +87,7 @@ export function buildRootWireConfig(opts) {
87
87
  : undefined;
88
88
  },
89
89
  onActionSpecAbsent: nameUnenforceableOnce,
90
+ ...(opts.renderChanges !== undefined ? { spentInputsChanged: opts.renderChanges } : {}),
90
91
  // ggui#1223 — the card's persisted spent `oneShot` names. A re-served card
91
92
  // boots a new iframe, so the guard's in-memory set starts empty; the
92
93
  // render's record says what this card already spent. It counts only when
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ggui-ai/iframe-runtime",
3
- "version": "0.23.0",
3
+ "version": "0.24.0",
4
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",
@@ -41,12 +41,12 @@
41
41
  "react": "^19.0.0",
42
42
  "react-dom": "^19.0.0",
43
43
  "zod": "^4.3.6",
44
- "@ggui-ai/live-channel": "0.23.0",
45
- "@ggui-ai/preview-a2ui": "0.23.0",
46
- "@ggui-ai/protocol": "0.23.0",
47
- "@ggui-ai/gadgets": "0.23.0",
48
- "@ggui-ai/design": "0.23.0",
49
- "@ggui-ai/wire": "0.23.0"
44
+ "@ggui-ai/design": "0.24.0",
45
+ "@ggui-ai/gadgets": "0.24.0",
46
+ "@ggui-ai/live-channel": "0.24.0",
47
+ "@ggui-ai/preview-a2ui": "0.24.0",
48
+ "@ggui-ai/protocol": "0.24.0",
49
+ "@ggui-ai/wire": "0.24.0"
50
50
  },
51
51
  "peerDependencies": {
52
52
  "@modelcontextprotocol/sdk": "^1.27.0"