@ggui-ai/protocol 0.1.0-rc.1 → 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 (92) hide show
  1. package/README.md +3 -3
  2. package/dist/bridge/invoke-agent.d.ts +8 -8
  3. package/dist/bridge/invoke-agent.d.ts.map +1 -1
  4. package/dist/bridge/invoke-agent.js +12 -12
  5. package/dist/envelopes/builders.d.ts +2 -4
  6. package/dist/envelopes/builders.d.ts.map +1 -1
  7. package/dist/envelopes/builders.js +2 -6
  8. package/dist/gadgets/stdlib-gadgets.d.ts +1 -1
  9. package/dist/gadgets/stdlib-gadgets.js +1 -1
  10. package/dist/index.d.ts +2 -1
  11. package/dist/index.d.ts.map +1 -1
  12. package/dist/index.js +4 -3
  13. package/dist/integrations/mcp-apps.d.ts +354 -711
  14. package/dist/integrations/mcp-apps.d.ts.map +1 -1
  15. package/dist/integrations/mcp-apps.js +266 -208
  16. package/dist/recommended-prompts.d.ts +1 -1
  17. package/dist/recommended-prompts.js +1 -1
  18. package/dist/schemas/data-contract.d.ts +4 -3
  19. package/dist/schemas/data-contract.d.ts.map +1 -1
  20. package/dist/schemas/data-contract.js +4 -3
  21. package/dist/schemas/invoke.d.ts +2 -2
  22. package/dist/schemas/invoke.js +2 -2
  23. package/dist/schemas/mcp.d.ts +60 -79
  24. package/dist/schemas/mcp.d.ts.map +1 -1
  25. package/dist/schemas/mcp.js +93 -116
  26. package/dist/schemas/sync-check.js +1 -1
  27. package/dist/stream/stream-parser.d.ts +4 -4
  28. package/dist/stream/stream-parser.d.ts.map +1 -1
  29. package/dist/stream/stream-parser.js +9 -9
  30. package/dist/transport/websocket.d.ts +14 -49
  31. package/dist/transport/websocket.d.ts.map +1 -1
  32. package/dist/types/app-config.d.ts +17 -32
  33. package/dist/types/app-config.d.ts.map +1 -1
  34. package/dist/types/canvas-lifecycle.d.ts +13 -13
  35. package/dist/types/canvas-lifecycle.d.ts.map +1 -1
  36. package/dist/types/canvas-lifecycle.js +3 -3
  37. package/dist/types/contract-inference.d.ts +4 -7
  38. package/dist/types/contract-inference.d.ts.map +1 -1
  39. package/dist/types/data-bindings.d.ts +1 -1
  40. package/dist/types/data-bindings.d.ts.map +1 -1
  41. package/dist/types/data-contract.d.ts +10 -10
  42. package/dist/types/data-contract.d.ts.map +1 -1
  43. package/dist/types/events.d.ts +14 -56
  44. package/dist/types/events.d.ts.map +1 -1
  45. package/dist/types/events.js +1 -8
  46. package/dist/types/feedback.d.ts +2 -4
  47. package/dist/types/feedback.d.ts.map +1 -1
  48. package/dist/types/handshake-suggestion.d.ts +1 -1
  49. package/dist/types/host-context.d.ts +5 -15
  50. package/dist/types/host-context.d.ts.map +1 -1
  51. package/dist/types/host-context.js +1 -1
  52. package/dist/types/live-channel.d.ts +128 -191
  53. package/dist/types/live-channel.d.ts.map +1 -1
  54. package/dist/types/llm-route.d.ts +233 -0
  55. package/dist/types/llm-route.d.ts.map +1 -0
  56. package/dist/types/llm-route.js +433 -0
  57. package/dist/types/llm.d.ts +35 -17
  58. package/dist/types/llm.d.ts.map +1 -1
  59. package/dist/types/llm.js +50 -39
  60. package/dist/types/mcp.d.ts +47 -137
  61. package/dist/types/mcp.d.ts.map +1 -1
  62. package/dist/types/mcp.js +2 -2
  63. package/dist/types/openrouter-models.d.ts +3 -2
  64. package/dist/types/openrouter-models.d.ts.map +1 -1
  65. package/dist/types/render-event.d.ts +119 -0
  66. package/dist/types/render-event.d.ts.map +1 -0
  67. package/dist/types/render-event.js +38 -0
  68. package/dist/types/session.d.ts +216 -342
  69. package/dist/types/session.d.ts.map +1 -1
  70. package/dist/types/ui-generator.d.ts +8 -9
  71. package/dist/types/ui-generator.d.ts.map +1 -1
  72. package/dist/validation/contract-validator.d.ts +60 -4
  73. package/dist/validation/contract-validator.d.ts.map +1 -1
  74. package/dist/validation/contract-validator.js +101 -5
  75. package/dist/validation/reserved-channels.d.ts +1 -1
  76. package/dist/validation/reserved-channels.js +5 -5
  77. package/dist/validation/sanitize-error.js +1 -1
  78. package/dist/validation/schema-compat-invariants.d.ts +2 -2
  79. package/dist/validation/schema-compat-invariants.js +2 -2
  80. package/dist/validation/schema-meta-validation.d.ts +1 -1
  81. package/dist/validation/schema-meta-validation.js +2 -2
  82. package/dist/validation/schema-subset.d.ts +1 -1
  83. package/dist/version.d.ts +253 -46
  84. package/dist/version.d.ts.map +1 -1
  85. package/dist/version.js +253 -46
  86. package/package.json +17 -16
  87. package/dist/navigation/index.d.ts +0 -3
  88. package/dist/navigation/index.d.ts.map +0 -1
  89. package/dist/navigation/index.js +0 -1
  90. package/dist/navigation/stack-navigation.d.ts +0 -55
  91. package/dist/navigation/stack-navigation.d.ts.map +0 -1
  92. package/dist/navigation/stack-navigation.js +0 -80
