@ggui-ai/mcp-server 0.8.0 → 0.10.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 (67) hide show
  1. package/dist/api-renders-routes.d.ts +21 -0
  2. package/dist/api-renders-routes.d.ts.map +1 -1
  3. package/dist/api-renders-routes.js +54 -28
  4. package/dist/api-renders-stream-route.d.ts +80 -0
  5. package/dist/api-renders-stream-route.d.ts.map +1 -0
  6. package/dist/api-renders-stream-route.js +311 -0
  7. package/dist/build-mcp.d.ts +48 -7
  8. package/dist/build-mcp.d.ts.map +1 -1
  9. package/dist/build-mcp.js +87 -6
  10. package/dist/code-module-variant.d.ts +150 -0
  11. package/dist/code-module-variant.d.ts.map +1 -0
  12. package/dist/code-module-variant.js +243 -0
  13. package/dist/code-routes.d.ts +12 -2
  14. package/dist/code-routes.d.ts.map +1 -1
  15. package/dist/code-routes.js +12 -2
  16. package/dist/console-session-routes.d.ts.map +1 -1
  17. package/dist/console-session-routes.js +11 -0
  18. package/dist/control-service.d.ts +29 -3
  19. package/dist/control-service.d.ts.map +1 -1
  20. package/dist/control-service.js +26 -2
  21. package/dist/ggui-session-channel/action-ingress.d.ts +2 -2
  22. package/dist/ggui-session-channel/action-ingress.d.ts.map +1 -1
  23. package/dist/ggui-session-channel/channel-subscriptions.d.ts +4 -4
  24. package/dist/ggui-session-channel/channel-subscriptions.d.ts.map +1 -1
  25. package/dist/ggui-session-channel/internal-types.d.ts +63 -9
  26. package/dist/ggui-session-channel/internal-types.d.ts.map +1 -1
  27. package/dist/ggui-session-channel/outbound.d.ts +15 -5
  28. package/dist/ggui-session-channel/outbound.d.ts.map +1 -1
  29. package/dist/ggui-session-channel/outbound.js +64 -24
  30. package/dist/ggui-session-channel/socket-router.d.ts +8 -3
  31. package/dist/ggui-session-channel/socket-router.d.ts.map +1 -1
  32. package/dist/ggui-session-channel/socket-router.js +6 -1
  33. package/dist/ggui-session-channel/subscribe.d.ts +48 -3
  34. package/dist/ggui-session-channel/subscribe.d.ts.map +1 -1
  35. package/dist/ggui-session-channel/subscribe.js +97 -36
  36. package/dist/ggui-session-channel/subscriber-lifecycle.d.ts +23 -13
  37. package/dist/ggui-session-channel/subscriber-lifecycle.d.ts.map +1 -1
  38. package/dist/ggui-session-channel/subscriber-lifecycle.js +24 -11
  39. package/dist/ggui-session-channel.d.ts +58 -11
  40. package/dist/ggui-session-channel.d.ts.map +1 -1
  41. package/dist/ggui-session-channel.js +55 -19
  42. package/dist/health-routes.d.ts +19 -3
  43. package/dist/health-routes.d.ts.map +1 -1
  44. package/dist/health-routes.js +26 -18
  45. package/dist/index.d.ts +6 -3
  46. package/dist/index.d.ts.map +1 -1
  47. package/dist/index.js +13 -1
  48. package/dist/instructions-presets.js +10 -10
  49. package/dist/mcp-apps-outbound.d.ts +88 -11
  50. package/dist/mcp-apps-outbound.d.ts.map +1 -1
  51. package/dist/mcp-apps-outbound.js +470 -77
  52. package/dist/mcp-endpoint-routes.d.ts +23 -5
  53. package/dist/mcp-endpoint-routes.d.ts.map +1 -1
  54. package/dist/mcp-endpoint-routes.js +69 -1
  55. package/dist/oauth-as-routes.d.ts +11 -0
  56. package/dist/oauth-as-routes.d.ts.map +1 -1
  57. package/dist/oauth-as-routes.js +45 -1
  58. package/dist/oauth.d.ts.map +1 -1
  59. package/dist/oauth.js +8 -1
  60. package/dist/runtime-bundle-hash.d.ts +55 -0
  61. package/dist/runtime-bundle-hash.d.ts.map +1 -0
  62. package/dist/runtime-bundle-hash.js +85 -0
  63. package/dist/runtime-bundle-route.js +1 -1
  64. package/dist/server.d.ts +239 -61
  65. package/dist/server.d.ts.map +1 -1
  66. package/dist/server.js +355 -143
  67. package/package.json +13 -12
@@ -20,11 +20,13 @@
20
20
  * mutation handlers (ggui_emit / connector `ctx.send`). Validated
21
21
  * through `assertStreamContract` before delivery.
22
22
  *
23
- * `props_update`: the agent-driven `ggui_update` handler calls
24
- * `channel.sendPropsUpdate(sessionId, props)` (wired as its
25
- * `propsUpdateNotifier`) to fan a `{type:'props_update'}` frame to
26
- * live subscribers. Reaches the renderer's existing `props_update`
27
- * branch in `iframe-runtime` and applies new props in-place.
23
+ * `props_update`: the agent-driven `ggui_update` / `ggui_amend`
24
+ * handlers call `channel.sendPropsUpdate(sessionId, props, epoch)`
25
+ * (wired as their `propsUpdateNotifier`) to fan a
26
+ * `{type:'props_update'}` frame to live subscribers. Reaches the
27
+ * renderer's `props_update` branch in `iframe-runtime`, which applies
28
+ * new props in-place — or freezes the mount when the frame's `epoch`
29
+ * exceeds its own (#483 freeze latch).
28
30
  *
29
31
  * Not handled here:
30
32
  *
@@ -109,6 +111,19 @@ export const DEFAULT_PRE_SUBSCRIBE_IDLE_MS = 30_000;
109
111
  * Disable with `0`.
110
112
  */
111
113
  export const DEFAULT_MAX_PRE_SUBSCRIBE_CONNECTIONS = 1_024;
114
+ /**
115
+ * Thrown by {@link GguiSessionChannelServer.attachExternalSubscriber}
116
+ * when the target render does not exist OR is bound to a different
117
+ * app than the caller claims. Callers that pre-gated (the SSE route
118
+ * does) treat this as a lost race with eviction; callers that did not
119
+ * MUST map it to their 404 surface.
120
+ */
121
+ export class ChannelSessionNotFoundError extends Error {
122
+ constructor(sessionId) {
123
+ super(`GguiSession '${sessionId}' not found (or bound to a different app)`);
124
+ this.name = "ChannelSessionNotFoundError";
125
+ }
126
+ }
112
127
  /**
113
128
  * Build an OSS live-channel server. The returned object is designed to be
114
129
  * composed into `createGguiServer` — see `server.ts` for the wire-up.
@@ -141,13 +156,18 @@ export function createGguiSessionChannelServer(opts) {
141
156
  ? new WebSocketServer({ noServer: true, maxPayload: wsMaxPayloadBytes })
142
157
  : new WebSocketServer({ noServer: true });
143
158
  /**
144
- * Flat set of all live WS subscribers. Replaces the per-render
145
- * `subscribersByRender` Map — routing is now StreamFanout's job;
146
- * this set tracks WS-specific bookkeeping (stats, shutdown-broadcast)
147
- * that the seam can't see (and shouldn't).
159
+ * Flat set of all live subscribers — WS-attached AND externally-
160
+ * attached (SSE). Replaces the per-render `subscribersByRender` Map —
161
+ * routing is now StreamFanout's job; this set tracks channel-level
162
+ * bookkeeping (stats, shutdown-broadcast) that the seam can't see
163
+ * (and shouldn't).
148
164
  */
149
165
  const wsSubscribers = new Set();
150
- /** ws → subscriber reverse index so socket-close can look up cheaply. */
166
+ /**
167
+ * ws → subscriber reverse index so socket-close / inbound dispatch
168
+ * can look up cheaply. WS-only: the lifecycle module populates it
169
+ * for `transport: 'ws'` subscribers only.
170
+ */
151
171
  const subscribersByWs = new WeakMap();
152
172
  // Send / fan-out family — wire-write primitives shared by every
153
173
  // handler module plus the public fan-out surfaces the returned
@@ -169,7 +189,6 @@ export function createGguiSessionChannelServer(opts) {
169
189
  logger: opts.logger,
170
190
  wsSubscribers,
171
191
  subscribersByWs,
172
- send,
173
192
  onFirstSubscriber: opts.onFirstSubscriber,
174
193
  onLastSubscriberGone: opts.onLastSubscriberGone,
175
194
  });
