@guuey/agent-client 0.2.2 → 0.3.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.
- package/dist/history.d.ts +14 -1
- package/dist/history.d.ts.map +1 -1
- package/dist/history.js +21 -0
- package/dist/index.d.ts +2 -4
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +4 -7
- package/dist/sse.d.ts +5 -0
- package/dist/sse.d.ts.map +1 -1
- package/dist/sse.js +34 -5
- package/dist/web-adapters.d.ts +48 -0
- package/dist/web-adapters.d.ts.map +1 -1
- package/dist/web-adapters.js +57 -0
- package/package.json +1 -1
- package/src/history.ts +23 -1
- package/src/index.ts +6 -33
- package/src/sse.ts +31 -7
- package/src/web-adapters.ts +97 -0
- package/dist/block-ui.d.ts +0 -117
- package/dist/block-ui.d.ts.map +0 -1
- package/dist/block-ui.js +0 -183
- package/dist/card-mount.d.ts +0 -69
- package/dist/card-mount.d.ts.map +0 -1
- package/dist/card-mount.js +0 -83
- package/dist/ggui-render.d.ts +0 -161
- package/dist/ggui-render.d.ts.map +0 -1
- package/dist/ggui-render.js +0 -164
- package/src/block-ui.ts +0 -212
- package/src/card-mount.ts +0 -101
- package/src/ggui-render.ts +0 -270
package/src/ggui-render.ts
DELETED
|
@@ -1,270 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* The **ggui render** channel: narrowing + self-contained shell construction
|
|
3
|
-
* for a generative-UI card produced by the ggui MCP server (`ggui_render`).
|
|
4
|
-
*
|
|
5
|
-
* ## Why this is a second channel and not the existing one
|
|
6
|
-
*
|
|
7
|
-
* `block-ui.ts`'s original narrowing accepts exactly one shape — an mcp-ui
|
|
8
|
-
* embedded resource that carries its HTML INLINE (`{uri, text|blob}`). A ggui
|
|
9
|
-
* render carries no HTML at all. Its `tool.done` looks like this on the wire
|
|
10
|
-
* (shaped identically to the production capture the widget's fixtures
|
|
11
|
-
* replay, but with the SAME synthetic ids those redacted fixtures use —
|
|
12
|
-
* `apps/widget/src/fixtures/issue2627-render-capture.sse.txt` seq 48):
|
|
13
|
-
*
|
|
14
|
-
* ```jsonc
|
|
15
|
-
* {"type":"tool.done", "toolCallId":"toolu_0000…0005",
|
|
16
|
-
* "uiData":{"sessionId":"render_0000…0001",
|
|
17
|
-
* "resourceUri":"ui://ggui/render/render_0000…0001/c10a2055…", … },
|
|
18
|
-
* "_meta":{"ai.ggui/render":{"sessionId":"render_0000…0001","appId":"APP00000",
|
|
19
|
-
* "runtimeUrl":"https://dev.mcp.sandbox.ggui.ai/_ggui/iframe-runtime.js",
|
|
20
|
-
* "wsUrl":"wss://…/ws","wsToken":"eyJ…","expiresAt":"…",
|
|
21
|
-
* "propsJson":"{…}"},
|
|
22
|
-
* "ui":{"resourceUri":"ui://ggui/render/…"}}}
|
|
23
|
-
* ```
|
|
24
|
-
*
|
|
25
|
-
* Two facts follow, and they shape everything below:
|
|
26
|
-
*
|
|
27
|
-
* 1. **`uiData.resourceUri` is the RECOGNITION signal.** It is the only part
|
|
28
|
-
* of the render's identity that survives `@silverprotocol/core`'s fold
|
|
29
|
-
* (the reducer copies `uiData` — and, as of `@silverprotocol/core`
|
|
30
|
-
* 0.4.1 (workspace#9), `_meta` — onto the `tool-result` block).
|
|
31
|
-
* 2. **`_meta["ai.ggui/render"]` is the MOUNT MATERIAL.** Everything needed
|
|
32
|
-
* to boot the card — which runtime bundle to load, which live-channel to
|
|
33
|
-
* open, which props to seed — lives there and nowhere else.
|
|
34
|
-
*
|
|
35
|
-
* ## How the card mounts (the ggui-documented self-contained shell)
|
|
36
|
-
*
|
|
37
|
-
* ggui's iframe runtime accepts its bootstrap from three delivery channels;
|
|
38
|
-
* the highest-priority one is the **self-contained shell**: HTML that inlines
|
|
39
|
-
* the slice envelope at `globalThis.__GGUI_META__` synchronously BEFORE the
|
|
40
|
-
* runtime bundle's `<script type="module">` evaluates, after which the runtime
|
|
41
|
-
* autostarts, creates its own mount container and renders — no postMessage
|
|
42
|
-
* round-trip, no host-side ggui code. That contract is stated verbatim by the
|
|
43
|
-
* runtime's own reader (`@ggui-ai/iframe-runtime`'s `parseMetaFromGlobal`:
|
|
44
|
-
* *"The global carries the SAME slice envelope shape as the wire `_meta`
|
|
45
|
-
* (`{ "ai.ggui/render": {...} }`) … per-render shells populate this
|
|
46
|
-
* synchronously BEFORE the runtime bundle's `<script type="module">`
|
|
47
|
-
* evaluates"*), and by its boot resolver (`runtime.js`'s autostart:
|
|
48
|
-
* `readSelfContainedMeta()` first, postMessage channels after).
|
|
49
|
-
*
|
|
50
|
-
* {@link gguiShellHtml} builds exactly that shell. Because the shell IS a
|
|
51
|
-
* string of HTML, the ggui card then rides the host's EXISTING mcp-ui mount
|
|
52
|
-
* path unchanged: it narrows to the same `McpUiResourcePayload` an inline
|
|
53
|
-
* resource does, so `@mcp-ui/client`'s `AppRenderer` posts it as `srcdoc` into
|
|
54
|
-
* the second-origin `mcp-app-sandbox.html` page — same double-iframe rule,
|
|
55
|
-
* same sandbox origin, same opaque inner frame. No second mount mechanism.
|
|
56
|
-
*
|
|
57
|
-
* The slice is inlined **verbatim**: `runtimeUrl` is honored as given (ggui's
|
|
58
|
-
* host checklist item 8 — "no fallback URL, no substitution"), and every other
|
|
59
|
-
* field is passed through untouched for the runtime's own projector to
|
|
60
|
-
* validate. This module reads exactly one field (`runtimeUrl`) and only to
|
|
61
|
-
* prove the slice is mountable at all.
|
|
62
|
-
*
|
|
63
|
-
* NOT in scope here: rehydrating a ggui card from persisted history. The
|
|
64
|
-
* bootstrap's `wsToken` expires minutes after the render (`expiresAt` in the
|
|
65
|
-
* capture above), so a stored bootstrap is dead on arrival — a history card
|
|
66
|
-
* without a live bootstrap correctly resolves to `undefined` and renders the
|
|
67
|
-
* host's placeholder rather than a broken mount.
|
|
68
|
-
*/
|
|
69
|
-
import type { AgBlock, JsonValue } from "@silverprotocol/core";
|
|
70
|
-
import { isJsonObject, type McpUiResourcePayload } from "./block-ui";
|
|
71
|
-
|
|
72
|
-
/** The `_meta` key the ggui render bootstrap rides on (MCP-Apps slice convention). */
|
|
73
|
-
export const GGUI_RENDER_META_KEY = "ai.ggui/render";
|
|
74
|
-
|
|
75
|
-
/** The `ui://` scheme prefix every ggui render resource uri carries. */
|
|
76
|
-
const UI_SCHEME = "ui://";
|
|
77
|
-
|
|
78
|
-
/**
|
|
79
|
-
* A ggui render bootstrap: the runtime bundle URL this module reads, plus the
|
|
80
|
-
* WHOLE `ai.ggui/render` slice, verbatim, for the shell to inline.
|
|
81
|
-
*/
|
|
82
|
-
export interface GguiRenderBootstrap {
|
|
83
|
-
/** `runtimeUrl` — the ESM bundle the shell loads. Honored as given. */
|
|
84
|
-
runtimeUrl: string;
|
|
85
|
-
/**
|
|
86
|
-
* The verbatim slice. Open-ended by construction: it is ggui's wire
|
|
87
|
-
* contract, not one this package owns, and the runtime's own projector is
|
|
88
|
-
* the authority on every field. Re-declaring it here would duplicate a
|
|
89
|
-
* contract we do not own and rot at ggui's next field addition.
|
|
90
|
-
*/
|
|
91
|
-
slice: { [key: string]: JsonValue };
|
|
92
|
-
}
|
|
93
|
-
|
|
94
|
-
/** A ggui render recognised on a tool result: its resource uri + mount material. */
|
|
95
|
-
export interface GguiRenderDescriptor {
|
|
96
|
-
/** `uiData.resourceUri` — `ui://ggui/render/<sessionId>/<contractHash>`. */
|
|
97
|
-
resourceUri: string;
|
|
98
|
-
/** `uiData.sessionId`, when present. */
|
|
99
|
-
sessionId?: string;
|
|
100
|
-
/**
|
|
101
|
-
* The `_meta["ai.ggui/render"]` slice, when it reached us. Absent for a
|
|
102
|
-
* persisted history card and for any consumer folding without `fold.ts`'s
|
|
103
|
-
* `_meta` carriage — such a descriptor is recognised but NOT mountable.
|
|
104
|
-
*/
|
|
105
|
-
bootstrap?: GguiRenderBootstrap;
|
|
106
|
-
}
|
|
107
|
-
|
|
108
|
-
/** A non-empty JSON string field. */
|
|
109
|
-
function isNonEmptyString(value: JsonValue | undefined): value is string {
|
|
110
|
-
return typeof value === "string" && value.length > 0;
|
|
111
|
-
}
|
|
112
|
-
|
|
113
|
-
/**
|
|
114
|
-
* Does this slice carry at least one MOUNT MODE discriminator?
|
|
115
|
-
*
|
|
116
|
-
* Mirrors `@ggui-ai/iframe-runtime`'s `validateMeta` (see
|
|
117
|
-
* `node_modules/@ggui-ai/iframe-runtime/dist/meta-parse.d.ts`) and the
|
|
118
|
-
* `McpAppAiGguiRenderMeta` doc comment it implements
|
|
119
|
-
* (`@ggui-ai/protocol/integrations/mcp-apps`): the runtime needs `runtimeUrl`
|
|
120
|
-
* PLUS one of live mode (`wsUrl` + `wsToken` together), `codeUrl`, or `kind` —
|
|
121
|
-
* without one of those three the iframe has nothing to mount.
|
|
122
|
-
*/
|
|
123
|
-
function hasModeDiscriminator(slice: { [key: string]: JsonValue }): boolean {
|
|
124
|
-
if (isNonEmptyString(slice.wsUrl) && isNonEmptyString(slice.wsToken)) return true;
|
|
125
|
-
if (isNonEmptyString(slice.codeUrl)) return true;
|
|
126
|
-
if (isNonEmptyString(slice.kind)) return true;
|
|
127
|
-
return false;
|
|
128
|
-
}
|
|
129
|
-
|
|
130
|
-
/**
|
|
131
|
-
* A `_meta` container → the ggui render bootstrap, or `undefined`.
|
|
132
|
-
*
|
|
133
|
-
* Two hard requirements, both the runtime's own `validateMeta` enforces
|
|
134
|
-
* (`MALFORMED_BOOTSTRAP`): a non-empty `runtimeUrl`, AND at least one mode
|
|
135
|
-
* discriminator (see {@link hasModeDiscriminator}). A slice with `runtimeUrl`
|
|
136
|
-
* alone has a bundle to load but nothing for it to mount — the runtime would
|
|
137
|
-
* boot into a blank shell rather than a card, so this guard treats that shape
|
|
138
|
-
* as unmountable too and returns `undefined`.
|
|
139
|
-
*/
|
|
140
|
-
export function asGguiRenderBootstrap(meta: JsonValue | undefined): GguiRenderBootstrap | undefined {
|
|
141
|
-
if (!isJsonObject(meta)) return undefined;
|
|
142
|
-
const slice = meta[GGUI_RENDER_META_KEY];
|
|
143
|
-
if (!isJsonObject(slice)) return undefined;
|
|
144
|
-
const runtimeUrl = slice.runtimeUrl;
|
|
145
|
-
if (typeof runtimeUrl !== "string" || runtimeUrl.length === 0) return undefined;
|
|
146
|
-
if (!hasModeDiscriminator(slice)) return undefined;
|
|
147
|
-
return { runtimeUrl, slice };
|
|
148
|
-
}
|
|
149
|
-
|
|
150
|
-
/**
|
|
151
|
-
* A tool result's `uiData` (+ its `_meta`, when carried) → a ggui render
|
|
152
|
-
* descriptor, or `undefined` for anything that is not one.
|
|
153
|
-
*
|
|
154
|
-
* The `ui://` scheme gate is deliberate: `uiData` is a general-purpose channel
|
|
155
|
-
* (every `structuredContent` of a `_meta.ui`-stamped tool lands there), so a
|
|
156
|
-
* bare `resourceUri` string is not on its own a claim of generative UI.
|
|
157
|
-
*/
|
|
158
|
-
export function asGguiRender(
|
|
159
|
-
uiData: JsonValue | undefined,
|
|
160
|
-
meta: JsonValue | undefined,
|
|
161
|
-
): GguiRenderDescriptor | undefined {
|
|
162
|
-
if (!isJsonObject(uiData)) return undefined;
|
|
163
|
-
const resourceUri = uiData.resourceUri;
|
|
164
|
-
if (typeof resourceUri !== "string" || !resourceUri.startsWith(UI_SCHEME)) return undefined;
|
|
165
|
-
const bootstrap = asGguiRenderBootstrap(meta);
|
|
166
|
-
return {
|
|
167
|
-
resourceUri,
|
|
168
|
-
...(typeof uiData.sessionId === "string" ? { sessionId: uiData.sessionId } : {}),
|
|
169
|
-
...(bootstrap ? { bootstrap } : {}),
|
|
170
|
-
};
|
|
171
|
-
}
|
|
172
|
-
|
|
173
|
-
/** A live `tool-result` AgBlock → its ggui render descriptor, if it is one. */
|
|
174
|
-
export function toolResultGguiRender(
|
|
175
|
-
block: Extract<AgBlock, { type: "tool-result" }>,
|
|
176
|
-
): GguiRenderDescriptor | undefined {
|
|
177
|
-
return asGguiRender(block.uiData, block._meta);
|
|
178
|
-
}
|
|
179
|
-
|
|
180
|
-
/** An untyped (persisted-snapshot) block → its ggui render descriptor, if it is one. */
|
|
181
|
-
export function blockGguiRender(block: JsonValue): GguiRenderDescriptor | undefined {
|
|
182
|
-
if (!isJsonObject(block)) return undefined;
|
|
183
|
-
if (block.type !== "tool-result") return undefined;
|
|
184
|
-
return asGguiRender(block.uiData, block._meta);
|
|
185
|
-
}
|
|
186
|
-
|
|
187
|
-
/**
|
|
188
|
-
* Embed a JSON value inside an inline `<script>` safely.
|
|
189
|
-
*
|
|
190
|
-
* `</script` inside a string literal terminates the element in the HTML
|
|
191
|
-
* parser regardless of JS quoting, and U+2028/U+2029 are line terminators in
|
|
192
|
-
* JS source but not in JSON — both are escaped at the `<`/codepoint level so
|
|
193
|
-
* the emitted text is still exactly the same JSON value.
|
|
194
|
-
*/
|
|
195
|
-
function inlineJson(value: JsonValue): string {
|
|
196
|
-
return JSON.stringify(value)
|
|
197
|
-
.replace(/</g, "\\u003c")
|
|
198
|
-
.replace(/\u2028/g, "\\u2028")
|
|
199
|
-
.replace(/\u2029/g, "\\u2029");
|
|
200
|
-
}
|
|
201
|
-
|
|
202
|
-
/** Escape a string for use inside a double-quoted HTML attribute. */
|
|
203
|
-
function attr(value: string): string {
|
|
204
|
-
return value
|
|
205
|
-
.replace(/&/g, "&")
|
|
206
|
-
.replace(/"/g, """)
|
|
207
|
-
.replace(/</g, "<")
|
|
208
|
-
.replace(/>/g, ">");
|
|
209
|
-
}
|
|
210
|
-
|
|
211
|
-
/**
|
|
212
|
-
* The ggui **self-contained shell** for a render bootstrap — see this module's
|
|
213
|
-
* header for the contract it implements.
|
|
214
|
-
*
|
|
215
|
-
* Ordering is guaranteed twice over: the classic `<script>` runs during parse,
|
|
216
|
-
* and the runtime's `<script type="module">` is deferred by definition, so the
|
|
217
|
-
* global is always populated before the bundle evaluates.
|
|
218
|
-
*/
|
|
219
|
-
export function gguiShellHtml(bootstrap: GguiRenderBootstrap): string {
|
|
220
|
-
const envelope = inlineJson({ [GGUI_RENDER_META_KEY]: bootstrap.slice });
|
|
221
|
-
return [
|
|
222
|
-
"<!doctype html>",
|
|
223
|
-
'<html lang="en">',
|
|
224
|
-
"<head>",
|
|
225
|
-
'<meta charset="utf-8">',
|
|
226
|
-
'<meta name="viewport" content="width=device-width, initial-scale=1">',
|
|
227
|
-
'<meta name="color-scheme" content="light dark">',
|
|
228
|
-
"<title>ggui card</title>",
|
|
229
|
-
"<style>html,body{margin:0;height:100%;background:transparent}</style>",
|
|
230
|
-
`<script>globalThis.__GGUI_META__=${envelope};</script>`,
|
|
231
|
-
`<script type="module" src="${attr(bootstrap.runtimeUrl)}"></script>`,
|
|
232
|
-
"</head>",
|
|
233
|
-
"<body></body>",
|
|
234
|
-
"</html>",
|
|
235
|
-
"",
|
|
236
|
-
].join("\n");
|
|
237
|
-
}
|
|
238
|
-
|
|
239
|
-
/**
|
|
240
|
-
* A ggui render descriptor → the mountable resource the host's existing
|
|
241
|
-
* mcp-ui path already knows how to mount, or `undefined` when the descriptor
|
|
242
|
-
* carries no bootstrap (history cards, and any fold that dropped `_meta`).
|
|
243
|
-
*
|
|
244
|
-
* The `uri` is the render's REAL `resourceUri` — the shell is the payload, not
|
|
245
|
-
* a renaming of the resource.
|
|
246
|
-
*
|
|
247
|
-
* **On `_meta` being required to MOUNT (but never to RECOGNISE).** Recognition
|
|
248
|
-
* — "this tool result is a ggui card" — is keyed on `uiData.resourceUri` alone
|
|
249
|
-
* and never waits for anything (see {@link asGguiRender}); nothing in this
|
|
250
|
-
* package is blocked on an upstream change. Mounting is different, and the
|
|
251
|
-
* requirement is ggui's, not ours: its runtime rejects a slice without
|
|
252
|
-
* `runtimeUrl` AND without at least one mode discriminator (`wsUrl`+`wsToken`,
|
|
253
|
-
* `codeUrl`, or `kind`) as `MALFORMED_BOOTSTRAP` and renders nothing. `uiData`
|
|
254
|
-
* carries none of those fields — it has `sessionId`, `resourceUri`, `action`,
|
|
255
|
-
* `contractHash`, `blueprintId`, `variantKey`, `cache`, `nextStep`, and that is
|
|
256
|
-
* all. So a bootstrap-less descriptor could only ever produce a blank frame;
|
|
257
|
-
* returning `undefined` and letting the host show its own placeholder is the
|
|
258
|
-
* honest answer, not a deferral. `@silverprotocol/core`'s `Reducer` is what
|
|
259
|
-
* puts `_meta` on the block for a live turn, in-repo, today.
|
|
260
|
-
*/
|
|
261
|
-
export function gguiRenderResource(
|
|
262
|
-
render: GguiRenderDescriptor,
|
|
263
|
-
): McpUiResourcePayload | undefined {
|
|
264
|
-
if (!render.bootstrap) return undefined;
|
|
265
|
-
return {
|
|
266
|
-
uri: render.resourceUri,
|
|
267
|
-
mimeType: "text/html",
|
|
268
|
-
text: gguiShellHtml(render.bootstrap),
|
|
269
|
-
};
|
|
270
|
-
}
|