@@ -1,399 +1,270 @@
1
- import type { EventSubscription } from './events';
2
1
  import type { CapabilityPermissions, QualityMetadata } from './capabilities';
3
2
  import type { KNOWN_PERMISSION_NAMES } from '../validation/hygiene-rules';
4
3
  import type { PropsSpec, StreamSpec, ActionSpec, ContextSpec, ClientCapabilitiesSpec, GadgetDescriptor, JsonSchema, JsonObject } from './data-contract';
5
4
  import type { EndUserIdentity } from './auth';
6
5
  import type { HostContextProjection } from './host-context';
7
- import type { McpAppsMode } from './app-config';
8
- import type { McpAppsStackItem } from '../integrations/mcp-apps';
6
+ import type { McpAppsRender } from '../integrations/mcp-apps';
9
7
  /**
10
- * Declarative action for interactive UI elements.
11
- *
12
- * @deprecated Pre-actionSpec wire shape. New code declares user gestures
13
- * via {@link DataContract.actionSpec} ({@link ActionEntry}); the entry
14
- * carries `label` / `schema` / `nextStep` / `confirm` / `icon`. This
15
- * interface is retained because some stored stack items still carry an
16
- * `actions?: Action[]` field through denormalizers.
8
+ * Recognised system-card kinds the runtime renders via built-in
9
+ * components. The wire is open-ended (string), but new kinds without a
10
+ * matching renderer fall through to a generic "system message" card so
11
+ * an old runtime + new server still produce something visible.
12
+ */
13
+ export type SystemRenderKind = 'no-credentials' | 'mcp-apps-probe' | (string & {});
14
+ /**
15
+ * Adapter permission status
16
+ */
17
+ export type PermissionStatus = 'granted' | 'denied' | 'prompt';
18
+ /**
19
+ * Adapter permissions — per-render map keyed by Web Permissions API
20
+ * name (see `KNOWN_PERMISSION_NAMES` in `validation/hygiene-rules.ts`).
17
21
  *
18
- * Extends {@link JsonObject} so it can be serialized directly over
19
- * WebSocket or stored server-side without transformation.
22
+ * Keys derive from `KNOWN_PERMISSION_NAMES` so there's one source of
23
+ * truth for permission identifiers across the protocol surface
24
+ * (schema validation, hygiene rules, and runtime SDK cache).
20
25
  */
