@ggui-ai/protocol-conformance 0.18.0 → 0.20.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.
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "forward-app-theme-carry-unknown-member",
|
|
3
|
+
"description": "FORWARD tolerance on the theme CARRY read (ggui#1155, VERSION-POLICY §3.6): a theme GET response exactly as a LATER release's door returns it — the stored document VERBATIM, carrying a synthetic `futureMember` this release does not name, with `interpreted.stripped` naming it. Today's response schema must accept it with the member INTACT, and the names in `interpreted.stripped` must be exactly the members today's parser cannot name. A reader that strips here is the ggui#1124 defect at the door built to prevent it.",
|
|
4
|
+
"direction": "forward",
|
|
5
|
+
"release": {
|
|
6
|
+
"label": "a later ggui release (synthetic): the theme GET's response with a synthetic `futureMember`",
|
|
7
|
+
"sha": "synthetic"
|
|
8
|
+
},
|
|
9
|
+
"wire": "app-theme-carry",
|
|
10
|
+
"payload": {
|
|
11
|
+
"theme": {
|
|
12
|
+
"overlayHash": "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
|
|
13
|
+
"overlays": {
|
|
14
|
+
"light": {
|
|
15
|
+
"--ggui-color-primary-600": "#7c3aed"
|
|
16
|
+
},
|
|
17
|
+
"dark": {
|
|
18
|
+
"--ggui-color-primary-600": "#a78bfa"
|
|
19
|
+
}
|
|
20
|
+
},
|
|
21
|
+
"mode": "dark",
|
|
22
|
+
"name": "violet",
|
|
23
|
+
"futureMember": {
|
|
24
|
+
"any": "shape"
|
|
25
|
+
}
|
|
26
|
+
},
|
|
27
|
+
"interpreted": {
|
|
28
|
+
"stripped": ["futureMember"]
|
|
29
|
+
}
|
|
30
|
+
},
|
|
31
|
+
"expect": "accepted"
|
|
32
|
+
}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/** The protocol-owned wires the catalog can grade. */
|
|
2
|
-
export declare const N1_COMPAT_WIRES: readonly ["app-theme", "app-theme-read", "render-meta", "generation-profile", "ops-generate-blueprint"];
|
|
2
|
+
export declare const N1_COMPAT_WIRES: readonly ["app-theme", "app-theme-read", "app-theme-carry", "render-meta", "generation-profile", "ops-generate-blueprint"];
|
|
3
3
|
/** `backward`: the previous release's payload against today's parser. `forward`: a later release's payload against today's READ door. */
|
|
4
4
|
export declare const N1_COMPAT_DIRECTIONS: readonly ["backward", "forward"];
|
|
5
5
|
export type N1CompatDirection = (typeof N1_COMPAT_DIRECTIONS)[number];
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/n1-compat-conformance/index.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/n1-compat-conformance/index.ts"],"names":[],"mappings":"AA6BA,sDAAsD;AACtD,eAAO,MAAM,eAAe,4HAA6H,CAAC;AAE1J,yIAAyI;AACzI,eAAO,MAAM,oBAAoB,kCAAmC,CAAC;AACrE,MAAM,MAAM,iBAAiB,GAAG,CAAC,OAAO,oBAAoB,CAAC,CAAC,MAAM,CAAC,CAAC;AACtE,MAAM,MAAM,YAAY,GAAG,CAAC,OAAO,eAAe,CAAC,CAAC,MAAM,CAAC,CAAC;AAE5D,MAAM,WAAW,eAAe;IAC9B,mFAAmF;IACnF,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,iKAAiK;IACjK,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAC;IACrB,6EAA6E;IAC7E,QAAQ,CAAC,UAAU,CAAC,EAAE,MAAM,CAAC;CAC9B;AAED,MAAM,WAAW,YAAY;IAC3B,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,WAAW,EAAE,MAAM,CAAC;IAC7B,QAAQ,CAAC,OAAO,EAAE,eAAe,CAAC;IAClC,0CAA0C;IAC1C,QAAQ,CAAC,SAAS,EAAE,iBAAiB,CAAC;IACtC,QAAQ,CAAC,IAAI,EAAE,YAAY,CAAC;IAC5B,4FAA4F;IAC5F,QAAQ,CAAC,OAAO,EAAE,OAAO,CAAC;IAC1B,gGAAgG;IAChG,QAAQ,CAAC,MAAM,EAAE,UAAU,CAAC;CAC7B;AAED,MAAM,WAAW,cAAc;IAC7B,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,SAAS,EAAE,iBAAiB,CAAC;IACtC,QAAQ,CAAC,IAAI,EAAE,OAAO,CAAC;IACvB,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;CACzB;AAgDD,eAAO,MAAM,eAAe,EAAE,SAAS,YAAY,EAOhC,CAAC;AAkEpB,4DAA4D;AAC5D,wBAAgB,sBAAsB,IAAI,SAAS,cAAc,EAAE,CAgBlE"}
|
|
@@ -14,15 +14,16 @@
|
|
|
14
14
|
* Pure-function catalog: no transport, no adopter input — graded on every
|
|
15
15
|
* `runConformance()` and by the kit's own unit lane.
|
|
16
16
|
*/
|
|
17
|
-
import { appGenerationProfileSchema, appThemeSchema, opsGenerateBlueprintInputSchema, parseAppThemeAtReadDoor } from '@ggui-ai/protocol';
|
|
17
|
+
import { appGenerationProfileSchema, appThemeGetResponseSchema, appThemeSchema, opsGenerateBlueprintInputSchema, parseAppThemeAtReadDoor } from '@ggui-ai/protocol';
|
|
18
18
|
import { MCP_APP_AI_GGUI_RENDER_META_KEY, parseMcpAppAiGguiRenderMeta, } from '@ggui-ai/protocol/integrations/mcp-apps';
|
|
19
19
|
import release2AppThemeV2 from './cases/release-2-app-theme-v2.json' with { type: 'json' };
|
|
20
20
|
import release2RenderMeta from './cases/release-2-render-meta.json' with { type: 'json' };
|
|
21
21
|
import release2GenerationProfile from './cases/release-2-generation-profile.json' with { type: 'json' };
|
|
22
22
|
import release2OpsGenerateBlueprint from './cases/release-2-ops-generate-blueprint.json' with { type: 'json' };
|
|
23
23
|
import forwardAppThemeUnknownMember from './cases/forward-app-theme-unknown-member.json' with { type: 'json' };
|
|
24
|
+
import forwardAppThemeCarryUnknownMember from './cases/forward-app-theme-carry-unknown-member.json' with { type: 'json' };
|
|
24
25
|
/** The protocol-owned wires the catalog can grade. */
|
|
25
|
-
export const N1_COMPAT_WIRES = ['app-theme', 'app-theme-read', 'render-meta', 'generation-profile', 'ops-generate-blueprint'];
|
|
26
|
+
export const N1_COMPAT_WIRES = ['app-theme', 'app-theme-read', 'app-theme-carry', 'render-meta', 'generation-profile', 'ops-generate-blueprint'];
|
|
26
27
|
/** `backward`: the previous release's payload against today's parser. `forward`: a later release's payload against today's READ door. */
|
|
27
28
|
export const N1_COMPAT_DIRECTIONS = ['backward', 'forward'];
|
|
28
29
|
function isN1CompatWire(v) {
|
|
@@ -79,6 +80,7 @@ export const N1_COMPAT_CASES = [
|
|
|
79
80
|
release2GenerationProfile,
|
|
80
81
|
release2OpsGenerateBlueprint,
|
|
81
82
|
forwardAppThemeUnknownMember,
|
|
83
|
+
forwardAppThemeCarryUnknownMember,
|
|
82
84
|
].map(n1CompatCase);
|
|
83
85
|
function gradeAppTheme(payload) {
|
|
84
86
|
const r = appThemeSchema.safeParse(payload);
|
|
@@ -97,6 +99,31 @@ function gradeAppThemeRead(payload) {
|
|
|
97
99
|
return { pass: false, detail: 'the read door changed the overlays while stripping' };
|
|
98
100
|
return { pass: true, detail: `parseAppThemeAtReadDoor: kept, stripped [${r.stripped.join(', ')}]` };
|
|
99
101
|
}
|
|
102
|
+
/** Key-order-insensitive JSON — a parser may emit named members first; VERBATIM is about content, not key order. */
|
|
103
|
+
function canonicalJson(v) {
|
|
104
|
+
const sortKeysDeep = (x) => Array.isArray(x) ? x.map(sortKeysDeep) : isRecord(x) ? Object.fromEntries(Object.keys(x).sort().map((k) => [k, sortKeysDeep(x[k])])) : x;
|
|
105
|
+
return JSON.stringify(sortKeysDeep(v));
|
|
106
|
+
}
|
|
107
|
+
function gradeAppThemeCarry(payload) {
|
|
108
|
+
// ggui#1155 — the CARRY read must return the stored document VERBATIM and name,
|
|
109
|
+
// beside it, exactly the members an INTERPRET reader on this release would drop.
|
|
110
|
+
const r = appThemeGetResponseSchema.safeParse(payload);
|
|
111
|
+
if (!r.success)
|
|
112
|
+
return { pass: false, detail: `appThemeGetResponseSchema refused a later release's carry response: ${r.error.issues.map((i) => i.message).join('; ')}` };
|
|
113
|
+
const sentTheme = isRecord(payload) ? payload['theme'] : undefined;
|
|
114
|
+
if (!isRecord(sentTheme))
|
|
115
|
+
return { pass: false, detail: 'the case must carry a non-null theme' };
|
|
116
|
+
if (canonicalJson(r.data.theme) !== canonicalJson(sentTheme))
|
|
117
|
+
return { pass: false, detail: 'the carry read did not return the stored document VERBATIM' };
|
|
118
|
+
const known = new Set(Object.keys(appThemeSchema.shape));
|
|
119
|
+
const unknown = Object.keys(sentTheme).filter((k) => !known.has(k)).sort();
|
|
120
|
+
const named = [...(r.data.interpreted?.stripped ?? [])].sort();
|
|
121
|
+
if (unknown.length === 0)
|
|
122
|
+
return { pass: false, detail: 'the case must carry a member today does not name' };
|
|
123
|
+
if (JSON.stringify(named) !== JSON.stringify(unknown))
|
|
124
|
+
return { pass: false, detail: `interpreted.stripped [${named.join(', ')}] must name exactly the members today cannot name [${unknown.join(', ')}]` };
|
|
125
|
+
return { pass: true, detail: `appThemeGetResponseSchema: verbatim, named [${unknown.join(', ')}]` };
|
|
126
|
+
}
|
|
100
127
|
function gradeGenerationProfile(payload) {
|
|
101
128
|
const r = appGenerationProfileSchema.safeParse(payload);
|
|
102
129
|
return r.success
|
|
@@ -130,11 +157,13 @@ export function runN1CompatConformance() {
|
|
|
130
157
|
? gradeAppTheme(c.payload)
|
|
131
158
|
: c.wire === 'app-theme-read'
|
|
132
159
|
? gradeAppThemeRead(c.payload)
|
|
133
|
-
: c.wire === '
|
|
134
|
-
?
|
|
135
|
-
: c.wire === '
|
|
136
|
-
?
|
|
137
|
-
:
|
|
160
|
+
: c.wire === 'app-theme-carry'
|
|
161
|
+
? gradeAppThemeCarry(c.payload)
|
|
162
|
+
: c.wire === 'render-meta'
|
|
163
|
+
? gradeRenderMeta(c.payload)
|
|
164
|
+
: c.wire === 'generation-profile'
|
|
165
|
+
? gradeGenerationProfile(c.payload)
|
|
166
|
+
: gradeOpsGenerateBlueprint(c.payload);
|
|
138
167
|
return { name: c.name, direction: c.direction, pass: graded.pass, detail: `${c.release.label} (${c.release.sha}) → ${graded.detail}` };
|
|
139
168
|
});
|
|
140
169
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@ggui-ai/protocol-conformance",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.20.0",
|
|
4
4
|
"description": "Conformance test kit for the ggui protocol. Drives a behavioral fixture catalog against a live implementation over WebSocket, a resources/read catalog against a live MCP server, and nine pure-function catalogs (schema, registration, resolution, binding, props-schema, host-helper, refusal-envelope, registry-completeness, transport-refusal) for the gadget, tool-binding, schema-precise-render, host-helper and pre-generation-refusal obligations, reporting a scorecard-style pass / fail / skip. MCP builders adopt this kit to prove their implementation is protocol-conformant.",
|
|
5
5
|
"license": "Apache-2.0",
|
|
6
6
|
"keywords": [
|
|
@@ -99,7 +99,7 @@
|
|
|
99
99
|
},
|
|
100
100
|
"dependencies": {
|
|
101
101
|
"ws": "^8.20.1",
|
|
102
|
-
"@ggui-ai/protocol": "0.
|
|
102
|
+
"@ggui-ai/protocol": "0.20.0"
|
|
103
103
|
},
|
|
104
104
|
"devDependencies": {
|
|
105
105
|
"@types/node": "^22.0.0",
|