@ggui-ai/protocol-conformance 0.20.0 → 0.22.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,33 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "forward-render-meta-unknown-member",
|
|
3
|
+
"description": "FORWARD tolerance (ggui#1178, VERSION-POLICY §3.6): the `_meta[\"ai.ggui/render\"]` slice a LATER release may write — a top-level member this release does not name (`futureMember`), and an `actionSpec` whose entry carries a member this release does not name (`futureEntryMember`). Both are synthetic by construction: the payload is the NEXT release's, so it carries no landed sha. The read door MUST accept the slice and KEEP the action contract (stripping the unknown entry member and naming it). Dropping the whole `actionSpec` is the violation: a runtime left with no contract has no one-shot guard and validates gestures permissively, which is the fail-open ggui#1178 closes.",
|
|
4
|
+
"direction": "forward",
|
|
5
|
+
"release": {
|
|
6
|
+
"label": "a later ggui release (synthetic): a render slice plus a synthetic top-level member and a synthetic action-entry member",
|
|
7
|
+
"sha": "synthetic"
|
|
8
|
+
},
|
|
9
|
+
"wire": "render-meta",
|
|
10
|
+
"payload": {
|
|
11
|
+
"ai.ggui/render": {
|
|
12
|
+
"sessionId": "r-1",
|
|
13
|
+
"appId": "app-1",
|
|
14
|
+
"runtimeUrl": "/_ggui/iframe-runtime.js",
|
|
15
|
+
"propsJson": "{}",
|
|
16
|
+
"epoch": 0,
|
|
17
|
+
"actionSpec": {
|
|
18
|
+
"submit": {
|
|
19
|
+
"label": "Submit",
|
|
20
|
+
"oneShot": true,
|
|
21
|
+
"futureEntryMember": 1
|
|
22
|
+
},
|
|
23
|
+
"cancel": {
|
|
24
|
+
"label": "Cancel"
|
|
25
|
+
}
|
|
26
|
+
},
|
|
27
|
+
"futureMember": {
|
|
28
|
+
"any": "value"
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
},
|
|
32
|
+
"expect": "accepted"
|
|
33
|
+
}
|
|
@@ -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":"AA8BA,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,EAQhC,CAAC;AA+EpB,4DAA4D;AAC5D,wBAAgB,sBAAsB,IAAI,SAAS,cAAc,EAAE,CAgBlE"}
|
|
@@ -22,6 +22,7 @@ import release2GenerationProfile from './cases/release-2-generation-profile.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
24
|
import forwardAppThemeCarryUnknownMember from './cases/forward-app-theme-carry-unknown-member.json' with { type: 'json' };
|
|
25
|
+
import forwardRenderMetaUnknownMember from './cases/forward-render-meta-unknown-member.json' with { type: 'json' };
|
|
25
26
|
/** The protocol-owned wires the catalog can grade. */
|
|
26
27
|
export const N1_COMPAT_WIRES = ['app-theme', 'app-theme-read', 'app-theme-carry', 'render-meta', 'generation-profile', 'ops-generate-blueprint'];
|
|
27
28
|
/** `backward`: the previous release's payload against today's parser. `forward`: a later release's payload against today's READ door. */
|
|
@@ -81,6 +82,7 @@ export const N1_COMPAT_CASES = [
|
|
|
81
82
|
release2OpsGenerateBlueprint,
|
|
82
83
|
forwardAppThemeUnknownMember,
|
|
83
84
|
forwardAppThemeCarryUnknownMember,
|
|
85
|
+
forwardRenderMetaUnknownMember,
|
|
84
86
|
].map(n1CompatCase);
|
|
85
87
|
function gradeAppTheme(payload) {
|
|
86
88
|
const r = appThemeSchema.safeParse(payload);
|
|
@@ -138,7 +140,11 @@ function gradeOpsGenerateBlueprint(payload) {
|
|
|
138
140
|
}
|
|
139
141
|
function gradeRenderMeta(payload) {
|
|
140
142
|
const themeIssues = [];
|
|
141
|
-
const
|
|
143
|
+
const actionIssues = [];
|
|
144
|
+
const r = parseMcpAppAiGguiRenderMeta(payload, {
|
|
145
|
+
onInvalidTheme: (issues) => themeIssues.push(...issues),
|
|
146
|
+
onInvalidActionSpec: (issues) => actionIssues.push(...issues),
|
|
147
|
+
});
|
|
142
148
|
if (!r.ok)
|
|
143
149
|
return { pass: false, detail: `parseMcpAppAiGguiRenderMeta refused the previous release's slice: ${r.reason}` };
|
|
144
150
|
if (r.meta === undefined)
|
|
@@ -148,7 +154,16 @@ function gradeRenderMeta(payload) {
|
|
|
148
154
|
if (sentTheme && (themeIssues.length > 0 || r.meta.theme === undefined)) {
|
|
149
155
|
return { pass: false, detail: `the previous release's theme was dropped at the read door: ${themeIssues.join('; ') || 'theme absent on the parsed meta'}` };
|
|
150
156
|
}
|
|
151
|
-
|
|
157
|
+
// ggui#1178 — an action contract the slice carried must survive the read door (unknown entry
|
|
158
|
+
// members stripped, never the whole spec dropped).
|
|
159
|
+
const sentActionSpec = isRecord(sent) && sent['actionSpec'] !== undefined;
|
|
160
|
+
if (sentActionSpec && (actionIssues.length > 0 || r.meta.actionSpec === undefined)) {
|
|
161
|
+
return { pass: false, detail: `the slice's action contract was dropped at the read door: ${actionIssues.join('; ') || 'actionSpec absent on the parsed meta'}` };
|
|
162
|
+
}
|
|
163
|
+
return {
|
|
164
|
+
pass: true,
|
|
165
|
+
detail: 'parseMcpAppAiGguiRenderMeta: accepted' + (sentTheme ? ', theme preserved' : '') + (sentActionSpec ? ', actionSpec preserved' : ''),
|
|
166
|
+
};
|
|
152
167
|
}
|
|
153
168
|
/** Grade every N−1 case against the protocol as shipped. */
|
|
154
169
|
export function runN1CompatConformance() {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@ggui-ai/protocol-conformance",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.22.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.22.0"
|
|
103
103
|
},
|
|
104
104
|
"devDependencies": {
|
|
105
105
|
"@types/node": "^22.0.0",
|