21
- export interface Action extends JsonObject {
22
- /** Unique identifier for this action (returned in the event payload) */
23
- id: string;
24
- /** Human-readable label displayed on the UI element */
25
- label: string;
26
- /** Optional description shown as secondary text */
27
- description?: string;
28
- /** Optional icon hint (emoji or icon name) */
29
- icon?: string;
30
- /** Visual style hint */
31
- variant?: 'primary' | 'secondary' | 'danger' | 'ghost';
32
- /** Whether to show confirmation before triggering */
33
- confirm?: boolean | string;
34
- /** Whether currently disabled */
35
- disabled?: boolean;
36
- }
26
+ export type AdapterPermissions = {
27
+ readonly [K in (typeof KNOWN_PERMISSION_NAMES)[number]]?: PermissionStatus;
28
+ };
37
29
  /**
38
- * Stack item — the generated/native component variant.
30
+ * Lifecycle status of a {@link Render}. RenderStore implementations MAY
31
+ * populate this on `get`; absent ⇒ caller treats as `'active'`.
39
32
  *
40
- * One of two variants in the {@link SessionStackEntry} discriminated
41
- * union (the other being {@link McpAppsStackItem} for inbound MCP Apps
42
- * hosting). The discriminator is the optional `type` field:
43
- * - `type: 'component'` or absent → this variant (generated UI).
44
- * - `type: 'mcpApps'` → {@link McpAppsStackItem}.
33
+ * Two states only:
34
+ * - `'active'` — within TTL, agent may still write/read.
35
+ * - `'expired'` — TTL elapsed; no further writes accepted, reads return
36
+ * a historical snapshot.
45
37
  *
46
- * Existing producers that don't populate `type` continue to create
47
- * component items — `type` is optional for back-compat with stored
48
- * data and producers from before the discriminated-union landed.
49
- * Consumers narrow via `item.type === 'mcpApps'` (or the
50
- * {@link isMcpAppsStackItem} helper in
51
- * `@ggui-ai/protocol/integrations/mcp-apps`).
38
+ * There is no explicit terminal state. Renders decay implicitly via TTL.
39
+ * The dropped `'completed'` state was a vestige of the deleted Session
40
+ * vessel — kept symmetrical with `ggui_new_session`, which was deleted
41
+ * in the earlier handshake collapse. The companion `ggui_close` tool
42
+ * that wrote it was removed in the same slice.
43
+ */
44
+ export type RenderStatus = 'active' | 'expired';
45
+ /**
46
+ * Common base for every {@link Render} variant. Carries identity,
47
+ * tenancy, lifecycle, and conversation-scoped context that every
48
+ * rendered thing has regardless of how its visible bits are
49
+ * produced (LLM-generated component, server-emitted system card,
50
+ * embedded MCP-App iframe).
52
51
  *
53
- * Generic `TProps` defaults to {@link JsonObject} for untyped usage.
54
- * When a contract is known, pass the inferred type for compile-time safety:
55
- * `StackItem<{ city: string; temperature: number }>`
52
+ * **Conversation-scoped context note** — fields that convey "this
53
+ * render belongs to a logical group of renders within a host
54
+ * conversation" (hostContext continuity, sibling render discovery)
55
+ * are NOT duplicated onto each Render. They look up via the optional
56
+ * {@link RenderBase.hostSession} pair instead. See
57
+ * [[session-concept-deletion-2026-05-27]] for the framing.
56
58
  */