@@ -197,7 +216,7 @@ export function createGguiSessionChannelServer(opts) {
197
216
  // Subscribe / credential-path family — upgrade-time identity
198
217
  // resolution (bearer / bootstrap / console cookie) + the
199
218
  // `subscribe` handler. See `ggui-session-channel/subscribe.ts`.
200
- const { resolveIdentityFromUpgrade, handleSubscribe } = createSubscribeHandlers({
219
+ const { resolveIdentityFromUpgrade, handleSubscribe, completeSubscribe } = createSubscribeHandlers({
201
220
  logger: opts.logger,
202
221
  auth: opts.auth,
203
222
  renderStore: opts.renderStore,
@@ -207,9 +226,9 @@ export function createGguiSessionChannelServer(opts) {
207
226
  cookieAuth: opts.cookieAuth,
208
227
  appIdFromIdentity: opts.appIdFromIdentity,
209
228
  versionPolicy: opts.versionPolicy,
210
- send,
211
229
  sendError,
212
230
  register,
231
+ unregister,
213
232
  });
214
233
  // WS lifecycle / message routing — the `connection` handler with
215
234
  // the per-socket inbound ordering chain, pre-subscribe identity +
@@ -272,6 +291,24 @@ export function createGguiSessionChannelServer(opts) {
272
291
  sendPropsUpdate: outbound.sendPropsUpdate,
273
292
  sendDrainAck: outbound.sendDrainAck,
274
293
  externalBroadcast: outbound.externalBroadcast,
294
+ async attachExternalSubscriber(args) {
295
+ // Defense-in-depth re-check — the transport route already gated
296
+ // on the wsToken's (sessionId, appId) claims, but this method is
297
+ // a public surface: never register a subscriber onto a render
298
+ // that no longer exists or belongs to a different app.
299
+ const stored = await opts.renderStore.get(args.sessionId);
300
+ if (!stored || stored.appId !== args.appId) {
301
+ throw new ChannelSessionNotFoundError(args.sessionId);
302
+ }
303
+ return completeSubscribe({
304
+ stored,
305
+ identity: args.identity,
306
+ sink: args.sink,
307
+ transport: "sse",
308
+ ...(args.sinceSequence !== undefined ? { sinceSequence: args.sinceSequence } : {}),
309
+ ...(args.fromSeq !== undefined ? { fromSeq: args.fromSeq } : {}),
310
+ });
311
+ },
275
312
  get subscriberCount() {
276
313
  return wsSubscribers.size;
277
314
  },
@@ -312,12 +349,11 @@ export function createGguiSessionChannelServer(opts) {
312
349
  const sessionIds = new Set();
313
350
  for (const sub of wsSubscribers) {
314
351
  sessionIds.add(sub.sessionId);
315
- try {
316
- sub.ws.close(1012, "service_restart");
317
- }
318
- catch {
319
- /* best-effort */
320
- }
352
+ // Transport-neutral shutdown: WS maps to close(1012), SSE ends
353
+ // the response so EventSource auto-reconnects to the new pod —
354
+ // exactly the 1012 semantics. `sink.end` is best-effort by
355
+ // contract (never throws on an already-closing transport).
356
+ sub.sink.end("service_restart");
321
357
  void sub.iter.return?.();
322
358
  }
323
359
  wsSubscribers.clear();
@@ -5,9 +5,10 @@
5
5
  * loop can run a handler, regardless of
6
6
  * readiness).
7
7
  * GET /ggui/health — readiness probe; runs the operator-supplied
8
- * readiness checks and reports per-check
9
- * results, live-channel counts, and the
10
- * thread-transport durability claim.
8
+ * readiness checks, advisory checks, and
9
+ * reports per-check results, live-channel
10
+ * counts, and the thread-transport durability
11
+ * claim.
11
12
  * GET /ggui/auth-check — authenticated liveness. 204 when the bearer
12
13
  * resolves via the configured AuthAdapter,
13
14
  * 401 otherwise (with `WWW-Authenticate` when
@@ -44,6 +45,21 @@ interface MountOptions {
44
45
  readonly name: string;
45
46
  readonly check: () => boolean | Promise<boolean>;
46
47
  }>;
48
+ /**
49
+ * Reported but non-gating checks — surfaced under `body.advisoryChecks`
50
+ * in the `/ggui/health` response with the SAME per-check boolean
51
+ * shape as `readinessChecks`, but a failing advisory check never
52
+ * flips `status` or the HTTP status code. Use this for a dependency
53
+ * whose health is worth reporting for diagnostics but that MUST NOT
54
+ * affect readiness on its own — e.g. a best-effort side channel
55
+ * every consumer already degrades gracefully around when the
56
+ * dependency is unavailable. Same 1s per-check timeout as
57
+ * `readinessChecks`; a timeout or thrown check reports `false`.
58
+ */
59
+ readonly advisoryChecks?: ReadonlyArray<{
60
+ readonly name: string;
61
+ readonly check: () => boolean | Promise<boolean>;
62
+ }>;
47
63
  /**
48
64
  * Late-bound live-channel reference. The channel is constructed
49
65
  * AFTER these routes mount (declaration-order in the composer), so
@@ -1 +1 @@
1
- {"version":3,"file":"health-routes.d.ts","sourceRoot":"","sources":["../src/health-routes.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA4BG;AAEH,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,0BAA0B,CAAC;AAC5D,OAAO,KAAK,EAAE,OAAO,EAAqB,MAAM,SAAS,CAAC;AAE1D,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,gBAAgB,CAAC;AACjD,OAAO,KAAK,EAAE,wBAAwB,EAAE,MAAM,2BAA2B,CAAC;AAC1E,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,aAAa,CAAC;AAU1C,UAAU,YAAY;IACpB,iCAAiC;IACjC,QAAQ,CAAC,GAAG,EAAE,OAAO,CAAC;IACtB,+DAA+D;IAC/D,QAAQ,CAAC,IAAI,EAAE,UAAU,CAAC;IAC1B,wDAAwD;IACxD,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;IAC3B,iEAAiE;IACjE,QAAQ,CAAC,eAAe,EAAE,aAAa,CAAC;QACtC,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;QACtB,QAAQ,CAAC,KAAK,EAAE,MAAM,OAAO,GAAG,OAAO,CAAC,OAAO,CAAC,CAAC;KAClD,CAAC,CAAC;IACH;;;;OAIG;IACH,QAAQ,CAAC,UAAU,EAAE,MAAM,wBAAwB,GAAG,IAAI,CAAC;IAC3D;;;;OAIG;IACH,QAAQ,CAAC,OAAO,CAAC,EAAE;QAAE,QAAQ,CAAC,UAAU,CAAC,EAAE,SAAS,GAAG,WAAW,CAAA;KAAE,CAAC;IACrE,gEAAgE;IAChE,QAAQ,CAAC,IAAI,EAAE,WAAW,CAAC;IAC3B,iEAAiE;IACjE,QAAQ,CAAC,YAAY,EAAE,OAAO,CAAC;IAC/B,uDAAuD;IACvD,QAAQ,CAAC,cAAc,CAAC,EAAE,MAAM,CAAC;IACjC,4DAA4D;IAC5D,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB;gEAC4D;IAC5D,QAAQ,CAAC,WAAW,CAAC,EAAE,aAAa,CAAC,MAAM,CAAC,CAAC;CAC9C;AAED;;;GAGG;AACH,wBAAgB,iBAAiB,CAAC,IAAI,EAAE,YAAY,GAAG,IAAI,CAuI1D"}
1
+ {"version":3,"file":"health-routes.d.ts","sourceRoot":"","sources":["../src/health-routes.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA6BG;AAEH,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,0BAA0B,CAAC;AAC5D,OAAO,KAAK,EAAE,OAAO,EAAqB,MAAM,SAAS,CAAC;AAE1D,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,gBAAgB,CAAC;AACjD,OAAO,KAAK,EAAE,wBAAwB,EAAE,MAAM,2BAA2B,CAAC;AAC1E,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,aAAa,CAAC;AAW1C,UAAU,YAAY;IACpB,iCAAiC;IACjC,QAAQ,CAAC,GAAG,EAAE,OAAO,CAAC;IACtB,+DAA+D;IAC/D,QAAQ,CAAC,IAAI,EAAE,UAAU,CAAC;IAC1B,wDAAwD;IACxD,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;IAC3B,iEAAiE;IACjE,QAAQ,CAAC,eAAe,EAAE,aAAa,CAAC;QACtC,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;QACtB,QAAQ,CAAC,KAAK,EAAE,MAAM,OAAO,GAAG,OAAO,CAAC,OAAO,CAAC,CAAC;KAClD,CAAC,CAAC;IACH;;;;;;;;;;OAUG;IACH,QAAQ,CAAC,cAAc,CAAC,EAAE,aAAa,CAAC;QACtC,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;QACtB,QAAQ,CAAC,KAAK,EAAE,MAAM,OAAO,GAAG,OAAO,CAAC,OAAO,CAAC,CAAC;KAClD,CAAC,CAAC;IACH;;;;OAIG;IACH,QAAQ,CAAC,UAAU,EAAE,MAAM,wBAAwB,GAAG,IAAI,CAAC;IAC3D;;;;OAIG;IACH,QAAQ,CAAC,OAAO,CAAC,EAAE;QAAE,QAAQ,CAAC,UAAU,CAAC,EAAE,SAAS,GAAG,WAAW,CAAA;KAAE,CAAC;IACrE,gEAAgE;IAChE,QAAQ,CAAC,IAAI,EAAE,WAAW,CAAC;IAC3B,iEAAiE;IACjE,QAAQ,CAAC,YAAY,EAAE,OAAO,CAAC;IAC/B,uDAAuD;IACvD,QAAQ,CAAC,cAAc,CAAC,EAAE,MAAM,CAAC;IACjC,4DAA4D;IAC5D,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB;gEAC4D;IAC5D,QAAQ,CAAC,WAAW,CAAC,EAAE,aAAa,CAAC,MAAM,CAAC,CAAC;CAC9C;AAED;;;GAGG;AACH,wBAAgB,iBAAiB,CAAC,IAAI,EAAE,YAAY,GAAG,IAAI,CAmJ1D"}
@@ -5,9 +5,10 @@
5
5
  * loop can run a handler, regardless of
6
6
  * readiness).
7
7
  * GET /ggui/health — readiness probe; runs the operator-supplied
8
- * readiness checks and reports per-check
9
- * results, live-channel counts, and the
10
- * thread-transport durability claim.
8
+ * readiness checks, advisory checks, and
9
+ * reports per-check results, live-channel
10
+ * counts, and the thread-transport durability
11
+ * claim.
11
12
  * GET /ggui/auth-check — authenticated liveness. 204 when the bearer
12
13
  * resolves via the configured AuthAdapter,
13
14
  * 401 otherwise (with `WWW-Authenticate` when
@@ -31,8 +32,9 @@ import { resolveIdentity, UnauthenticatedError } from "./auth.js";
31
32
  import { buildWwwAuthenticate, resolveIssuerUrl } from "./oauth.js";
32
33
  /**
33
34
  * 1s per-check timeout — a hung dependency must not block the K8s
34
- * liveness probe, which itself runs on a short period. A timeout is
35
- * treated as a failed check; the dependency is degraded either way.
35
+ * readiness probe (/ggui/health), which itself runs on a short period.
36
+ * A timeout is treated as a failed check; the dependency is degraded
37
+ * either way.
36
38
  */
37
39
  const READINESS_CHECK_TIMEOUT_MS = 1_000;
38
40
  /**
@@ -40,29 +42,29 @@ const READINESS_CHECK_TIMEOUT_MS = 1_000;
40
42
  * express app. Returns nothing — the routes self-register.
41
43
  */
42
44
  export function mountHealthRoutes(opts) {
43
- const { app, info, toolCount, readinessChecks, getChannel, threads, auth, oauthEnabled, logger, corsOrigins, } = opts;
44
- async function runReadinessChecks() {
45
- if (readinessChecks.length === 0) {
46
- return { allReady: true, results: {} };
45
+ const { app, info, toolCount, readinessChecks, advisoryChecks, getChannel, threads, auth, oauthEnabled, logger, corsOrigins, } = opts;
46
+ async function runChecks(checks) {
47
+ if (checks.length === 0) {
48
+ return { allOk: true, results: {} };
47
49
  }
48
50
  const results = {};
49
- let allReady = true;
50
- await Promise.all(readinessChecks.map(async ({ name, check }) => {
51
+ let allOk = true;
52
+ await Promise.all(checks.map(async ({ name, check }) => {
51
53
  try {
52
- const ready = await Promise.race([
54
+ const ok = await Promise.race([
53
55
  Promise.resolve().then(() => check()),
54
56
  new Promise((resolve) => setTimeout(() => resolve(false), READINESS_CHECK_TIMEOUT_MS)),
55
57
  ]);
56
- results[name] = ready;
57
- if (!ready)
58
- allReady = false;
58
+ results[name] = ok;
59
+ if (!ok)
60
+ allOk = false;
59
61
  }
60
62
  catch {
61
63
  results[name] = false;
62
- allReady = false;
64
+ allOk = false;
63
65
  }
64
66
  }));
65
- return { allReady, results };
67
+ return { allOk, results };
66
68
  }
67
69
  // No body needed for the kubelet's HTTP check (status code is the
68
70
  // signal), but a tiny JSON keeps the endpoint debuggable from a
@@ -73,7 +75,10 @@ export function mountHealthRoutes(opts) {
73
75
  });
74
76
  app.get("/ggui/health", (_req, res) => {
75
77
  void (async () => {
76
- const { allReady, results } = await runReadinessChecks();
78
+ const [{ allOk: allReady, results }, { results: advisoryResults },] = await Promise.all([
79
+ runChecks(readinessChecks),
80
+ runChecks(advisoryChecks ?? []),
81
+ ]);
77
82
  const body = {
78
83
  status: allReady ? "ok" : "degraded",
79
84
  server: info.name,
@@ -111,6 +116,9 @@ export function mountHealthRoutes(opts) {
111
116
  if (Object.keys(results).length > 0) {
112
117
  body.checks = results;
113
118
  }
119
+ if (Object.keys(advisoryResults).length > 0) {
120
+ body.advisoryChecks = advisoryResults;
121
+ }
114
122
  res.status(allReady ? 200 : 503).json(body);
115
123
  })();
116
124
  });
package/dist/index.d.ts CHANGED
@@ -30,9 +30,12 @@ export type { GadgetDescriptor, McpUiDisplayMode, GguiSession, SystemGguiSession
30
30
  export type { GenerationDeps } from '@ggui-ai/mcp-server-handlers';
31
31
  export { buildNoCredentialsGguiSession } from '@ggui-ai/mcp-server-handlers';
32
32
  export { buildOpsBundleHandlers, createGguiServer, defaultHandlers } from './server.js';
33
- export type { CreateGguiServerOptions, GguiServer, OpsBundleDeps, } from './server.js';
33
+ export type { CreateGguiServerOptions, ErrorMapperResult, GguiServer, OpsBundleDeps, } from './server.js';
34
34
  export { buildControlService, CONTROL_PATH, filterHandlersByAudience, SINGLE_CALL_OPS, } from './control-service.js';
35
35
  export type { AudienceTag, BuildControlServiceArgs } from './control-service.js';
36
+ export { computeRuntimeBundleHash, insertRuntimeBundleHash, resolveHashedRuntimeBundleUrl, resolveRuntimeBundleHash, } from './runtime-bundle-hash.js';
37
+ export { createCodeModuleUrlMinter, composeCodeModuleUrl, } from './code-module-variant.js';
38
+ export type { MintCodeModuleUrl } from './code-module-variant.js';
36
39
  export { FileSystemCodeStore } from './code-store-fs.js';
37
40
  export type { FileSystemCodeStoreOptions } from './code-store-fs.js';
38
41
  export type { McpServerMount } from './mcp-mounts.js';
@@ -45,8 +48,8 @@ export type { ServerInfo } from './build-mcp.js';
45
48
  export { UnauthenticatedError, DEFAULT_BUILDER_APP_ID, defaultAppIdFromIdentity, } from './auth.js';
46
49
  export { createConsoleLogger } from './logger.js';
47
50
  export type { Logger } from './logger.js';
48
- export { createGguiSessionChannelServer, DEFAULT_RENDER_CHANNEL_PATH, } from './ggui-session-channel.js';
49
- export type { GguiSessionChannelOptions, GguiSessionChannelServer, } from './ggui-session-channel.js';
51
+ export { ChannelSessionNotFoundError, createGguiSessionChannelServer, DEFAULT_RENDER_CHANNEL_PATH, } from './ggui-session-channel.js';
52
+ export type { GguiSessionChannelOptions, GguiSessionChannelServer, SubscriberSink, } from './ggui-session-channel.js';
50
53
  export { resolveStorageFromConfig } from './storage.js';
51
54
  export type { ResolveStorageFromConfigOptions, ResolvedStorageStores, } from './storage.js';
52
55
  export { DEFAULT_PAIRING_ADMIN_INIT_PATH, DEFAULT_PAIRING_PATH, mountPairingTransport, } from './pairing-transport.js';
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;GA0BG;AAEH,YAAY,EAAE,cAAc,EAAE,aAAa,EAAE,MAAM,8BAA8B,CAAC;AAKlF,YAAY,EACV,gBAAgB,EAChB,gBAAgB,EAChB,WAAW,EACX,iBAAiB,GAClB,MAAM,mBAAmB,CAAC;AAC3B,YAAY,EAAE,cAAc,EAAE,MAAM,8BAA8B,CAAC;AAKnE,OAAO,EAAE,6BAA6B,EAAE,MAAM,8BAA8B,CAAC;AAC7E,OAAO,EAAE,sBAAsB,EAAE,gBAAgB,EAAE,eAAe,EAAE,MAAM,aAAa,CAAC;AACxF,YAAY,EACV,uBAAuB,EACvB,iBAAiB,EACjB,UAAU,EACV,aAAa,GACd,MAAM,aAAa,CAAC;AAMrB,OAAO,EACL,mBAAmB,EACnB,YAAY,EACZ,wBAAwB,EACxB,eAAe,GAChB,MAAM,sBAAsB,CAAC;AAC9B,YAAY,EAAE,WAAW,EAAE,uBAAuB,EAAE,MAAM,sBAAsB,CAAC;AAMjF,OAAO,EACL,wBAAwB,EACxB,uBAAuB,EACvB,6BAA6B,EAC7B,wBAAwB,GACzB,MAAM,0BAA0B,CAAC;AAMlC,OAAO,EACL,yBAAyB,EACzB,oBAAoB,GACrB,MAAM,0BAA0B,CAAC;AAClC,YAAY,EAAE,iBAAiB,EAAE,MAAM,0BAA0B,CAAC;AAIlE,OAAO,EAAE,mBAAmB,EAAE,MAAM,oBAAoB,CAAC;AACzD,YAAY,EAAE,0BAA0B,EAAE,MAAM,oBAAoB,CAAC;AAKrE,YAAY,EAAE,cAAc,EAAE,MAAM,iBAAiB,CAAC;AAMtD,YAAY,EAAE,UAAU,EAAE,WAAW,EAAE,MAAM,iBAAiB,CAAC;AAC/D,OAAO,EACL,mBAAmB,EACnB,uBAAuB,EACvB,mBAAmB,GACpB,MAAM,iBAAiB,CAAC;AAOzB,OAAO,EACL,+BAA+B,EAC/B,uBAAuB,GACxB,MAAM,0BAA0B,CAAC;AAKlC,OAAO,EACL,uBAAuB,EACvB,0BAA0B,EAC1B,eAAe,EACf,iBAAiB,GAClB,MAAM,oBAAoB,CAAC;AAC5B,YAAY,EACV,mBAAmB,EACnB,gBAAgB,EAChB,kBAAkB,EAClB,wBAAwB,EACxB,aAAa,GACd,MAAM,oBAAoB,CAAC;AAC5B,YAAY,EAAE,UAAU,EAAE,MAAM,gBAAgB,CAAC;AACjD,OAAO,EACL,oBAAoB,EACpB,sBAAsB,EACtB,wBAAwB,GACzB,MAAM,WAAW,CAAC;AACnB,OAAO,EAAE,mBAAmB,EAAE,MAAM,aAAa,CAAC;AAClD,YAAY,EAAE,MAAM,EAAE,MAAM,aAAa,CAAC;AAC1C,OAAO,EACL,2BAA2B,EAC3B,8BAA8B,EAC9B,2BAA2B,GAC5B,MAAM,2BAA2B,CAAC;AACnC,YAAY,EACV,yBAAyB,EACzB,wBAAwB,EACxB,cAAc,GACf,MAAM,2BAA2B,CAAC;AACnC,OAAO,EAAE,wBAAwB,EAAE,MAAM,cAAc,CAAC;AACxD,YAAY,EACV,+BAA+B,EAC/B,qBAAqB,GACtB,MAAM,cAAc,CAAC;AACtB,OAAO,EACL,+BAA+B,EAC/B,oBAAoB,EACpB,qBAAqB,GACtB,MAAM,wBAAwB,CAAC;AAChC,YAAY,EAAE,uBAAuB,EAAE,MAAM,wBAAwB,CAAC;AAKtE,OAAO,EACL,wBAAwB,EACxB,4BAA4B,EAC5B,oBAAoB,EACpB,wBAAwB,EACxB,6BAA6B,EAC7B,qBAAqB,EACrB,gCAAgC,GACjC,MAAM,wBAAwB,CAAC;AAChC,YAAY,EAAE,4BAA4B,EAAE,MAAM,wBAAwB,CAAC;AAK3E,OAAO,EACL,kCAAkC,EAClC,eAAe,GAChB,MAAM,4BAA4B,CAAC;AACpC,YAAY,EAAE,yBAAyB,EAAE,MAAM,4BAA4B,CAAC;AAM5E,OAAO,EACL,gBAAgB,EAChB,yBAAyB,EACzB,uBAAuB,EACvB,oBAAoB,EACpB,aAAa,EACb,mBAAmB,GACpB,MAAM,sBAAsB,CAAC;AAC9B,YAAY,EACV,qBAAqB,EACrB,kBAAkB,EAClB,0BAA0B,GAC3B,MAAM,sBAAsB,CAAC;AAC9B,OAAO,EAAE,+BAA+B,EAAE,MAAM,kCAAkC,CAAC;AACnF,YAAY,EAAE,gCAAgC,EAAE,MAAM,kCAAkC,CAAC;AAKzF,OAAO,EACL,kBAAkB,GACnB,MAAM,wBAAwB,CAAC;AAChC,YAAY,EACV,kBAAkB,EAClB,iBAAiB,EACjB,iBAAiB,EACjB,mBAAmB,EACnB,yBAAyB,GAC1B,MAAM,wBAAwB,CAAC;AAChC,OAAO,EACL,wBAAwB,EACxB,2BAA2B,EAC3B,iCAAiC,EACjC,sBAAsB,EACtB,qBAAqB,GACtB,MAAM,kBAAkB,CAAC;AAC1B,OAAO,EACL,8BAA8B,EAC9B,+BAA+B,EAC/B,+BAA+B,EAC/B,kBAAkB,EAClB,sBAAsB,EACtB,qBAAqB,GACtB,MAAM,kBAAkB,CAAC;AAC1B,YAAY,EACV,WAAW,EACX,YAAY,EACZ,cAAc,EACd,eAAe,EACf,cAAc,EACd,uBAAuB,GACxB,MAAM,kBAAkB,CAAC;AAC1B,OAAO,EAAE,iBAAiB,EAAE,MAAM,mBAAmB,CAAC;AACtD,YAAY,EAAE,wBAAwB,EAAE,MAAM,mBAAmB,CAAC;AAClE,OAAO,EAAE,eAAe,EAAE,MAAM,iBAAiB,CAAC;AAClD,YAAY,EAAE,sBAAsB,EAAE,MAAM,iBAAiB,CAAC;AAC9D,OAAO,EAAE,wBAAwB,EAAE,MAAM,4BAA4B,CAAC;AACtE,YAAY,EACV,eAAe,EACf,oBAAoB,EACpB,wBAAwB,GACzB,MAAM,4BAA4B,CAAC;AACpC,OAAO,EACL,wBAAwB,EACxB,sBAAsB,GACvB,MAAM,2BAA2B,CAAC;AACnC,YAAY,EACV,qBAAqB,EACrB,oBAAoB,GACrB,MAAM,2BAA2B,CAAC;AACnC,YAAY,EAAE,uBAAuB,EAAE,MAAM,kBAAkB,CAAC;AAChE,OAAO,EAAE,mBAAmB,EAAE,MAAM,6BAA6B,CAAC;AAClE,YAAY,EAAE,0BAA0B,EAAE,MAAM,6BAA6B,CAAC;AAC9E,OAAO,EAAE,mBAAmB,EAAE,MAAM,6BAA6B,CAAC;AAClE,YAAY,EAAE,0BAA0B,EAAE,MAAM,6BAA6B,CAAC;AAC9E,OAAO,EAAE,yBAAyB,EAAE,MAAM,4BAA4B,CAAC;AACvE,YAAY,EACV,mBAAmB,EACnB,0BAA0B,EAC1B,QAAQ,IAAI,2BAA2B,GACxC,MAAM,4BAA4B,CAAC;AACpC,OAAO,EACL,kCAAkC,EAClC,iCAAiC,GAClC,MAAM,sCAAsC,CAAC;AAC9C,YAAY,EAAE,mCAAmC,EAAE,MAAM,sCAAsC,CAAC;AAIhG,YAAY,EACV,oBAAoB,EACpB,OAAO,EACP,iBAAiB,EACjB,WAAW,EACX,cAAc,EACd,gBAAgB,GACjB,MAAM,0BAA0B,CAAC;AAClC,OAAO,EACL,wBAAwB,EACxB,oBAAoB,EACpB,8BAA8B,EAC9B,oBAAoB,GACrB,MAAM,uBAAuB,CAAC;AAC/B,YAAY,EACV,mBAAmB,EACnB,sBAAsB,GACvB,MAAM,uBAAuB,CAAC;AAO/B,OAAO,EAAE,sBAAsB,EAAE,MAAM,oCAAoC,CAAC;AAC5E,YAAY,EAAE,cAAc,EAAE,MAAM,0BAA0B,CAAC;AAS/D,OAAO,EAAE,mBAAmB,EAAE,MAAM,oCAAoC,CAAC;AACzE,YAAY,EAAE,0BAA0B,EAAE,MAAM,oCAAoC,CAAC;AAQrF,OAAO,EACL,sBAAsB,EACtB,kBAAkB,GACnB,MAAM,oCAAoC,CAAC;AAC5C,YAAY,EACV,6BAA6B,EAC7B,yBAAyB,GAC1B,MAAM,oCAAoC,CAAC;AAC5C,YAAY,EAAE,WAAW,EAAE,UAAU,EAAE,MAAM,0BAA0B,CAAC;AASxE,OAAO,EAAE,yBAAyB,EAAE,MAAM,oCAAoC,CAAC;AAC/E,YAAY,EACV,qBAAqB,EACrB,gCAAgC,GACjC,MAAM,oCAAoC,CAAC;AAC5C,YAAY,EAAE,iBAAiB,EAAE,MAAM,0BAA0B,CAAC;AAOlE,YAAY,EAAE,0BAA0B,EAAE,MAAM,8BAA8B,CAAC;AAU/E,YAAY,EAAE,WAAW,EAAE,MAAM,8BAA8B,CAAC;AAOhE,YAAY,EAAE,cAAc,EAAE,MAAM,yBAAyB,CAAC;AAU9D,YAAY,EACV,WAAW,EACX,iBAAiB,GAClB,MAAM,2BAA2B,CAAC;AAQnC,YAAY,EACV,WAAW,EACX,QAAQ,EACR,YAAY,EACZ,cAAc,EACd,eAAe,EACf,gBAAgB,EAChB,WAAW,EACX,aAAa,EACb,iBAAiB,EACjB,kBAAkB,GACnB,MAAM,0BAA0B,CAAC;AAKlC,OAAO,EACL,mBAAmB,EACnB,oBAAoB,EACpB,kBAAkB,GACnB,MAAM,0BAA0B,CAAC"}
package/dist/index.js CHANGED
@@ -37,6 +37,18 @@ export { buildOpsBundleHandlers, createGguiServer, defaultHandlers } from './ser
37
37
  // mounts it automatically; the pieces are exported so deployments can
38
38
  // assert their own control surface at boot.
39
39
  export { buildControlService, CONTROL_PATH, filterHandlersByAudience, SINGLE_CALL_OPS, } from './control-service.js';
40
+ // Content-hashed runtime-bundle URL naming (#472). `createGguiServer`
41
+ // stamps the hashed name itself; these are exported for deployments
42
+ // that compose their own render handler (replacing the factory's
43
+ // default set) and therefore mint an absolute `runtimeUrl` directly —
44
+ // one derivation, no drift against the route the factory mounts.
45
+ export { computeRuntimeBundleHash, insertRuntimeBundleHash, resolveHashedRuntimeBundleUrl, resolveRuntimeBundleHash, } from './runtime-bundle-hash.js';
46
+ // Strict-CSP module-variant delivery (ggui#522 slice 2). The minter is
47
+ // exported for the same composed-handler deployments: a pod that binds
48
+ // `codeStore`/`codeBaseUrl` on its own render tool builds the minter
49
+ // from `resolveRuntimeBundleHash()` so its `codeModuleUrl`s name the
50
+ // exact variant family the co-resident factory routes serve.
51
+ export { createCodeModuleUrlMinter, composeCodeModuleUrl, } from './code-module-variant.js';
40
52
  // Content-addressable code delivery (2026-05-03). FileSystemCodeStore
41
53
  // is the OSS dev default; in-memory variant ships in
42
54
  // `@ggui-ai/mcp-server-core/in-memory` for tests + ephemeral runs.
@@ -56,7 +68,7 @@ export { composePreviewReservedValidator, mergeReservedValidators, } from './res
56
68
  export { checkRenderSchemaCompat, DEFAULT_SCHEMA_COMPAT_MODE, hasErrorFinding, SchemaCompatError, } from './schema-compat.js';
57
69
  export { UnauthenticatedError, DEFAULT_BUILDER_APP_ID, defaultAppIdFromIdentity, } from './auth.js';
58
70
  export { createConsoleLogger } from './logger.js';
59
- export { createGguiSessionChannelServer, DEFAULT_RENDER_CHANNEL_PATH, } from './ggui-session-channel.js';
71
+ export { ChannelSessionNotFoundError, createGguiSessionChannelServer, DEFAULT_RENDER_CHANNEL_PATH, } from './ggui-session-channel.js';
60
72
  export { resolveStorageFromConfig } from './storage.js';
61
73
  export { DEFAULT_PAIRING_ADMIN_INIT_PATH, DEFAULT_PAIRING_PATH, mountPairingTransport, } from './pairing-transport.js';
62
74
  // End-user browser-session cookie + login routes. Cookie + endpoints
@@ -64,7 +64,7 @@ const DEFAULT_PRESET_BODY = [
64
64
  '',
65
65
  'Every UI you render is described by a DataContract — a typed declaration of four data surfaces. You author the contract at handshake time; the contract is what the renderer compiles + the agent later interacts with. Four specs, each with one job:',
66
66
  '',
67
- ' • propsSpec — agent → client (one-shot data). The initial values the rendered UI displays. Push sends these as `props`; ggui_update mutates them after delivery. e.g. a weather card has {temperature, condition, city} on propsSpec.',
67
+ ' • propsSpec — agent → client (one-shot data). The initial values the rendered UI displays. Push sends these as `props`; ggui_amend / ggui_update mutate them after delivery. e.g. a weather card has {temperature, condition, city} on propsSpec.',
68
68
  '',
69
69
  ' • actionSpec — client → agent (discrete events). User gestures (clicks, submits) that drive the agent\'s NEXT TURN. Each entry has a `label`, optional `schema` for the payload, and optional `nextStep: "<toolName>"`. When `nextStep` is present, it names the tool the agent SHOULD call next AND the same name MUST also appear in `agentCapabilities.tools` (cross-ref invariant; rejection code `cross_reference_unresolved`). Omit `nextStep` entirely when the agent should decide freely from broader context (open-ended form submits). e.g. a feedback form has `submit` on actionSpec with `nextStep:"record_feedback"` (and `record_feedback` listed under `agentCapabilities.tools`).',
70
70
  '',
@@ -80,8 +80,8 @@ const DEFAULT_PRESET_BODY = [
80
80
  '',
81
81
  'TOOL PREREQUISITES (read once, internalize):',
82
82
  ' • `ggui_handshake` is a prerequisite for `ggui_render` ONLY. It returns the contract+code negotiation state (`handshakeId`) that `ggui_render` consumes. Call it BEFORE rendering a NEW UI.',
83
- ' • `ggui_consume` and `ggui_update` operate on EXISTING sessions identified by `sessionId` and require NO handshake. The `sessionId` was minted by an earlier `ggui_render` call; it identifies the live iframe the user is interacting with. Calling `ggui_handshake` before `ggui_consume`/`ggui_update` is a category error — it would mint a SEPARATE new session that the user can\'t see, orphaning whichever live iframe the gesture targeted.',
84
- ' • Each `sessionId` is the agent\'s persistent handle to one specific live iframe. Reuse it across consume/update for as long as the user is interacting with that UI; only call `ggui_handshake` again when you need to spawn a genuinely new UI surface.',
83
+ ' • `ggui_consume`, `ggui_amend`, and `ggui_update` operate on EXISTING sessions identified by `sessionId` and require NO handshake. The `sessionId` was minted by an earlier `ggui_render` call; it identifies the live iframe the user is interacting with. Calling `ggui_handshake` before `ggui_consume`/`ggui_amend`/`ggui_update` is a category error — it would mint a SEPARATE new session that the user can\'t see, orphaning whichever live iframe the gesture targeted.',
84
+ ' • Each `sessionId` is the agent\'s persistent handle to one specific live iframe. Reuse it across consume/amend/update for as long as the user is interacting with that UI; only call `ggui_handshake` again when you need to spawn a genuinely new UI surface.',
85
85
  '',
86
86
  ' 1. ggui_handshake — FIRST CALL for any UI. Negotiate a contract for the next render. Post {intent, blueprintDraft: {contract, variance?, generator?}}. `variance` is optional and steers cache lookup + gen by axis: `persona` ("minimalist", "playful"), `aesthetic` ("dense", "spacious"), `seedPrompt` (deterministic seed). HANDSHAKE IS FORGIVING — it ALWAYS returns a handshakeId + a CONFORMING contract, even when your draft is malformed: the server cache-matches an existing blueprint OR repairs your draft until it passes validation. The suggestion carries origin: cache (an existing blueprint matched — delivered instantly) | agent (your draft was already clean) | synth (your draft had problems and the server repaired it; `suggestion.validationFindings` lists exactly what was wrong so you learn for next time). CRITICAL: a synth result is NOT an error and NOT a request to retry — the returned contract is READY. Accept it and call ggui_render. Do NOT re-call ggui_handshake hoping for a different origin — that is a loop that wastes turns and changes nothing.',
87
87
  '',
@@ -89,9 +89,9 @@ const DEFAULT_PRESET_BODY = [
89
89
  '',
90
90
  ' 3. NEXT STEP — read the render response. If it carries a `nextStep` field, call that tool with the given args. Render only emits `nextStep` when the contract declared a non-empty actionSpec (i.e., the UI has interactive buttons/forms); in that case nextStep names ggui_consume and your job is to long-poll for the user\'s gesture. If the render response has NO nextStep, the UI is pure-display (props only, no actionSpec) — you can end your turn; the user reads the UI and types their next prompt when they\'re ready.',
91
91
  '',
92
- ' 4. ggui_consume (when render said to) — long-poll for user interaction. Keyed by sessionId. Blocks up to ~15 min (deployment-configurable); returns when an actionSpec event arrives or the session closes. Each return carries `{events, status}`: `events[]` is the discrete action(s) the user just took. Each event is an envelope `{intent, actionData, uiContext, actionId, firedAt}` — `actionData` is WHAT the user did (action name + validated payload, plus `nextStep` hint if the author declared one); `uiContext` is the iframe-local snapshot of every declared contextSpec slot AT THE MOMENT the action fired (form fields, selected tab, slider value, scroll position — whatever the contract declared). Both inform your reaction without a second round trip. Honor each event\'s `actionData.nextStep` hint if the tool is available, then loop back to step 1 to render the response. Continue until consume returns status:"completed" or no further follow-up is needed.',
92
+ ' 4. ggui_consume (when render said to) — long-poll for user interaction. Keyed by sessionId. Pass `timeout: 25` — each call long-polls up to 25 seconds (the server-enforced per-call cap; the default is 0, an instant non-blocking drain) and returns when an actionSpec event arrives or the window lapses. On an empty `events` array you may re-call once, then end your turn — when nobody is polling, a user gesture rings the chat as a new user message carrying its own consume directive. Each return carries `{events, status}`: `events[]` is the discrete action(s) the user just took. Each event is an envelope `{intent, actionData, uiContext, actionId, firedAt}` — `actionData` is WHAT the user did (action name + validated payload, plus `nextStep` hint if the author declared one); `uiContext` is the iframe-local snapshot of every declared contextSpec slot AT THE MOMENT the action fired (form fields, selected tab, slider value, scroll position — whatever the contract declared). Both inform your reaction without a second round trip. Honor each event\'s `actionData.nextStep` hint if the tool is available, then loop back to step 1 to render the response. Continue until consume returns status:"expired" (the render is gone) or no further follow-up is needed.',
93
93
  '',
94
- ' 5. ggui_update — reflect the new state in the UI. After ANY domain-tool call whose result changed data the rendered UI displays (e.g. `todo_toggle` flips a todo\'s `done`, `cart_add_item` extends a cart, `note_save` persists text), you MUST immediately call `ggui_update` with the refreshed props so the user sees what just happened. The rendered UI does NOT auto-refresh — it only shows the props it was last given. Skipping `ggui_update` after a state-mutating tool call leaves the user staring at stale state and is the #1 wire bug. Pattern: `consume → domain-tool → ggui_update → loop to consume`. Two modes: `{sessionId, kind:"replace", props}` sends the FULL new props map (use when most fields changed or you want deterministic restoration); `{sessionId, kind:"merge", patch}` sends ONLY the delta as RFC 7396 JSON Merge Patch (shallow merge, recurse on nested objects, `null` deletes a key, arrays fully replace — use when most props stay the same and only one or two fields changed). Prefer `merge` after a single domain-tool mutation; prefer `replace` when restoring state or when most fields changed. The only times you skip `ggui_update` are: (a) the domain tool was pure-read (todo_list, search, etc.) AND its result wasn\'t for the UI, or (b) the contract has no propsSpec (pure-display with no mutable state).',
94
+ ' 5. ggui_amend — reflect the new state in the UI, in place. After ANY domain-tool call whose result changed data the rendered UI displays (e.g. `todo_toggle` flips a todo\'s `done`, `cart_add_item` extends a cart, `note_save` persists text), you MUST immediately call `ggui_amend` with the refreshed props so the user sees what just happened. The rendered UI does NOT auto-refresh — it only shows the props it was last given. Skipping this after a state-mutating tool call leaves the user staring at stale state and is the #1 wire bug. Pattern: `consume → domain-tool → ggui_amend → loop to consume`. Two modes: `{sessionId, kind:"replace", props}` sends the FULL new props map (use when most fields changed or you want deterministic restoration); `{sessionId, kind:"merge", patch}` sends ONLY the delta as RFC 7396 JSON Merge Patch (shallow merge, recurse on nested objects, `null` deletes a key, arrays fully replace — use when most props stay the same and only one or two fields changed). Prefer `merge` after a single domain-tool mutation. The only times you skip it: (a) the domain tool was pure-read (todo_list, search, etc.) AND its result wasn\'t for the UI, or (b) the contract has no propsSpec (pure-display with no mutable state). AMEND vs UPDATE vs NEW RENDER: `ggui_amend` repaints the SAME card quietly — no new card, the history number does not advance; it is the default move in gesture loops. `ggui_update` renders the updated state as a NEW card in the conversation and advances the history number — use it for state MILESTONES worth a card in the transcript, or when the original card is no longer visible/usable. When the user\'s gesture asks for a structurally DIFFERENT surface (picking from a menu, opening a detail view, navigating), run ggui_handshake + ggui_render for the next UI instead. If amend/update returns `updated: false` with a warning, your patch changed nothing: either send genuinely different values or render the next surface.',
95
95
  '',
96
96
  'In short: let the protocol\'s `nextStep` fields drive routing. Every ggui_* tool whose response logically chains forwards (handshake → render → consume) emits a nextStep when there IS a next step; the absence of nextStep means "you\'re done with this thread for now."',
97
97
  '',
@@ -100,7 +100,7 @@ const DEFAULT_PRESET_BODY = [
100
100
  'When the user reloads the page after interacting with a rendered UI, the iframe re-mounts but the agent process may have lost its `ggui_consume` long-poll. Subsequent clicks in the rehydrated iframe arrive as a USER MESSAGE that carries an explicit `sessionId` — either as a structured directive block (host-synthesized prefix wrapping the user prose) or as a typed slice the host passes through. EITHER WAY, the rule is the same:',
101
101
  '',
102
102
  ' • The named `sessionId` identifies an EXISTING live iframe. Treat the message as a continuation of THAT session, not as a fresh request.',
103
- ' • REQUIRED first tool call: `ggui_consume({sessionId: <the-named-id>})` to drain the queued gesture. This returns the event the user fired; act on it with the appropriate domain tool, then `ggui_update({sessionId, ...})` on the SAME sessionId.',
103
+ ' • REQUIRED first tool call: `ggui_consume({sessionId: <the-named-id>})` to drain the queued gesture. This returns the event the user fired; act on it with the appropriate domain tool, then `ggui_amend({sessionId, ...})` on the SAME sessionId — the card the user is looking at repaints in place.',
104
104
  ' • DO NOT call `ggui_handshake` for a rehydrated-gesture message. Handshaking mints a SEPARATE new session the user can\'t see, orphaning the live iframe they actually clicked. This is the most common reload-flow regression — when in doubt and you see a `sessionId` named in the user message, your first tool call is `ggui_consume({sessionId})`, full stop.',
105
105
  '',
106
106
  'Worked example — user reload + click:',
@@ -110,13 +110,13 @@ const DEFAULT_PRESET_BODY = [
110
110
  ' 3. Agent receives a user message containing `sessionId: s_abc123` (in a host-synthesized directive or as a structured slice).',
111
111
  ' 4. Agent calls `ggui_consume({sessionId: "s_abc123"})` — NOT `ggui_handshake`.',
112
112
  ' 5. Consume returns the click event; agent calls `todo_toggle({id: 1, done: false})`.',
113
- ' 6. Agent calls `ggui_update({sessionId: "s_abc123", kind: "merge", patch: {todos: [...]}})`.',
113
+ ' 6. Agent calls `ggui_amend({sessionId: "s_abc123", kind: "merge", patch: {todos: [...]}})` — the mounted card repaints in place.',
114
114
  '',
115
115
  'The same flow applies whether the message arrives mid-conversation or as the very first turn after the user opens a saved chat URL — the sessionId is the source of truth for which iframe to address.',
116
116
  '',
117
117
  '═══ COMPLEMENTARY TOOLS ═══',
118
118
  '',
119
- ' • ggui_update — refresh a delivered UI with new props WITHOUT destroying it. Two modes: `kind:"replace"` (full props) or `kind:"merge"` (RFC 7396 delta — see step 5). ALWAYS call this after any state-mutating tool call; re-rendering would lose scroll position, focus, and uncommitted input. Forgetting `ggui_update` after a mutation is the most common protocol bug.',
119
+ ' • ggui_amend — repaint the delivered UI in place with new props (no new card, history number unchanged). Two modes: `kind:"replace"` (full props) or `kind:"merge"` (RFC 7396 delta — see step 5). ALWAYS call this after any state-mutating tool call; re-rendering would lose scroll position, focus, and uncommitted input. Forgetting it after a mutation is the most common protocol bug.\n • ggui_update — render the updated state as a NEW card and advance the history number. For milestones worth showing in the transcript, or when the original card is gone/far away.',
120
120
  '',
121
121
  ' • ggui_emit — push frames to a streamSpec channel on a delivered UI. Use when the contract declared streamSpec (chat tokens, progress bars, live data). Frames must match the channel\'s declared `schema`. The live channel of the wire carries these.',
122
122
  '',
@@ -134,9 +134,9 @@ const DEFAULT_PRESET_BODY = [
134
134
  '',
135
135
  '═══ TOOL DISCOVERY (lazy-loading hosts) ═══',
136
136
  '',
137
- 'Some MCP hosts (notably claude.ai\'s connector model) use PROGRESSIVE tool discovery — only a small priority subset of tools is warmed at conversation start; the rest must be explicitly discovered via `tool_search` before they\'re callable. The ggui_* loop crosses this boundary on every render: handshake/render warm easily because you call them early, but `ggui_consume` and `ggui_update` are needed AFTER render and the host may not have loaded them yet.',
137
+ 'Some MCP hosts (notably claude.ai\'s connector model) use PROGRESSIVE tool discovery — only a small priority subset of tools is warmed at conversation start; the rest must be explicitly discovered via `tool_search` before they\'re callable. The ggui_* loop crosses this boundary on every render: handshake/render warm easily because you call them early, but `ggui_consume`, `ggui_amend`, and `ggui_update` are needed AFTER render and the host may not have loaded them yet.',
138
138
  '',
139
- 'SYMPTOM: a tool call fails with a message like "tool has not been loaded yet — call tool_search first" or "you do not have the correct parameter names." RECOVERY: call `tool_search` with the tool name as a query (e.g. `tool_search({ query: "ggui_consume" })`), wait for the load to complete, then retry the original call with the same args. Same pattern for `ggui_update`. After one successful `tool_search` per tool per conversation, subsequent calls work directly.',
139
+ 'SYMPTOM: a tool call fails with a message like "tool has not been loaded yet — call tool_search first" or "you do not have the correct parameter names." RECOVERY: call `tool_search` with the tool name as a query (e.g. `tool_search({ query: "ggui_consume" })`), wait for the load to complete, then retry the original call with the same args. Same pattern for `ggui_amend` / `ggui_update`. After one successful `tool_search` per tool per conversation, subsequent calls work directly.',
140
140
  '',
141
141
  'WHEN IN DOUBT: if the render response carries `nextStep`, the host has effectively asked you to call that tool. Don\'t skip ggui_consume because the host whined about it being unloaded — `tool_search` first, then call. Skipping it leaves the user staring at a UI whose actions silently never reach the agent — the worst protocol failure mode.',
142
142
  ];