@guuey/agent-client 0.2.1 → 0.2.3
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/ggui-render.d.ts +52 -93
- package/dist/ggui-render.d.ts.map +1 -1
- package/dist/ggui-render.js +26 -105
- package/dist/index.d.ts +1 -1
- package/dist/index.d.ts.map +1 -1
- package/package.json +2 -1
- package/src/ggui-render.ts +67 -169
- package/src/index.ts +1 -0
package/dist/ggui-render.d.ts
CHANGED
|
@@ -2,27 +2,27 @@
|
|
|
2
2
|
* The **ggui render** channel: narrowing + self-contained shell construction
|
|
3
3
|
* for a generative-UI card produced by the ggui MCP server (`ggui_render`).
|
|
4
4
|
*
|
|
5
|
-
* ##
|
|
5
|
+
* ## Where the pieces live (guuey#108 / ggui#427)
|
|
6
6
|
*
|
|
7
|
-
*
|
|
8
|
-
*
|
|
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):
|
|
7
|
+
* The two halves of this channel have different owners, and the module is
|
|
8
|
+
* split along that line:
|
|
13
9
|
*
|
|
14
|
-
*
|
|
15
|
-
*
|
|
16
|
-
*
|
|
17
|
-
*
|
|
18
|
-
*
|
|
19
|
-
*
|
|
20
|
-
*
|
|
21
|
-
*
|
|
22
|
-
*
|
|
23
|
-
*
|
|
10
|
+
* - **ggui's wire contract** — what makes a `_meta` slice a mountable
|
|
11
|
+
* render bootstrap, and what the self-contained shell must contain — is
|
|
12
|
+
* OWNED by ggui and imported from
|
|
13
|
+
* `@ggui-ai/protocol/integrations/mcp-apps` ({@link asGguiRenderBootstrap},
|
|
14
|
+
* {@link gguiShellHtml}, the `ai.ggui/render` key). This package used to
|
|
15
|
+
* carry byte-compatible private copies (lifted upstream as ggui#427);
|
|
16
|
+
* re-exporting the originals means a shell-contract change lands here by
|
|
17
|
+
* bumping the pin, not by mirror-editing two repos.
|
|
18
|
+
* - **host/silverprotocol shapes** — the `uiData`-keyed RECOGNITION signal,
|
|
19
|
+
* the `AgBlock` tool-result narrowing, and the `McpUiResourcePayload`
|
|
20
|
+
* adapter onto the host's existing mcp-ui mount path — are guuey-side
|
|
21
|
+
* contracts and stay implemented here.
|
|
24
22
|
*
|
|
25
|
-
*
|
|
23
|
+
* ## Why recognition and mounting are separate
|
|
24
|
+
*
|
|
25
|
+
* A ggui render's `tool.done` carries two distinct signals:
|
|
26
26
|
*
|
|
27
27
|
* 1. **`uiData.resourceUri` is the RECOGNITION signal.** It is the only part
|
|
28
28
|
* of the render's identity that survives `@silverprotocol/core`'s fold
|
|
@@ -32,61 +32,29 @@
|
|
|
32
32
|
* to boot the card — which runtime bundle to load, which live-channel to
|
|
33
33
|
* open, which props to seed — lives there and nowhere else.
|
|
34
34
|
*
|
|
35
|
-
*
|
|
36
|
-
*
|
|
37
|
-
*
|
|
38
|
-
*
|
|
39
|
-
*
|
|
40
|
-
*
|
|
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.
|
|
35
|
+
* The shell {@link gguiShellHtml} builds is a string of HTML, so the ggui
|
|
36
|
+
* card rides the host's EXISTING mcp-ui mount path unchanged: it narrows to
|
|
37
|
+
* the same `McpUiResourcePayload` an inline resource does, so
|
|
38
|
+
* `@mcp-ui/client`'s `AppRenderer` posts it as `srcdoc` into the
|
|
39
|
+
* second-origin `mcp-app-sandbox.html` page — same double-iframe rule, same
|
|
40
|
+
* sandbox origin, same opaque inner frame. No second mount mechanism.
|
|
62
41
|
*
|
|
63
42
|
* NOT in scope here: rehydrating a ggui card from persisted history. The
|
|
64
|
-
* bootstrap's `wsToken` expires minutes after the render
|
|
65
|
-
*
|
|
66
|
-
*
|
|
67
|
-
*
|
|
43
|
+
* bootstrap's `wsToken` expires minutes after the render, so a stored
|
|
44
|
+
* bootstrap is dead on arrival — a history card without a live bootstrap
|
|
45
|
+
* correctly resolves to `undefined` and renders the host's placeholder
|
|
46
|
+
* rather than a broken mount.
|
|
68
47
|
*/
|
|
69
48
|
import type { AgBlock, JsonValue } from "@silverprotocol/core";
|
|
49
|
+
import { type GguiRenderBootstrap } from "@ggui-ai/protocol/integrations/mcp-apps";
|
|
70
50
|
import { type McpUiResourcePayload } from "./block-ui";
|
|
71
|
-
|
|
72
|
-
export
|
|
51
|
+
export { asGguiRenderBootstrap, gguiShellHtml, } from "@ggui-ai/protocol/integrations/mcp-apps";
|
|
52
|
+
export type { GguiRenderBootstrap, GguiShellHtmlOptions, } from "@ggui-ai/protocol/integrations/mcp-apps";
|
|
73
53
|
/**
|
|
74
|
-
*
|
|
75
|
-
*
|
|
54
|
+
* The `_meta` key the ggui render bootstrap rides on. Alias of the
|
|
55
|
+
* protocol package's own constant — one spelling, owned upstream.
|
|
76
56
|
*/
|
|
77
|
-
export
|
|
78
|
-
/** `runtimeUrl` — the ESM bundle the shell loads. Honored as given. */
|
|
79
|
-
runtimeUrl: string;
|
|
80
|
-
/**
|
|
81
|
-
* The verbatim slice. Open-ended by construction: it is ggui's wire
|
|
82
|
-
* contract, not one this package owns, and the runtime's own projector is
|
|
83
|
-
* the authority on every field. Re-declaring it here would duplicate a
|
|
84
|
-
* contract we do not own and rot at ggui's next field addition.
|
|
85
|
-
*/
|
|
86
|
-
slice: {
|
|
87
|
-
[key: string]: JsonValue;
|
|
88
|
-
};
|
|
89
|
-
}
|
|
57
|
+
export declare const GGUI_RENDER_META_KEY: "ai.ggui/render";
|
|
90
58
|
/** A ggui render recognised on a tool result: its resource uri + mount material. */
|
|
91
59
|
export interface GguiRenderDescriptor {
|
|
92
60
|
/** `uiData.resourceUri` — `ui://ggui/render/<sessionId>/<contractHash>`. */
|
|
@@ -100,17 +68,6 @@ export interface GguiRenderDescriptor {
|
|
|
100
68
|
*/
|
|
101
69
|
bootstrap?: GguiRenderBootstrap;
|
|
102
70
|
}
|
|
103
|
-
/**
|
|
104
|
-
* A `_meta` container → the ggui render bootstrap, or `undefined`.
|
|
105
|
-
*
|
|
106
|
-
* Two hard requirements, both the runtime's own `validateMeta` enforces
|
|
107
|
-
* (`MALFORMED_BOOTSTRAP`): a non-empty `runtimeUrl`, AND at least one mode
|
|
108
|
-
* discriminator (see {@link hasModeDiscriminator}). A slice with `runtimeUrl`
|
|
109
|
-
* alone has a bundle to load but nothing for it to mount — the runtime would
|
|
110
|
-
* boot into a blank shell rather than a card, so this guard treats that shape
|
|
111
|
-
* as unmountable too and returns `undefined`.
|
|
112
|
-
*/
|
|
113
|
-
export declare function asGguiRenderBootstrap(meta: JsonValue | undefined): GguiRenderBootstrap | undefined;
|
|
114
71
|
/**
|
|
115
72
|
* A tool result's `uiData` (+ its `_meta`, when carried) → a ggui render
|
|
116
73
|
* descriptor, or `undefined` for anything that is not one.
|
|
@@ -120,21 +77,19 @@ export declare function asGguiRenderBootstrap(meta: JsonValue | undefined): Ggui
|
|
|
120
77
|
* bare `resourceUri` string is not on its own a claim of generative UI.
|
|
121
78
|
*/
|
|
122
79
|
export declare function asGguiRender(uiData: JsonValue | undefined, meta: JsonValue | undefined): GguiRenderDescriptor | undefined;
|
|
123
|
-
/**
|
|
80
|
+
/**
|
|
81
|
+
* A live `tool-result` AgBlock → its ggui render descriptor, if it is one.
|
|
82
|
+
*
|
|
83
|
+
* NOTE: `@ggui-ai/protocol/integrations/mcp-apps` exports a helper of the
|
|
84
|
+
* same name that narrows a spec-canonical MCP `CallToolResult` instead. This
|
|
85
|
+
* one is the silverprotocol-side twin — the input is the FOLDED block, whose
|
|
86
|
+
* `uiData`/`_meta` carriage is `@silverprotocol/core`'s contract, not ggui's.
|
|
87
|
+
*/
|
|
124
88
|
export declare function toolResultGguiRender(block: Extract<AgBlock, {
|
|
125
89
|
type: "tool-result";
|
|
126
90
|
}>): GguiRenderDescriptor | undefined;
|
|
127
91
|
/** An untyped (persisted-snapshot) block → its ggui render descriptor, if it is one. */
|
|
128
92
|
export declare function blockGguiRender(block: JsonValue): GguiRenderDescriptor | undefined;
|
|
129
|
-
/**
|
|
130
|
-
* The ggui **self-contained shell** for a render bootstrap — see this module's
|
|
131
|
-
* header for the contract it implements.
|
|
132
|
-
*
|
|
133
|
-
* Ordering is guaranteed twice over: the classic `<script>` runs during parse,
|
|
134
|
-
* and the runtime's `<script type="module">` is deferred by definition, so the
|
|
135
|
-
* global is always populated before the bundle evaluates.
|
|
136
|
-
*/
|
|
137
|
-
export declare function gguiShellHtml(bootstrap: GguiRenderBootstrap): string;
|
|
138
93
|
/**
|
|
139
94
|
* A ggui render descriptor → the mountable resource the host's existing
|
|
140
95
|
* mcp-ui path already knows how to mount, or `undefined` when the descriptor
|
|
@@ -143,6 +98,12 @@ export declare function gguiShellHtml(bootstrap: GguiRenderBootstrap): string;
|
|
|
143
98
|
* The `uri` is the render's REAL `resourceUri` — the shell is the payload, not
|
|
144
99
|
* a renaming of the resource.
|
|
145
100
|
*
|
|
101
|
+
* The shell is built `background: 'transparent'`: every guuey host that
|
|
102
|
+
* mounts through this adapter (widget, portal web, Studio) draws its own
|
|
103
|
+
* card chrome around the iframe, so the host page composits behind the card.
|
|
104
|
+
* The upstream default (`'surface'`) is for standalone served documents —
|
|
105
|
+
* see `GguiShellHtmlOptions` in `@ggui-ai/protocol/integrations/mcp-apps`.
|
|
106
|
+
*
|
|
146
107
|
* **On `_meta` being required to MOUNT (but never to RECOGNISE).** Recognition
|
|
147
108
|
* — "this tool result is a ggui card" — is keyed on `uiData.resourceUri` alone
|
|
148
109
|
* and never waits for anything (see {@link asGguiRender}); nothing in this
|
|
@@ -150,12 +111,10 @@ export declare function gguiShellHtml(bootstrap: GguiRenderBootstrap): string;
|
|
|
150
111
|
* requirement is ggui's, not ours: its runtime rejects a slice without
|
|
151
112
|
* `runtimeUrl` AND without at least one mode discriminator (`wsUrl`+`wsToken`,
|
|
152
113
|
* `codeUrl`, or `kind`) as `MALFORMED_BOOTSTRAP` and renders nothing. `uiData`
|
|
153
|
-
* carries none of those fields
|
|
154
|
-
*
|
|
155
|
-
*
|
|
156
|
-
*
|
|
157
|
-
* honest answer, not a deferral. `@silverprotocol/core`'s `Reducer` is what
|
|
158
|
-
* puts `_meta` on the block for a live turn, in-repo, today.
|
|
114
|
+
* carries none of those fields, so a bootstrap-less descriptor could only ever
|
|
115
|
+
* produce a blank frame; returning `undefined` and letting the host show its
|
|
116
|
+
* own placeholder is the honest answer, not a deferral. `@silverprotocol/core`'s
|
|
117
|
+
* `Reducer` is what puts `_meta` on the block for a live turn, in-repo, today.
|
|
159
118
|
*/
|
|
160
119
|
export declare function gguiRenderResource(render: GguiRenderDescriptor): McpUiResourcePayload | undefined;
|
|
161
120
|
//# sourceMappingURL=ggui-render.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ggui-render.d.ts","sourceRoot":"","sources":["../src/ggui-render.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"ggui-render.d.ts","sourceRoot":"","sources":["../src/ggui-render.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA8CG;AACH,OAAO,KAAK,EAAE,OAAO,EAAE,SAAS,EAAE,MAAM,sBAAsB,CAAC;AAC/D,OAAO,EAIL,KAAK,mBAAmB,EACzB,MAAM,yCAAyC,CAAC;AACjD,OAAO,EAAgB,KAAK,oBAAoB,EAAE,MAAM,YAAY,CAAC;AAErE,OAAO,EACL,qBAAqB,EACrB,aAAa,GACd,MAAM,yCAAyC,CAAC;AACjD,YAAY,EACV,mBAAmB,EACnB,oBAAoB,GACrB,MAAM,yCAAyC,CAAC;AAEjD;;;GAGG;AACH,eAAO,MAAM,oBAAoB,kBAAkC,CAAC;AAKpE,oFAAoF;AACpF,MAAM,WAAW,oBAAoB;IACnC,4EAA4E;IAC5E,WAAW,EAAE,MAAM,CAAC;IACpB,wCAAwC;IACxC,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB;;;;OAIG;IACH,SAAS,CAAC,EAAE,mBAAmB,CAAC;CACjC;AAED;;;;;;;GAOG;AACH,wBAAgB,YAAY,CAC1B,MAAM,EAAE,SAAS,GAAG,SAAS,EAC7B,IAAI,EAAE,SAAS,GAAG,SAAS,GAC1B,oBAAoB,GAAG,SAAS,CAUlC;AAED;;;;;;;GAOG;AACH,wBAAgB,oBAAoB,CAClC,KAAK,EAAE,OAAO,CAAC,OAAO,EAAE;IAAE,IAAI,EAAE,aAAa,CAAA;CAAE,CAAC,GAC/C,oBAAoB,GAAG,SAAS,CAElC;AAED,wFAAwF;AACxF,wBAAgB,eAAe,CAAC,KAAK,EAAE,SAAS,GAAG,oBAAoB,GAAG,SAAS,CAIlF;AAED;;;;;;;;;;;;;;;;;;;;;;;;;GAyBG;AACH,wBAAgB,kBAAkB,CAChC,MAAM,EAAE,oBAAoB,GAC3B,oBAAoB,GAAG,SAAS,CAOlC"}
|
package/dist/ggui-render.js
CHANGED
|
@@ -1,54 +1,13 @@
|
|
|
1
|
+
import { asGguiRenderBootstrap, gguiShellHtml, MCP_APP_AI_GGUI_RENDER_META_KEY, } from "@ggui-ai/protocol/integrations/mcp-apps";
|
|
1
2
|
import { isJsonObject } from "./block-ui";
|
|
2
|
-
|
|
3
|
-
export const GGUI_RENDER_META_KEY = "ai.ggui/render";
|
|
4
|
-
/** The `ui://` scheme prefix every ggui render resource uri carries. */
|
|
5
|
-
const UI_SCHEME = "ui://";
|
|
6
|
-
/** A non-empty JSON string field. */
|
|
7
|
-
function isNonEmptyString(value) {
|
|
8
|
-
return typeof value === "string" && value.length > 0;
|
|
9
|
-
}
|
|
10
|
-
/**
|
|
11
|
-
* Does this slice carry at least one MOUNT MODE discriminator?
|
|
12
|
-
*
|
|
13
|
-
* Mirrors `@ggui-ai/iframe-runtime`'s `validateMeta` (see
|
|
14
|
-
* `node_modules/@ggui-ai/iframe-runtime/dist/meta-parse.d.ts`) and the
|
|
15
|
-
* `McpAppAiGguiRenderMeta` doc comment it implements
|
|
16
|
-
* (`@ggui-ai/protocol/integrations/mcp-apps`): the runtime needs `runtimeUrl`
|
|
17
|
-
* PLUS one of live mode (`wsUrl` + `wsToken` together), `codeUrl`, or `kind` —
|
|
18
|
-
* without one of those three the iframe has nothing to mount.
|
|
19
|
-
*/
|
|
20
|
-
function hasModeDiscriminator(slice) {
|
|
21
|
-
if (isNonEmptyString(slice.wsUrl) && isNonEmptyString(slice.wsToken))
|
|
22
|
-
return true;
|
|
23
|
-
if (isNonEmptyString(slice.codeUrl))
|
|
24
|
-
return true;
|
|
25
|
-
if (isNonEmptyString(slice.kind))
|
|
26
|
-
return true;
|
|
27
|
-
return false;
|
|
28
|
-
}
|
|
3
|
+
export { asGguiRenderBootstrap, gguiShellHtml, } from "@ggui-ai/protocol/integrations/mcp-apps";
|
|
29
4
|
/**
|
|
30
|
-
*
|
|
31
|
-
*
|
|
32
|
-
* Two hard requirements, both the runtime's own `validateMeta` enforces
|
|
33
|
-
* (`MALFORMED_BOOTSTRAP`): a non-empty `runtimeUrl`, AND at least one mode
|
|
34
|
-
* discriminator (see {@link hasModeDiscriminator}). A slice with `runtimeUrl`
|
|
35
|
-
* alone has a bundle to load but nothing for it to mount — the runtime would
|
|
36
|
-
* boot into a blank shell rather than a card, so this guard treats that shape
|
|
37
|
-
* as unmountable too and returns `undefined`.
|
|
5
|
+
* The `_meta` key the ggui render bootstrap rides on. Alias of the
|
|
6
|
+
* protocol package's own constant — one spelling, owned upstream.
|
|
38
7
|
*/
|
|
39
|
-
export
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
const slice = meta[GGUI_RENDER_META_KEY];
|
|
43
|
-
if (!isJsonObject(slice))
|
|
44
|
-
return undefined;
|
|
45
|
-
const runtimeUrl = slice.runtimeUrl;
|
|
46
|
-
if (typeof runtimeUrl !== "string" || runtimeUrl.length === 0)
|
|
47
|
-
return undefined;
|
|
48
|
-
if (!hasModeDiscriminator(slice))
|
|
49
|
-
return undefined;
|
|
50
|
-
return { runtimeUrl, slice };
|
|
51
|
-
}
|
|
8
|
+
export const GGUI_RENDER_META_KEY = MCP_APP_AI_GGUI_RENDER_META_KEY;
|
|
9
|
+
/** The `ui://` scheme prefix every ggui render resource uri carries. */
|
|
10
|
+
const UI_SCHEME = "ui://";
|
|
52
11
|
/**
|
|
53
12
|
* A tool result's `uiData` (+ its `_meta`, when carried) → a ggui render
|
|
54
13
|
* descriptor, or `undefined` for anything that is not one.
|
|
@@ -70,7 +29,14 @@ export function asGguiRender(uiData, meta) {
|
|
|
70
29
|
...(bootstrap ? { bootstrap } : {}),
|
|
71
30
|
};
|
|
72
31
|
}
|
|
73
|
-
/**
|
|
32
|
+
/**
|
|
33
|
+
* A live `tool-result` AgBlock → its ggui render descriptor, if it is one.
|
|
34
|
+
*
|
|
35
|
+
* NOTE: `@ggui-ai/protocol/integrations/mcp-apps` exports a helper of the
|
|
36
|
+
* same name that narrows a spec-canonical MCP `CallToolResult` instead. This
|
|
37
|
+
* one is the silverprotocol-side twin — the input is the FOLDED block, whose
|
|
38
|
+
* `uiData`/`_meta` carriage is `@silverprotocol/core`'s contract, not ggui's.
|
|
39
|
+
*/
|
|
74
40
|
export function toolResultGguiRender(block) {
|
|
75
41
|
return asGguiRender(block.uiData, block._meta);
|
|
76
42
|
}
|
|
@@ -82,55 +48,6 @@ export function blockGguiRender(block) {
|
|
|
82
48
|
return undefined;
|
|
83
49
|
return asGguiRender(block.uiData, block._meta);
|
|
84
50
|
}
|
|
85
|
-
/**
|
|
86
|
-
* Embed a JSON value inside an inline `<script>` safely.
|
|
87
|
-
*
|
|
88
|
-
* `</script` inside a string literal terminates the element in the HTML
|
|
89
|
-
* parser regardless of JS quoting, and U+2028/U+2029 are line terminators in
|
|
90
|
-
* JS source but not in JSON — both are escaped at the `<`/codepoint level so
|
|
91
|
-
* the emitted text is still exactly the same JSON value.
|
|
92
|
-
*/
|
|
93
|
-
function inlineJson(value) {
|
|
94
|
-
return JSON.stringify(value)
|
|
95
|
-
.replace(/</g, "\\u003c")
|
|
96
|
-
.replace(/\u2028/g, "\\u2028")
|
|
97
|
-
.replace(/\u2029/g, "\\u2029");
|
|
98
|
-
}
|
|
99
|
-
/** Escape a string for use inside a double-quoted HTML attribute. */
|
|
100
|
-
function attr(value) {
|
|
101
|
-
return value
|
|
102
|
-
.replace(/&/g, "&")
|
|
103
|
-
.replace(/"/g, """)
|
|
104
|
-
.replace(/</g, "<")
|
|
105
|
-
.replace(/>/g, ">");
|
|
106
|
-
}
|
|
107
|
-
/**
|
|
108
|
-
* The ggui **self-contained shell** for a render bootstrap — see this module's
|
|
109
|
-
* header for the contract it implements.
|
|
110
|
-
*
|
|
111
|
-
* Ordering is guaranteed twice over: the classic `<script>` runs during parse,
|
|
112
|
-
* and the runtime's `<script type="module">` is deferred by definition, so the
|
|
113
|
-
* global is always populated before the bundle evaluates.
|
|
114
|
-
*/
|
|
115
|
-
export function gguiShellHtml(bootstrap) {
|
|
116
|
-
const envelope = inlineJson({ [GGUI_RENDER_META_KEY]: bootstrap.slice });
|
|
117
|
-
return [
|
|
118
|
-
"<!doctype html>",
|
|
119
|
-
'<html lang="en">',
|
|
120
|
-
"<head>",
|
|
121
|
-
'<meta charset="utf-8">',
|
|
122
|
-
'<meta name="viewport" content="width=device-width, initial-scale=1">',
|
|
123
|
-
'<meta name="color-scheme" content="light dark">',
|
|
124
|
-
"<title>ggui card</title>",
|
|
125
|
-
"<style>html,body{margin:0;height:100%;background:transparent}</style>",
|
|
126
|
-
`<script>globalThis.__GGUI_META__=${envelope};</script>`,
|
|
127
|
-
`<script type="module" src="${attr(bootstrap.runtimeUrl)}"></script>`,
|
|
128
|
-
"</head>",
|
|
129
|
-
"<body></body>",
|
|
130
|
-
"</html>",
|
|
131
|
-
"",
|
|
132
|
-
].join("\n");
|
|
133
|
-
}
|
|
134
51
|
/**
|
|
135
52
|
* A ggui render descriptor → the mountable resource the host's existing
|
|
136
53
|
* mcp-ui path already knows how to mount, or `undefined` when the descriptor
|
|
@@ -139,6 +56,12 @@ export function gguiShellHtml(bootstrap) {
|
|
|
139
56
|
* The `uri` is the render's REAL `resourceUri` — the shell is the payload, not
|
|
140
57
|
* a renaming of the resource.
|
|
141
58
|
*
|
|
59
|
+
* The shell is built `background: 'transparent'`: every guuey host that
|
|
60
|
+
* mounts through this adapter (widget, portal web, Studio) draws its own
|
|
61
|
+
* card chrome around the iframe, so the host page composits behind the card.
|
|
62
|
+
* The upstream default (`'surface'`) is for standalone served documents —
|
|
63
|
+
* see `GguiShellHtmlOptions` in `@ggui-ai/protocol/integrations/mcp-apps`.
|
|
64
|
+
*
|
|
142
65
|
* **On `_meta` being required to MOUNT (but never to RECOGNISE).** Recognition
|
|
143
66
|
* — "this tool result is a ggui card" — is keyed on `uiData.resourceUri` alone
|
|
144
67
|
* and never waits for anything (see {@link asGguiRender}); nothing in this
|
|
@@ -146,12 +69,10 @@ export function gguiShellHtml(bootstrap) {
|
|
|
146
69
|
* requirement is ggui's, not ours: its runtime rejects a slice without
|
|
147
70
|
* `runtimeUrl` AND without at least one mode discriminator (`wsUrl`+`wsToken`,
|
|
148
71
|
* `codeUrl`, or `kind`) as `MALFORMED_BOOTSTRAP` and renders nothing. `uiData`
|
|
149
|
-
* carries none of those fields
|
|
150
|
-
*
|
|
151
|
-
*
|
|
152
|
-
*
|
|
153
|
-
* honest answer, not a deferral. `@silverprotocol/core`'s `Reducer` is what
|
|
154
|
-
* puts `_meta` on the block for a live turn, in-repo, today.
|
|
72
|
+
* carries none of those fields, so a bootstrap-less descriptor could only ever
|
|
73
|
+
* produce a blank frame; returning `undefined` and letting the host show its
|
|
74
|
+
* own placeholder is the honest answer, not a deferral. `@silverprotocol/core`'s
|
|
75
|
+
* `Reducer` is what puts `_meta` on the block for a live turn, in-repo, today.
|
|
155
76
|
*/
|
|
156
77
|
export function gguiRenderResource(render) {
|
|
157
78
|
if (!render.bootstrap)
|
|
@@ -159,6 +80,6 @@ export function gguiRenderResource(render) {
|
|
|
159
80
|
return {
|
|
160
81
|
uri: render.resourceUri,
|
|
161
82
|
mimeType: "text/html",
|
|
162
|
-
text: gguiShellHtml(render.bootstrap),
|
|
83
|
+
text: gguiShellHtml(render.bootstrap, { background: "transparent" }),
|
|
163
84
|
};
|
|
164
85
|
}
|
package/dist/index.d.ts
CHANGED
|
@@ -4,7 +4,7 @@ export { createWebAdapters, localStorageThreadStore, webGenerateId, fetchStreamT
|
|
|
4
4
|
export { fetchThreadHistory, threadHistoryRowsToMessages, threadHistoryRowsToCards, HistoryUnauthorizedError, type ThreadHistoryRow, type ThreadHistoryFetchOptions, } from "./history";
|
|
5
5
|
export { ingestMessageFrame } from "./blocks";
|
|
6
6
|
export { asResourcePayload, asUiResource, blockUiResource, cardUiResource, isJsonObject, resourceHtml, scanProviderRawForUiResource, sortHistoryCards, toolNameFor, toolResultUiResource, type McpUiResourcePayload, } from "./block-ui";
|
|
7
|
-
export { asGguiRender, asGguiRenderBootstrap, blockGguiRender, gguiRenderResource, gguiShellHtml, toolResultGguiRender, GGUI_RENDER_META_KEY, type GguiRenderBootstrap, type GguiRenderDescriptor, } from "./ggui-render";
|
|
7
|
+
export { asGguiRender, asGguiRenderBootstrap, blockGguiRender, gguiRenderResource, gguiShellHtml, toolResultGguiRender, GGUI_RENDER_META_KEY, type GguiRenderBootstrap, type GguiRenderDescriptor, type GguiShellHtmlOptions, } from "./ggui-render";
|
|
8
8
|
export { cardCardMount, toolResultCardMount, type CardMount, type CardMountChannel, } from "./card-mount";
|
|
9
9
|
export type { AgEvent, AgReduceResult, AgMessage, AgBlock } from "@silverprotocol/core";
|
|
10
10
|
export type { AgentMessage, HistoryCard, ProfileConsentRequest, ProfileLinkRequest, ThreadIdStore, GenerateId, InvokeRequest, InvokeTransport, AgentInvokeAdapters, AgentInvokeHistoryAdapter, AgentInvokeStatus, HistoryLoadResult, UseAgentInvokeOptions, UseAgentInvokeReturn, } from "./types";
|
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,cAAc,EACd,oBAAoB,EACpB,mBAAmB,EACnB,WAAW,EACX,mBAAmB,EACnB,gBAAgB,EAChB,KAAK,cAAc,GACpB,MAAM,OAAO,CAAC;AACf,OAAO,EAAE,iBAAiB,EAAE,MAAM,eAAe,CAAC;AAClD,OAAO,EACL,iBAAiB,EACjB,uBAAuB,EACvB,aAAa,EACb,oBAAoB,EACpB,kBAAkB,EAClB,KAAK,wBAAwB,GAC9B,MAAM,gBAAgB,CAAC;AACxB,OAAO,EACL,kBAAkB,EAClB,2BAA2B,EAC3B,wBAAwB,EACxB,wBAAwB,EACxB,KAAK,gBAAgB,EACrB,KAAK,yBAAyB,GAC/B,MAAM,WAAW,CAAC;AACnB,OAAO,EAAE,kBAAkB,EAAE,MAAM,UAAU,CAAC;AAG9C,OAAO,EACL,iBAAiB,EACjB,YAAY,EACZ,eAAe,EACf,cAAc,EACd,YAAY,EACZ,YAAY,EACZ,4BAA4B,EAC5B,gBAAgB,EAChB,WAAW,EACX,oBAAoB,EACpB,KAAK,oBAAoB,GAC1B,MAAM,YAAY,CAAC;AAIpB,OAAO,EACL,YAAY,EACZ,qBAAqB,EACrB,eAAe,EACf,kBAAkB,EAClB,aAAa,EACb,oBAAoB,EACpB,oBAAoB,EACpB,KAAK,mBAAmB,EACxB,KAAK,oBAAoB,GAC1B,MAAM,eAAe,CAAC;AACvB,OAAO,EACL,aAAa,EACb,mBAAmB,EACnB,KAAK,SAAS,EACd,KAAK,gBAAgB,GACtB,MAAM,cAAc,CAAC;AAItB,YAAY,EAAE,OAAO,EAAE,cAAc,EAAE,SAAS,EAAE,OAAO,EAAE,MAAM,sBAAsB,CAAC;AACxF,YAAY,EACV,YAAY,EACZ,WAAW,EACX,qBAAqB,EACrB,kBAAkB,EAClB,aAAa,EACb,UAAU,EACV,aAAa,EACb,eAAe,EACf,mBAAmB,EACnB,yBAAyB,EACzB,iBAAiB,EACjB,iBAAiB,EACjB,qBAAqB,EACrB,oBAAoB,GACrB,MAAM,SAAS,CAAC"}
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,cAAc,EACd,oBAAoB,EACpB,mBAAmB,EACnB,WAAW,EACX,mBAAmB,EACnB,gBAAgB,EAChB,KAAK,cAAc,GACpB,MAAM,OAAO,CAAC;AACf,OAAO,EAAE,iBAAiB,EAAE,MAAM,eAAe,CAAC;AAClD,OAAO,EACL,iBAAiB,EACjB,uBAAuB,EACvB,aAAa,EACb,oBAAoB,EACpB,kBAAkB,EAClB,KAAK,wBAAwB,GAC9B,MAAM,gBAAgB,CAAC;AACxB,OAAO,EACL,kBAAkB,EAClB,2BAA2B,EAC3B,wBAAwB,EACxB,wBAAwB,EACxB,KAAK,gBAAgB,EACrB,KAAK,yBAAyB,GAC/B,MAAM,WAAW,CAAC;AACnB,OAAO,EAAE,kBAAkB,EAAE,MAAM,UAAU,CAAC;AAG9C,OAAO,EACL,iBAAiB,EACjB,YAAY,EACZ,eAAe,EACf,cAAc,EACd,YAAY,EACZ,YAAY,EACZ,4BAA4B,EAC5B,gBAAgB,EAChB,WAAW,EACX,oBAAoB,EACpB,KAAK,oBAAoB,GAC1B,MAAM,YAAY,CAAC;AAIpB,OAAO,EACL,YAAY,EACZ,qBAAqB,EACrB,eAAe,EACf,kBAAkB,EAClB,aAAa,EACb,oBAAoB,EACpB,oBAAoB,EACpB,KAAK,mBAAmB,EACxB,KAAK,oBAAoB,EACzB,KAAK,oBAAoB,GAC1B,MAAM,eAAe,CAAC;AACvB,OAAO,EACL,aAAa,EACb,mBAAmB,EACnB,KAAK,SAAS,EACd,KAAK,gBAAgB,GACtB,MAAM,cAAc,CAAC;AAItB,YAAY,EAAE,OAAO,EAAE,cAAc,EAAE,SAAS,EAAE,OAAO,EAAE,MAAM,sBAAsB,CAAC;AACxF,YAAY,EACV,YAAY,EACZ,WAAW,EACX,qBAAqB,EACrB,kBAAkB,EAClB,aAAa,EACb,UAAU,EACV,aAAa,EACb,eAAe,EACf,mBAAmB,EACnB,yBAAyB,EACzB,iBAAiB,EACjB,iBAAiB,EACjB,qBAAqB,EACrB,oBAAoB,GACrB,MAAM,SAAS,CAAC"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@guuey/agent-client",
|
|
3
|
-
"version": "0.2.
|
|
3
|
+
"version": "0.2.3",
|
|
4
4
|
"description": "Client SDK for Guuey's agent runtime: the `useAgentInvoke` React hook + pure SSE helpers that speak the /agent/invoke streaming contract, plus the paginated thread-history read plane. Host adapters (storage / id / transport) are injected, so it runs on web (Next) and React Native alike.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"type": "module",
|
|
@@ -28,6 +28,7 @@
|
|
|
28
28
|
}
|
|
29
29
|
},
|
|
30
30
|
"dependencies": {
|
|
31
|
+
"@ggui-ai/protocol": "0.6.3",
|
|
31
32
|
"@silverprotocol/core": "0.4.1"
|
|
32
33
|
},
|
|
33
34
|
"peerDependencies": {
|
package/src/ggui-render.ts
CHANGED
|
@@ -2,27 +2,27 @@
|
|
|
2
2
|
* The **ggui render** channel: narrowing + self-contained shell construction
|
|
3
3
|
* for a generative-UI card produced by the ggui MCP server (`ggui_render`).
|
|
4
4
|
*
|
|
5
|
-
* ##
|
|
5
|
+
* ## Where the pieces live (guuey#108 / ggui#427)
|
|
6
6
|
*
|
|
7
|
-
*
|
|
8
|
-
*
|
|
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):
|
|
7
|
+
* The two halves of this channel have different owners, and the module is
|
|
8
|
+
* split along that line:
|
|
13
9
|
*
|
|
14
|
-
*
|
|
15
|
-
*
|
|
16
|
-
*
|
|
17
|
-
*
|
|
18
|
-
*
|
|
19
|
-
*
|
|
20
|
-
*
|
|
21
|
-
*
|
|
22
|
-
*
|
|
23
|
-
*
|
|
10
|
+
* - **ggui's wire contract** — what makes a `_meta` slice a mountable
|
|
11
|
+
* render bootstrap, and what the self-contained shell must contain — is
|
|
12
|
+
* OWNED by ggui and imported from
|
|
13
|
+
* `@ggui-ai/protocol/integrations/mcp-apps` ({@link asGguiRenderBootstrap},
|
|
14
|
+
* {@link gguiShellHtml}, the `ai.ggui/render` key). This package used to
|
|
15
|
+
* carry byte-compatible private copies (lifted upstream as ggui#427);
|
|
16
|
+
* re-exporting the originals means a shell-contract change lands here by
|
|
17
|
+
* bumping the pin, not by mirror-editing two repos.
|
|
18
|
+
* - **host/silverprotocol shapes** — the `uiData`-keyed RECOGNITION signal,
|
|
19
|
+
* the `AgBlock` tool-result narrowing, and the `McpUiResourcePayload`
|
|
20
|
+
* adapter onto the host's existing mcp-ui mount path — are guuey-side
|
|
21
|
+
* contracts and stay implemented here.
|
|
24
22
|
*
|
|
25
|
-
*
|
|
23
|
+
* ## Why recognition and mounting are separate
|
|
24
|
+
*
|
|
25
|
+
* A ggui render's `tool.done` carries two distinct signals:
|
|
26
26
|
*
|
|
27
27
|
* 1. **`uiData.resourceUri` is the RECOGNITION signal.** It is the only part
|
|
28
28
|
* of the render's identity that survives `@silverprotocol/core`'s fold
|
|
@@ -32,64 +32,45 @@
|
|
|
32
32
|
* to boot the card — which runtime bundle to load, which live-channel to
|
|
33
33
|
* open, which props to seed — lives there and nowhere else.
|
|
34
34
|
*
|
|
35
|
-
*
|
|
36
|
-
*
|
|
37
|
-
*
|
|
38
|
-
*
|
|
39
|
-
*
|
|
40
|
-
*
|
|
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.
|
|
35
|
+
* The shell {@link gguiShellHtml} builds is a string of HTML, so the ggui
|
|
36
|
+
* card rides the host's EXISTING mcp-ui mount path unchanged: it narrows to
|
|
37
|
+
* the same `McpUiResourcePayload` an inline resource does, so
|
|
38
|
+
* `@mcp-ui/client`'s `AppRenderer` posts it as `srcdoc` into the
|
|
39
|
+
* second-origin `mcp-app-sandbox.html` page — same double-iframe rule, same
|
|
40
|
+
* sandbox origin, same opaque inner frame. No second mount mechanism.
|
|
62
41
|
*
|
|
63
42
|
* NOT in scope here: rehydrating a ggui card from persisted history. The
|
|
64
|
-
* bootstrap's `wsToken` expires minutes after the render
|
|
65
|
-
*
|
|
66
|
-
*
|
|
67
|
-
*
|
|
43
|
+
* bootstrap's `wsToken` expires minutes after the render, so a stored
|
|
44
|
+
* bootstrap is dead on arrival — a history card without a live bootstrap
|
|
45
|
+
* correctly resolves to `undefined` and renders the host's placeholder
|
|
46
|
+
* rather than a broken mount.
|
|
68
47
|
*/
|
|
69
48
|
import type { AgBlock, JsonValue } from "@silverprotocol/core";
|
|
49
|
+
import {
|
|
50
|
+
asGguiRenderBootstrap,
|
|
51
|
+
gguiShellHtml,
|
|
52
|
+
MCP_APP_AI_GGUI_RENDER_META_KEY,
|
|
53
|
+
type GguiRenderBootstrap,
|
|
54
|
+
} from "@ggui-ai/protocol/integrations/mcp-apps";
|
|
70
55
|
import { isJsonObject, type McpUiResourcePayload } from "./block-ui";
|
|
71
56
|
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
57
|
+
export {
|
|
58
|
+
asGguiRenderBootstrap,
|
|
59
|
+
gguiShellHtml,
|
|
60
|
+
} from "@ggui-ai/protocol/integrations/mcp-apps";
|
|
61
|
+
export type {
|
|
62
|
+
GguiRenderBootstrap,
|
|
63
|
+
GguiShellHtmlOptions,
|
|
64
|
+
} from "@ggui-ai/protocol/integrations/mcp-apps";
|
|
77
65
|
|
|
78
66
|
/**
|
|
79
|
-
*
|
|
80
|
-
*
|
|
67
|
+
* The `_meta` key the ggui render bootstrap rides on. Alias of the
|
|
68
|
+
* protocol package's own constant — one spelling, owned upstream.
|
|
81
69
|
*/
|
|
82
|
-
export
|
|
83
|
-
|
|
84
|
-
|
|
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
|
-
}
|
|
70
|
+
export const GGUI_RENDER_META_KEY = MCP_APP_AI_GGUI_RENDER_META_KEY;
|
|
71
|
+
|
|
72
|
+
/** The `ui://` scheme prefix every ggui render resource uri carries. */
|
|
73
|
+
const UI_SCHEME = "ui://";
|
|
93
74
|
|
|
94
75
|
/** A ggui render recognised on a tool result: its resource uri + mount material. */
|
|
95
76
|
export interface GguiRenderDescriptor {
|
|
@@ -105,48 +86,6 @@ export interface GguiRenderDescriptor {
|
|
|
105
86
|
bootstrap?: GguiRenderBootstrap;
|
|
106
87
|
}
|
|
107
88
|
|
|
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
89
|
/**
|
|
151
90
|
* A tool result's `uiData` (+ its `_meta`, when carried) → a ggui render
|
|
152
91
|
* descriptor, or `undefined` for anything that is not one.
|
|
@@ -170,7 +109,14 @@ export function asGguiRender(
|
|
|
170
109
|
};
|
|
171
110
|
}
|
|
172
111
|
|
|
173
|
-
/**
|
|
112
|
+
/**
|
|
113
|
+
* A live `tool-result` AgBlock → its ggui render descriptor, if it is one.
|
|
114
|
+
*
|
|
115
|
+
* NOTE: `@ggui-ai/protocol/integrations/mcp-apps` exports a helper of the
|
|
116
|
+
* same name that narrows a spec-canonical MCP `CallToolResult` instead. This
|
|
117
|
+
* one is the silverprotocol-side twin — the input is the FOLDED block, whose
|
|
118
|
+
* `uiData`/`_meta` carriage is `@silverprotocol/core`'s contract, not ggui's.
|
|
119
|
+
*/
|
|
174
120
|
export function toolResultGguiRender(
|
|
175
121
|
block: Extract<AgBlock, { type: "tool-result" }>,
|
|
176
122
|
): GguiRenderDescriptor | undefined {
|
|
@@ -184,58 +130,6 @@ export function blockGguiRender(block: JsonValue): GguiRenderDescriptor | undefi
|
|
|
184
130
|
return asGguiRender(block.uiData, block._meta);
|
|
185
131
|
}
|
|
186
132
|
|
|
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
133
|
/**
|
|
240
134
|
* A ggui render descriptor → the mountable resource the host's existing
|
|
241
135
|
* mcp-ui path already knows how to mount, or `undefined` when the descriptor
|
|
@@ -244,6 +138,12 @@ export function gguiShellHtml(bootstrap: GguiRenderBootstrap): string {
|
|
|
244
138
|
* The `uri` is the render's REAL `resourceUri` — the shell is the payload, not
|
|
245
139
|
* a renaming of the resource.
|
|
246
140
|
*
|
|
141
|
+
* The shell is built `background: 'transparent'`: every guuey host that
|
|
142
|
+
* mounts through this adapter (widget, portal web, Studio) draws its own
|
|
143
|
+
* card chrome around the iframe, so the host page composits behind the card.
|
|
144
|
+
* The upstream default (`'surface'`) is for standalone served documents —
|
|
145
|
+
* see `GguiShellHtmlOptions` in `@ggui-ai/protocol/integrations/mcp-apps`.
|
|
146
|
+
*
|
|
247
147
|
* **On `_meta` being required to MOUNT (but never to RECOGNISE).** Recognition
|
|
248
148
|
* — "this tool result is a ggui card" — is keyed on `uiData.resourceUri` alone
|
|
249
149
|
* and never waits for anything (see {@link asGguiRender}); nothing in this
|
|
@@ -251,12 +151,10 @@ export function gguiShellHtml(bootstrap: GguiRenderBootstrap): string {
|
|
|
251
151
|
* requirement is ggui's, not ours: its runtime rejects a slice without
|
|
252
152
|
* `runtimeUrl` AND without at least one mode discriminator (`wsUrl`+`wsToken`,
|
|
253
153
|
* `codeUrl`, or `kind`) as `MALFORMED_BOOTSTRAP` and renders nothing. `uiData`
|
|
254
|
-
* carries none of those fields
|
|
255
|
-
*
|
|
256
|
-
*
|
|
257
|
-
*
|
|
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.
|
|
154
|
+
* carries none of those fields, so a bootstrap-less descriptor could only ever
|
|
155
|
+
* produce a blank frame; returning `undefined` and letting the host show its
|
|
156
|
+
* own placeholder is the honest answer, not a deferral. `@silverprotocol/core`'s
|
|
157
|
+
* `Reducer` is what puts `_meta` on the block for a live turn, in-repo, today.
|
|
260
158
|
*/
|
|
261
159
|
export function gguiRenderResource(
|
|
262
160
|
render: GguiRenderDescriptor,
|
|
@@ -265,6 +163,6 @@ export function gguiRenderResource(
|
|
|
265
163
|
return {
|
|
266
164
|
uri: render.resourceUri,
|
|
267
165
|
mimeType: "text/html",
|
|
268
|
-
text: gguiShellHtml(render.bootstrap),
|
|
166
|
+
text: gguiShellHtml(render.bootstrap, { background: "transparent" }),
|
|
269
167
|
};
|
|
270
168
|
}
|