57
- export interface StackItem<TProps = JsonObject> {
58
- id: string;
59
+ export interface RenderBase {
60
+ /** Render identity. The value an iframe's bootstrap meta and every
61
+ * wire reference (props_update, consume, update) keys by. */
62
+ readonly id: string;
63
+ /** App identity (tenancy boundary, always per-render). */
64
+ readonly appId: string;
65
+ /** Authenticated end-user (after the auth gate). Absent for anon
66
+ * flows. */
67
+ readonly userId?: string;
68
+ /** Full authenticated end-user identity (populated after auth gate). */
69
+ readonly endUserIdentity?: EndUserIdentity;
59
70
  /**
60
- * Variant discriminator. Optional on this (component) variant — an
61
- * absent `type` implicitly means `'component'`. Required `'mcpApps'`
62
- * on the {@link McpAppsStackItem} variant.
63
- */
64
- type?: 'component';
65
- /**
66
- * Theme preset id for THIS stack item — agent-explicit per-push
67
- * override on the layered theme-resolution chain:
71
+ * Host-supplied conversation-grouping pair, captured ONCE at render
72
+ * creation from `_meta["ai.ggui/host-session"]` on the inbound tool
73
+ * call. Identifies the MCP host (claude.ai, sample, etc.) and the
74
+ * host's opaque grouping key for "this conversation" — typically
75
+ * the host's thread/chat id.
68
76
  *
69
- * 1. `StackItem.themeId` — this field (rare; agent explicit)
70
- * 2. `Session.themeId` — chat-scoped default (set on new_session)
71
- * 3. `App.defaultThemeId` — server-side per-app default
72
- * 4. server fallback — `listThemes()[0]` / process default
77
+ * Multiple renders inside one host conversation share the same
78
+ * `hostSessionId`. The `ggui_list_renders(hostName, hostSessionId)`
79
+ * tool enumerates renders belonging to a host conversation; absent
80
+ * `hostSession` ⇒ the render is one-shot (functional but
81
+ * non-rehydratable).
73
82
  *
74
- * First non-undefined wins at bootstrap-meta projection time. Set
75
- * this only when the agent wants ONE push to render differently
76
- * from the session default (e.g. an urgent alert in `crimson` while
77
- * the rest of the chat stays on `slate`). Most pushes omit it and
78
- * inherit the session theme; that's the intended common case.
83
+ * Once captured, immutable for the render's lifetime.
79
84
  */
80
- themeId?: string;
81
- /** Blueprint component code (pure UI) */
82
- componentCode: string;
83
- /** Props to pass to the component at render time.
84
- * Carries request-specific data values (e.g., { city: "Seoul", temperature: 15 }).
85
- * Renderer passes these as: <Component {...props} /> */
86
- props?: TProps;
87
- /** The prompt that produced this component (if LLM-generated) */
88
- prompt?: string;
89
- /** Human-readable description of what this component is */
90
- description?: string;
91
- /** Content type. Determines how componentCode is interpreted.
92
- * Default: 'application/javascript+react' */
93
- contentType?: string;
94
- /** Agent message to display while the component generates (thinking indicator) */
95
- message?: string;
96
- /** JSON Schema for validating user-submitted form data */
97
- schema?: JsonSchema;
85
+ readonly hostSession?: {
86
+ readonly hostName: string;
87
+ readonly hostSessionId: string;
88
+ };
98
89
  /**
99
- * Event filter — pre-actionSpec event-routing surface.
100
- *
101
- * @deprecated The four-spec model (propsSpec/actionSpec/streamSpec/
102
- * contextSpec) on {@link DataContract} replaces wholesale event
103
- * filtering. Retained on stored stack items for back-compat.
90
+ * Theme preset id for this render. Resolution chain:
91
+ * 1. `Render.themeId` — agent-explicit per-render override
92
+ * 2. `App.defaultThemeId` — server-side per-app default
93
+ * 3. server fallback (process default)
94
+ * First non-undefined wins at bootstrap-meta projection.
104
95
  */
105
- subscription?: EventSubscription;
106
- /** Capability permissions granted to this component */
107
- capabilities?: CapabilityPermissions;
96
+ readonly themeId?: string;
108
97
  /**
109
- * Declarative interaction contract — pre-actionSpec list shape.
98
+ * Latest `HostContextProjection` echoed from the iframe-runtime via
99
+ * the live-channel `host_context_observed` message. Captured at
100
+ * iframe `ui/initialize` time + updated on every spec-defined
101
+ * `ui/notifications/host-context-changed` notification.
110
102
  *
111
- * @deprecated Use {@link DataContract.actionSpec} ({@link ActionEntry})
112
- * instead. Retained on stored stack items for back-compat.
103
+ * Surfaced to the agent via `client.hostContext` on `ggui_handshake`
104
+ * and `ggui_consume` output so the agent can reason about device
105
+ * class, available display modes, and container dimensions.
106
+ *
107
+ * Absent ⇒ the iframe has not echoed a HostContext yet; agent falls
108
+ * back to ggui's `InterfaceContext`.
109
+ */
110
+ readonly hostContext?: HostContextProjection;
111
+ /**
112
+ * Per-render Web Permissions API status map. Populated as the user
113
+ * grants / denies adapter requests (geolocation, camera, etc.) over
114
+ * the render's lifetime.
113
115
  */
114
- actions?: Action[];
115
- /** Quality evaluation metadata */
116
- quality?: QualityMetadata;
117
- /** Generation error message (populated on failure) */
118
- error?: string;
119
- /** Stream contract — describes what data the component accepts in real-time via ggui_emit */
120
- streamSpec?: StreamSpec;
121
- /** Props contract — initial render data interface (JSON Schema-based) */
122
- propsSpec?: PropsSpec;
123
- /** Action contract — user interaction callbacks (JSON Schema-based) */
124
- actionSpec?: ActionSpec;
116
+ readonly adapterPermissions?: AdapterPermissions;
117
+ /** Resolved lifecycle status. Absent ⇒ caller treats as `'active'`. */
118
+ readonly status?: RenderStatus;
119
+ /** Monotonic event ledger sequence (per-render — each Render has its
120
+ * own RenderEvent ledger). */
121
+ readonly eventSequence: number;
122
+ /** Creation timestamp (epoch ms). */
123
+ readonly createdAt: number;
124
+ /** Last activity timestamp (epoch ms). */
125
+ readonly lastActivityAt: number;
126
+ /** Expiry timestamp (epoch ms). */
127
+ readonly expiresAt: number;
128
+ }
129
+ /**
130
+ * `Render` — the canonical protocol shape for a single rendered UI.
131
+ *
132
+ * Three variants share {@link RenderBase} (identity, tenancy,
133
+ * lifecycle); each variant adds its own visible-bits surface:
134
+ *
135
+ * - {@link ComponentRender} — LLM-generated / native React component.
136
+ * Carries `componentCode`, `propsSpec`, `actionSpec`, `streamSpec`,
137
+ * `contextSpec`, `gadgetDescriptors`, etc.
138
+ * - {@link SystemRender} — server-emitted system card. Carries an
139
+ * opaque `kind` + structured `props`; the runtime maps the kind to
140
+ * a built-in `.tsx` renderer.
141
+ * - {@link McpAppsRender} — embedded third-party MCP-App iframe.
142
+ * Carries `resourceUri` + MCP server config. (Lives in
143
+ * `@ggui-ai/protocol/integrations/mcp-apps` to keep core typing
144
+ * MCP-Apps-opt-in.)
145
+ *
146
+ * Narrowing pattern:
147
+ * ```ts
148
+ * if (render.type === 'mcpApps') {
149
+ * // McpAppsRender — render via host-role adapter
150
+ * } else if (render.type === 'system') {
151
+ * // SystemRender — render via built-in card registry
152
+ * } else {
153
+ * // ComponentRender — render via DynamicComponent
154
+ * }
155
+ * ```
156
+ */
157
+ export type Render<TProps = JsonObject> = ComponentRender<TProps> | SystemRender | McpAppsRender;
158
+ /**
159
+ * Render variant: LLM-generated / native React component.
160
+ *
161
+ * Discriminator: `type === 'component'` OR `type` absent (back-compat
162
+ * default — every producer that omits `type` is producing this variant).
163
+ */
164
+ export interface ComponentRender<TProps = JsonObject> extends RenderBase {
165
+ /** Variant discriminator. Optional; absent ⇒ `'component'`. */
166
+ readonly type?: 'component';
167
+ /** Blueprint component code (pure UI). */
168
+ readonly componentCode: string;
169
+ /** Props passed to the component at render time. Carries
170
+ * request-specific data values (e.g., `{ city: 'Seoul', temperature: 15 }`). */
171
+ readonly props?: TProps;
172
+ /** The prompt that produced this component (when LLM-generated). */
173
+ readonly prompt?: string;
174
+ /** Human-readable description of what this component is. */
175
+ readonly description?: string;
176
+ /** Content type. Determines how `componentCode` is interpreted.
177
+ * Default: `'application/javascript+react'`. */
178
+ readonly contentType?: string;
179
+ /** Agent message to display while the component generates
180
+ * (thinking indicator). */
181
+ readonly message?: string;
182
+ /** JSON Schema for validating user-submitted form data. */
183
+ readonly schema?: JsonSchema;
184
+ /** Capability permissions granted to this component. */
185
+ readonly capabilities?: CapabilityPermissions;
186
+ /** Quality evaluation metadata. */
187
+ readonly quality?: QualityMetadata;
188
+ /** Generation error message (populated on failure). */
189
+ readonly error?: string;
190
+ /** Stream contract — describes what data the component accepts in
191
+ * real-time via `ggui_emit`. */
192
+ readonly streamSpec?: StreamSpec;
193
+ /** Props contract — initial render data interface
194
+ * (JSON Schema-based). */
195
+ readonly propsSpec?: PropsSpec;
196
+ /** Action contract — user interaction callbacks
197
+ * (JSON Schema-based). */
198
+ readonly actionSpec?: ActionSpec;
125
199
  /** Context contract — observable client state surfaced to the agent's
126
- * LLM context via React Context Providers. See {@link ContextSpec}
127
- * for the full contract. */
128
- contextSpec?: ContextSpec;
200
+ * LLM context via React Context Providers. */
201
+ readonly contextSpec?: ContextSpec;
129
202
  /**
130
203
  * Client capabilities catalog — declarative browser-capability gadget
131
- * hook bindings carried alongside this stack item. Mirrored verbatim
132
- * from `DataContract.clientCapabilities` at `ggui_push` commit time.
204
+ * hook bindings carried alongside this render. Mirrored verbatim from
205
+ * `DataContract.clientCapabilities` at `ggui_render` commit time.
133
206
  *
134
- * The persisted shape is the **wire** view
135
- * ({@link ClientCapabilitiesSpec}) — package-keyed:
136
- * `Record<package, { exports: Record<exportName, …> }>`. Identity
137
- * (`package` + per-export `name`) is preserved verbatim from the
138
- * agent's push payload — no enrichment overlay, no `version` (it
139
- * resolves from `App.gadgets`). Resolution metadata (`version`,
140
- * `bundleUrl`, `bundleSri`, `bundleHost`, `permission`, `connect`,
141
- * `styleUrl`, ...) lives on the parallel
142
- * {@link SessionStackEntry.gadgetDescriptors} sidecar — a filtered
143
- * snapshot of `App.gadgets` covering only the descriptors referenced
144
- * by this contract.
145
- *
146
- * Absent ⇒ no permissions requested (default-deny). Bootstrap omits
147
- * `permissionsPolicy` and the public-render HTTP response sets no
148
- * `Permissions-Policy` header beyond the host's own defaults.
207
+ * Package-keyed wire shape; identity (`package` + per-export `name`)
208
+ * preserved verbatim from the agent's render payload — no enrichment
209
+ * overlay, no `version` (resolves from `App.gadgets`). Resolution
210
+ * metadata lives on the parallel
211
+ * {@link ComponentRender.gadgetDescriptors} sidecar.
149
212
  */
150
- clientCapabilities?: ClientCapabilitiesSpec;
213
+ readonly clientCapabilities?: ClientCapabilitiesSpec;
151
214
  /**
152
- * Descriptor sidecar. Subset of `App.gadgets` containing
153
- * exactly the descriptors referenced by `clientCapabilities.gadgets`
154
- * — matched by `(package, export name)`. `version` is not on the
155
- * wire; it resolves from each descriptor's `App.gadgets` entry.
156
- * Snapshot at push-time; never re-resolved at render. Permissions-Policy
157
- * + CSP + bundle-loader derivation read from this array (see
158
- * `bootstrap-meta-derivation.ts`).
159
- *
160
- * Smaller than the full `App.gadgets` (~1-5KB typical); the full
161
- * catalog stays recoverable from operator config if forensics need it.
162
- *
163
- * Omitted when `clientCapabilities` declares no gadgets.
215
+ * Descriptor sidecar — subset of `App.gadgets` containing exactly
216
+ * the descriptors referenced by `clientCapabilities`. Snapshot at
217
+ * render-commit time; never re-resolved at runtime. Permissions-Policy
218
+ * + CSP + bundle-loader derivation read from this array.
164
219
  */
165
- gadgetDescriptors?: readonly GadgetDescriptor[];
220
+ readonly gadgetDescriptors?: readonly GadgetDescriptor[];
166
221
  /**
167
222
  * Last-known snapshot of the contextSpec slot values, mirrored from
168
- * the runtime's `ui/update-model-context` posts via `ggui_runtime_sync_context`.
169
- * Keyed by slot name; values are JSON-serializable per the slot's
170
- * declared schema. Last-write-wins (REPLACE semantics, never merge).
223
+ * the runtime's `ui/update-model-context` posts via
224
+ * `ggui_runtime_sync_context`. Last-write-wins (REPLACE semantics,
225
+ * never merge).
171
226
  *
172
227
  * Resume contract — when chat-history rehydrate re-mounts the
173
228
  * iframe, the resource handler seeds `contextSlots[i].default` with
174
229
  * `contextSnapshot[name]` (when present) instead of the
175
- * authoring-time default. Without this field, rehydrate loses the
176
- * user's interactive state (typed text, counter values, toggle
177
- * positions); with it, the user sees their last-known state.
230
+ * authoring-time default.
178
231
  *
179
232
  * Absent on initial render until the first context-update fires
180
- * from the runtime. The bootstrap-meta projection
181
- * (`deriveStackItemBootstrapView`) gracefully falls back to
182
- * `entry.default` per slot when this field is absent.
233
+ * from the runtime.
183
234
  */
184
- contextSnapshot?: JsonObject;
185
- createdAt: string;
235
+ readonly contextSnapshot?: JsonObject;
186
236
  }
187
237
  /**
188
- * Recognised system-card kinds the runtime renders via built-in
189
- * components. The wire is open-ended (string), but new kinds without a
190
- * matching renderer fall through to a generic "system message" card so
191
- * an old runtime + new server still produce something visible.
192
- */
193
- export type SystemStackItemKind = 'no-credentials' | 'mcp-apps-probe' | (string & {});
194
- /**
195
- * Stack item — server-emitted system card.
196
- *
197
- * The third StackItem variant. Used when the SERVER (not an LLM, not
198
- * an MCP host) needs to render a UI card the operator can act on:
199
- * "set up your LLM key", "rate limited, try later", "server is
200
- * misconfigured", etc.
238
+ * Render variant: server-emitted system card.
201
239
  *
202
- * Why a separate variant: prior to this, system cards were authored
203
- * as raw ESM source strings inlined alongside generated `componentCode`
204
- * (see deleted `CONNECT_CLAUDE_CARD_COMPONENT_CODE`). That pattern
205
- * bypasses TS, lint, and design-system co-evolution, and forces server
206
- * code to hand-emit `React.createElement` calls. The system variant
207
- * carries an opaque `kind` + structured `props`; the runtime maps the
208
- * kind to a real `.tsx` component bundled inside it.
240
+ * Used when the SERVER (not an LLM, not an MCP host) needs to render
241
+ * a UI card the operator can act on: "set up your LLM key", "rate
242
+ * limited, try later", "server is misconfigured", etc.
209
243
  *
210
244
  * Wire shape is intentionally tiny — server emits identifier + data,
211
- * not code.
245
+ * not code. The runtime maps `kind` to a built-in `.tsx` component.
212
246
  */
213
- export interface SystemStackItem<TProps extends JsonObject = JsonObject> {
214
- id: string;
215
- type: 'system';
247
+ export interface SystemRender<TProps extends JsonObject = JsonObject> extends RenderBase {
248
+ readonly type: 'system';
216
249
  /**
217
250
  * Identifier the runtime maps to a built-in renderer. Stable strings
218
251
  * (kebab-case) that survive runtime version skew — adding a new kind
219
252
  * never breaks an older runtime; it just falls through to a generic
220
253
  * card carrying the props as raw JSON.
221
254
  */
222
- kind: SystemStackItemKind;
255
+ readonly kind: SystemRenderKind;
223
256
  /** Props forwarded to the built-in renderer. */
224
- props?: TProps;
257
+ readonly props?: TProps;
225
258
  /** Optional descriptive text the server can attach (telemetry, logs). */
226
- description?: string;
227
- createdAt: string;
228
- }
229
- /**
230
- * Canonical session-stack entry — discriminated union over the three
231
- * stack-item variants ggui supports:
232
- *
233
- * - {@link StackItem} — generated / native component (has
234
- * `componentCode`, `actionSpec`, `streamSpec`, etc.).
235
- * - {@link McpAppsStackItem} — embedded third-party MCP App iframe.
236
- * - {@link SystemStackItem} — server-emitted system card (carries a
237
- * stable `kind` + props; runtime renders via a built-in
238
- * component).
239
- *
240
- * Narrowing pattern:
241
- * ```ts
242
- * if (entry.type === 'mcpApps') {
243
- * // McpAppsStackItem — render via host-role adapter
244
- * } else if (entry.type === 'system') {
245
- * // SystemStackItem — render via built-in card registry
246
- * } else {
247
- * // StackItem — render via DynamicComponent
248
- * }
249
- * ```
250
- *
251
- * Or use the {@link isMcpAppsStackItem} / component helpers exported
252
- * from `@ggui-ai/protocol/integrations/mcp-apps`.
253
- */
254
- export type SessionStackEntry<TProps = JsonObject> = StackItem<TProps> | McpAppsStackItem | SystemStackItem;
255
- /**
256
- * Adapter permission status
257
- */
258
- export type PermissionStatus = 'granted' | 'denied' | 'prompt';
259
- /**
260
- * Adapter permissions — per-session map keyed by Web Permissions API
261
- * name (see `KNOWN_PERMISSION_NAMES` in `validation/hygiene-rules.ts`).
262
- *
263
- * Keys derive from `KNOWN_PERMISSION_NAMES` so there's one source of
264
- * truth for permission identifiers across the protocol surface
265
- * (schema validation, hygiene rules, and runtime SDK cache).
266
- *
267
- * Pre-launch posture: no backward-compat alias for the old field
268
- * names — every consumer reads/writes through the runtime SDK
269
- * `requestPermission` call, which itself is gated against the same
270
- * enum.
271
- */
272
- export type AdapterPermissions = {
273
- readonly [K in (typeof KNOWN_PERMISSION_NAMES)[number]]?: PermissionStatus;
274
- };
275
- /**
276
- * Session state (internal — includes implementation details like connectionId)
277
- */
278
- export interface Session {
279
- id: string;
280
- appId: string;
281
- connectionId?: string;
282
- userId?: string;
283
- /** Full authenticated end-user identity (populated after auth gate) */
284
- endUserIdentity?: EndUserIdentity;
285
- /**
286
- * Theme preset id for this chat-scoped session. Set on
287
- * `ggui_new_session` from the agent's explicit `themeId` input or
288
- * the per-app `App.defaultThemeId` default. Read at every
289
- * `ggui_push` commit so the bootstrap-meta projection can resolve
290
- * the layered theme chain (see {@link StackItem.themeId} for the
291
- * full ordering). Absent ⇒ the chain falls through to the app /
292
- * server default at render time.
293
- */
294
- themeId?: string;
295
- stack: SessionStackEntry[];
296
- currentStackIndex: number;
297
- adapterPermissions: AdapterPermissions;
298
- eventSequence: number;
299
- createdAt: number;
300
- lastActivityAt: number;
301
- expiresAt: number;
302
- /**
303
- * Resolved session lifecycle status. SessionStore implementations
304
- * MAY populate this on `get` (e.g. InMemory + Sqlite compute from
305
- * internal `closed` flag + `expiresAt`; cloud Dynamo reads
306
- * `sessionStatus` row column). Absent = caller treats as
307
- * `'active'` — the legacy default for stores that don't surface
308
- * lifecycle state.
309
- */
310
- status?: 'active' | 'completed' | 'expired';
311
- /**
312
- * Latest `HostContextProjection` echoed from the iframe-runtime via
313
- * the live-channel `host_context_observed` message. Captured at iframe
314
- * `ui/initialize` time + updated on every spec-defined
315
- * `ui/notifications/host-context-changed` notification.
316
- *
317
- * Surfaced to the agent via `client.hostContext` on `ggui_handshake`
318
- * and `ggui_consume` output so the agent can reason about device
319
- * class, available display modes, and container dimensions on each
320
- * turn..
321
- *
322
- * Absent ⇒ the iframe has not echoed a HostContext yet (first push
323
- * before iframe mount, or non-spec-compliant host that doesn't
324
- * supply `McpUiHostContext`). Agent falls back to ggui's own
325
- * `InterfaceContext` in that case.
326
- */
327
- hostContext?: HostContextProjection;
328
- /**
329
- * Resolved MCP-Apps presentation mode for this session. Persisted at
330
- * `ggui_new_session` time from the app config's `defaultMcpAppsMode`
331
- * so mid-session app-config changes don't disrupt live sessions.
332
- *
333
- * - `'inline'` (default): each `ggui_push` returns its own ui://
334
- * resource. Today's behavior.
335
- * - `'canvas'`: a session-scoped iframe was minted by
336
- * `ggui_new_session`. `ggui_push` delivers via live-channel WS once
337
- * the canvas iframe completes its `ui/initialize` handshake (see
338
- * {@link canvasLoaded}).
339
- *
340
- * Absent ⇒ `'inline'` (zero-config default for legacy sessions).
341
- *
342
- */
343
- mcpAppsMode?: McpAppsMode;
344
- /**
345
- * Set to `true` when the canvas iframe (for `mcpAppsMode === 'canvas'`
346
- * sessions) has completed its `ui/initialize` handshake AND opened
347
- * its live-channel subscription. Read by `ggui_push` to decide whether
348
- * to deliver state via WS (canvas loaded) or return a per-push
349
- * resource (canvas not yet loaded → fallback to inline-style
350
- * delivery for that single push).
351
- *
352
- * Always undefined / false for `mcpAppsMode === 'inline'` sessions.
353
- *
354
- */
355
- canvasLoaded?: boolean;
356
- /**
357
- * Currently-active stack item id from the canvas's user nav stack.
358
- * Updated server-side on every `canvas_navigated` envelope from the
359
- * iframe. Drives `ggui_consume`'s active-pipe resolution (consumer
360
- * picks events targeting the active item, ignoring background
361
- * cold-gen for items the user has navigated away from) and gates
362
- * which in-flight `runGeneration` AbortSignals should fire.
363
- *
364
- * Distinct from `currentStackIndex`: that field tracks the SERVER's
365
- * stack mutation cursor (last push location); this tracks the
366
- * USER's navigation focus. They can diverge when the agent pushes
367
- * item C while the user is on item B — currentStackIndex points
368
- * to C, activeStackItemId stays B.
369
- *
370
- * Absent for inline sessions + the first push of a canvas session
371
- * before any `canvas_navigated` lands..
372
- */
373
- activeStackItemId?: string;
374
- }
375
- /**
376
- * Session view — clean protocol representation (no internal fields).
377
- * This is what agents see via getSession.
378
- */
379
- export interface SessionView {
380
- id: string;
381
- appId: string;
382
- status: 'active' | 'completed' | 'expired';
383
- stack: SessionStackEntry[];
384
- currentStackIndex: number;
385
- eventSequence: number;
386
- endUserIdentity?: EndUserIdentity;
387
- adapterPermissions?: AdapterPermissions;
388
- createdAt: string;
389
- lastActivityAt: string;
390
- expiresAt: string;
259
+ readonly description?: string;
391
260
  }
392
261
  /**
393
262
  * A single turn in the agent-user conversation.
394
- * Platforms SHOULD record conversation turns automatically and forward them to agents.
263
+ * Platforms SHOULD record conversation turns automatically and forward
264
+ * them to agents.
395
265
  *
396
- * Generic `TToolArgs` defaults to {@link JsonObject} for the tool call arguments type.
266
+ * Generic `TToolArgs` defaults to {@link JsonObject} for the tool call
267
+ * arguments type.
397
268
  */
398
269
  export interface ConversationTurn<TToolArgs = JsonObject> {
399
270
  role: 'user' | 'agent';
@@ -407,10 +278,13 @@ export interface ConversationTurn<TToolArgs = JsonObject> {
407
278
  /**
408
279
  * Progress update emitted by a producer during component production.
409
280
  * Delivered via the `ggui:logs` bridge event.
281
+ *
282
+ * Post-Phase-B: collapsed from `{ sessionId, stackItemId }` to a single
283
+ * `renderId` (the two identifiers were the same value once each render
284
+ * was its own thing).
410
285
  */
411
286
  export interface ProgressUpdate {
412
- sessionId: string;
413
- stackItemId: string;
287
+ renderId: string;
414
288
  step: 'queued' | 'negotiating' | 'matching' | 'generating' | 'compiling' | 'evaluating' | 'complete' | 'error';
415
289
  label: string;
416
290
  percent: number;