@ggui-ai/protocol-conformance 0.15.0 → 0.17.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/README.md +26 -16
- package/dist/cli.d.ts +6 -2
- package/dist/cli.d.ts.map +1 -1
- package/dist/cli.js +52 -6
- package/dist/domain-error-conformance/cases/amend-unknown-session.json +9 -0
- package/dist/domain-error-conformance/cases/consume-unknown-session.json +9 -0
- package/dist/domain-error-conformance/cases/emit-unknown-session.json +9 -0
- package/dist/domain-error-conformance/cases/get-session-unknown-session.json +6 -0
- package/dist/domain-error-conformance/cases/render-unknown-handshake.json +6 -0
- package/dist/domain-error-conformance/cases/update-unknown-session.json +9 -0
- package/dist/domain-error-conformance/index.d.ts +97 -0
- package/dist/domain-error-conformance/index.d.ts.map +1 -0
- package/dist/domain-error-conformance/index.js +97 -0
- package/dist/fixtures/index.d.ts +2 -2
- package/dist/fixtures/index.d.ts.map +1 -1
- package/dist/fixtures/index.js +3 -3
- package/dist/fixtures/{subscribe-tenancy → subscribe-app-scope}/absent-appid-defaults.json +1 -1
- package/dist/fixtures/{subscribe-tenancy → subscribe-app-scope}/app-mismatch.json +1 -1
- package/dist/fixtures/subscribe-app-scope/index.d.ts +6 -0
- package/dist/fixtures/subscribe-app-scope/index.d.ts.map +1 -0
- package/dist/fixtures/{subscribe-tenancy → subscribe-app-scope}/index.js +5 -5
- package/dist/index.d.ts +7 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +7 -1
- package/dist/n1-compat-conformance/cases/forward-app-theme-unknown-member.json +27 -0
- package/dist/n1-compat-conformance/cases/release-2-app-theme-v2.json +24 -0
- package/dist/n1-compat-conformance/cases/release-2-generation-profile.json +16 -0
- package/dist/n1-compat-conformance/cases/release-2-ops-generate-blueprint.json +33 -0
- package/dist/n1-compat-conformance/cases/release-2-render-meta.json +40 -0
- package/dist/n1-compat-conformance/index.d.ts +36 -0
- package/dist/n1-compat-conformance/index.d.ts.map +1 -0
- package/dist/n1-compat-conformance/index.js +140 -0
- package/dist/refusal-envelope-conformance/cases/refuse-after-fix-caller.json +3 -3
- package/dist/refusal-envelope-conformance/cases/refuse-non-render-surface.json +4 -4
- package/dist/run-conformance.d.ts +13 -3
- package/dist/run-conformance.d.ts.map +1 -1
- package/dist/run-conformance.js +83 -7
- package/dist/theme-binding-conformance/cases/app-theme-v1-base.json +18 -0
- package/dist/theme-binding-conformance/cases/app-theme-v1-one-palette.json +11 -0
- package/dist/theme-binding-conformance/cases/app-theme-v2-accepted.json +18 -0
- package/dist/theme-binding-conformance/cases/app-theme-v2-keyframes-accepted.json +16 -0
- package/dist/theme-binding-conformance/cases/app-theme-v2-keyframes-not-keyframes.json +15 -0
- package/dist/theme-binding-conformance/cases/app-theme-v2-label-too-long.json +17 -0
- package/dist/theme-binding-conformance/cases/app-theme-v2-missing-dark.json +13 -0
- package/dist/theme-binding-conformance/cases/app-theme-v2-no-hash.json +15 -0
- package/dist/theme-binding-conformance/cases/app-theme-v2-platform-pin.json +17 -0
- package/dist/theme-binding-conformance/cases/overlay-hash-canonical.json +17 -0
- package/dist/theme-binding-conformance/cases/theme-mode-absence.json +10 -0
- package/dist/theme-binding-conformance/cases/theme-mode-default-when-silent-host.json +10 -0
- package/dist/theme-binding-conformance/cases/theme-mode-order.json +10 -0
- package/dist/theme-binding-conformance/index.d.ts +59 -0
- package/dist/theme-binding-conformance/index.d.ts.map +1 -0
- package/dist/theme-binding-conformance/index.js +110 -0
- package/dist/transport-refusal-conformance/cases/refuse-deprovisioned-endpoint.json +7 -5
- package/dist/transport-refusal-conformance/cases/refuse-render-only-code.json +3 -2
- package/dist/transport-refusal-conformance/index.d.ts +2 -0
- package/dist/transport-refusal-conformance/index.d.ts.map +1 -1
- package/dist/transport-refusal-conformance/index.js +2 -2
- package/dist/types.d.ts +1 -1
- package/package.json +17 -2
- package/dist/fixtures/subscribe-tenancy/index.d.ts +0 -6
- package/dist/fixtures/subscribe-tenancy/index.d.ts.map +0 -1
package/dist/run-conformance.js
CHANGED
|
@@ -47,7 +47,8 @@
|
|
|
47
47
|
* loop is empty today.)
|
|
48
48
|
* l. Record the outcome via reporter + accumulate in result.
|
|
49
49
|
* 3. Fold in the PURE-FUNCTION catalogs (ggui#786) —
|
|
50
|
-
* `refusal-envelope`, `registry-completeness
|
|
50
|
+
* `refusal-envelope`, `registry-completeness`, `transport-refusal`,
|
|
51
|
+
* `domain-error` and `theme-binding` (ggui#987). They grade a
|
|
51
52
|
* caller-supplied projector / registry rather than the wire, so
|
|
52
53
|
* they need no transport; when the caller supplies neither, their
|
|
53
54
|
* rows are reported SKIPPED with that reason so an ungraded
|
|
@@ -66,9 +67,14 @@ import { openWsTransport } from './ws-transport.js';
|
|
|
66
67
|
import { refusalEnvelopeCases, runRefusalEnvelopeConformance, } from './refusal-envelope-conformance/index.js';
|
|
67
68
|
import { registryCompletenessPins, runRegistryCompletenessConformance, } from './registry-completeness/index.js';
|
|
68
69
|
import { runTransportRefusalConformance, transportRefusalCases, } from './transport-refusal-conformance/index.js';
|
|
70
|
+
import { runThemeBindingConformance } from './theme-binding-conformance/index.js';
|
|
71
|
+
import { runN1CompatConformance } from './n1-compat-conformance/index.js';
|
|
72
|
+
import { domainErrorCases, runDomainErrorConformance, } from './domain-error-conformance/index.js';
|
|
69
73
|
/**
|
|
70
|
-
* Row-name prefixes for the
|
|
71
|
-
*
|
|
74
|
+
* Row-name prefixes for the catalogs the runner folds into a run beside
|
|
75
|
+
* the WebSocket fixtures: three grade an in-process function or a data
|
|
76
|
+
* table; `domain-error` (ggui#880) drives live `tools/call` scenarios
|
|
77
|
+
* through an adopter-supplied driver. Prefixed so a catalog row can never collide with a
|
|
72
78
|
* WebSocket fixture name, and so the reporter can group them without a
|
|
73
79
|
* second registry of names.
|
|
74
80
|
*/
|
|
@@ -76,6 +82,9 @@ export const PURE_FUNCTION_CATALOG_SLUGS = [
|
|
|
76
82
|
'refusal-envelope',
|
|
77
83
|
'registry-completeness',
|
|
78
84
|
'transport-refusal',
|
|
85
|
+
'domain-error',
|
|
86
|
+
'theme-binding',
|
|
87
|
+
'n1-compat',
|
|
79
88
|
];
|
|
80
89
|
// =============================================================================
|
|
81
90
|
// Runner
|
|
@@ -113,7 +122,7 @@ export async function runConformance(config) {
|
|
|
113
122
|
break;
|
|
114
123
|
}
|
|
115
124
|
}
|
|
116
|
-
runPureFunctionCatalogs(config, reporter, { passed, failed, skipped });
|
|
125
|
+
await runPureFunctionCatalogs(config, reporter, { passed, failed, skipped });
|
|
117
126
|
const result = {
|
|
118
127
|
passed,
|
|
119
128
|
failed,
|
|
@@ -138,7 +147,7 @@ export async function runConformance(config) {
|
|
|
138
147
|
* absent input produces a SKIP row naming what to supply rather than
|
|
139
148
|
* nothing at all.
|
|
140
149
|
*/
|
|
141
|
-
function runPureFunctionCatalogs(config, reporter, buckets) {
|
|
150
|
+
async function runPureFunctionCatalogs(config, reporter, buckets) {
|
|
142
151
|
const only = config.only;
|
|
143
152
|
const wanted = (name) => only === undefined || only.length === 0 || only.includes(name);
|
|
144
153
|
const pass = (name) => {
|
|
@@ -201,6 +210,33 @@ function runPureFunctionCatalogs(config, reporter, buckets) {
|
|
|
201
210
|
});
|
|
202
211
|
}
|
|
203
212
|
}
|
|
213
|
+
// ── domain-error (ggui#880) ──
|
|
214
|
+
const toolCallDriver = config.toolCallDriver;
|
|
215
|
+
if (toolCallDriver === undefined) {
|
|
216
|
+
for (const testCase of domainErrorCases) {
|
|
217
|
+
skip(`domain-error/${testCase.name}`, 'no `toolCallDriver` supplied — pass one to runConformance(), or `--tool-call-driver <module>` on the CLI, to grade SPEC §7.9\u2019s Plane-2 wire text');
|
|
218
|
+
}
|
|
219
|
+
}
|
|
220
|
+
else {
|
|
221
|
+
// A live driver is real calls: drive only the rows `only` selects.
|
|
222
|
+
const selected = domainErrorCases.filter((testCase) => wanted(`domain-error/${testCase.name}`));
|
|
223
|
+
const graded = selected.length === 0
|
|
224
|
+
? { passed: [], failed: [], skipped: [] }
|
|
225
|
+
: await runDomainErrorConformance(toolCallDriver, { cases: selected });
|
|
226
|
+
for (const name of graded.passed)
|
|
227
|
+
pass(`domain-error/${name}`);
|
|
228
|
+
for (const skipped of graded.skipped)
|
|
229
|
+
skip(`domain-error/${skipped.name}`, skipped.reason);
|
|
230
|
+
for (const mismatch of graded.failed) {
|
|
231
|
+
fail({
|
|
232
|
+
name: `domain-error/${mismatch.name}`,
|
|
233
|
+
criterion: `Plane-2 wire text (SPEC §7.9): ${mismatch.criterion}`,
|
|
234
|
+
expected: mismatch.expected,
|
|
235
|
+
received: mismatch.actual,
|
|
236
|
+
message: 'the raw tools/call result does not carry the registered slug as the leading token of its text',
|
|
237
|
+
});
|
|
238
|
+
}
|
|
239
|
+
}
|
|
204
240
|
// ── registry-completeness ──
|
|
205
241
|
const registry = config.refusalRegistry;
|
|
206
242
|
if (registry === undefined) {
|
|
@@ -222,6 +258,46 @@ function runPureFunctionCatalogs(config, reporter, buckets) {
|
|
|
222
258
|
});
|
|
223
259
|
}
|
|
224
260
|
}
|
|
261
|
+
// ── theme-binding (ggui#987) ──
|
|
262
|
+
// The theming wave's arbiter: graded against the protocol's own functions
|
|
263
|
+
// (`appThemeSchema`, `effectiveThemeMode`, `canonicalOverlayHash`), so
|
|
264
|
+
// there is no adopter input to supply and no reason to skip — every run
|
|
265
|
+
// reports it. `only` still selects rows.
|
|
266
|
+
for (const graded of await runThemeBindingConformance()) {
|
|
267
|
+
const name = `theme-binding/${graded.name}`;
|
|
268
|
+
if (graded.pass) {
|
|
269
|
+
pass(name);
|
|
270
|
+
}
|
|
271
|
+
else {
|
|
272
|
+
fail({
|
|
273
|
+
name,
|
|
274
|
+
criterion: 'theme binding (SPEC "Theme binding — the normative total order", ggui#987)',
|
|
275
|
+
expected: 'the case\u2019s expectation',
|
|
276
|
+
received: graded.detail,
|
|
277
|
+
message: graded.detail,
|
|
278
|
+
});
|
|
279
|
+
}
|
|
280
|
+
}
|
|
281
|
+
// ── n1-compat (ggui#1014 §3.6) ──
|
|
282
|
+
// Previous-release payloads against today's protocol parsers: no adopter
|
|
283
|
+
// input, never skipped; a failing row is a receiver that broke N−1.
|
|
284
|
+
for (const graded of runN1CompatConformance()) {
|
|
285
|
+
const name = `n1-compat/${graded.name}`;
|
|
286
|
+
if (graded.pass) {
|
|
287
|
+
pass(name);
|
|
288
|
+
}
|
|
289
|
+
else {
|
|
290
|
+
fail({
|
|
291
|
+
name,
|
|
292
|
+
criterion: 'N−1 wire compatibility (VERSION-POLICY §3.6, ggui#1014)',
|
|
293
|
+
expected: graded.direction === 'forward'
|
|
294
|
+
? 'a later release\u2019s payload KEPT by today\u2019s read door — the unknown top-level member stripped and named'
|
|
295
|
+
: 'the previous release\u2019s payload accepted by today\u2019s parser',
|
|
296
|
+
received: graded.detail,
|
|
297
|
+
message: graded.detail,
|
|
298
|
+
});
|
|
299
|
+
}
|
|
300
|
+
}
|
|
225
301
|
}
|
|
226
302
|
async function runOneFixture(fixture, config, _reporter) {
|
|
227
303
|
const fixtureStarted = Date.now();
|
|
@@ -684,8 +760,8 @@ function slugToCriterion(slug) {
|
|
|
684
760
|
return 'SPEC §4.4 reserved-channel authority';
|
|
685
761
|
case 'schema-version-handshake':
|
|
686
762
|
return 'Protocol #3 version negotiation';
|
|
687
|
-
case 'subscribe-
|
|
688
|
-
return 'SPEC §12.2 subscribe
|
|
763
|
+
case 'subscribe-app-scope':
|
|
764
|
+
return 'SPEC §12.2 subscribe app scope — appId MUST match the bound app (§12.2.3 APP_MISMATCH)';
|
|
689
765
|
default:
|
|
690
766
|
return slug;
|
|
691
767
|
}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "app-theme-v1-base",
|
|
3
|
+
"description": "A v1 overlay carrying a registered `base` ladder is REFUSED since ggui#987 (D2 = B): the registration tier and `base` are gone. Accepted before #987 (promoted, then failed \u2014 the \u00a71.1 receipt).",
|
|
4
|
+
"theme": {
|
|
5
|
+
"mode": "dark",
|
|
6
|
+
"cssVariables": {},
|
|
7
|
+
"base": {
|
|
8
|
+
"documentHash": "0000000000000000000000000000000000000000000000000000000000000000",
|
|
9
|
+
"light": {
|
|
10
|
+
"--ggui-color-primary-500": "#3355ff"
|
|
11
|
+
},
|
|
12
|
+
"dark": {
|
|
13
|
+
"--ggui-color-primary-500": "#99aaff"
|
|
14
|
+
}
|
|
15
|
+
}
|
|
16
|
+
},
|
|
17
|
+
"expect": "refused"
|
|
18
|
+
}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "app-theme-v1-one-palette",
|
|
3
|
+
"description": "The v1 overlay wire \u2014 a pinned mode and ONE cssVariables palette \u2014 is REFUSED since ggui#987: an overlay that could not follow the host's mode is not accepted; both projections are required. Accepted before #987 (promoted, then failed \u2014 the \u00a71.1 receipt).",
|
|
4
|
+
"theme": {
|
|
5
|
+
"mode": "light",
|
|
6
|
+
"cssVariables": {
|
|
7
|
+
"--ggui-color-primary-500": "#3355ff"
|
|
8
|
+
}
|
|
9
|
+
},
|
|
10
|
+
"expect": "refused"
|
|
11
|
+
}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "app-theme-v2-accepted",
|
|
3
|
+
"description": "The v2 overlay: both projections, the attestation, an optional default mode and a label. Accepted.",
|
|
4
|
+
"theme": {
|
|
5
|
+
"overlayHash": "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
|
|
6
|
+
"overlays": {
|
|
7
|
+
"light": {
|
|
8
|
+
"--ggui-color-primary-500": "#3355ff"
|
|
9
|
+
},
|
|
10
|
+
"dark": {
|
|
11
|
+
"--ggui-color-primary-500": "#99aaff"
|
|
12
|
+
}
|
|
13
|
+
},
|
|
14
|
+
"mode": "light",
|
|
15
|
+
"name": "ocean"
|
|
16
|
+
},
|
|
17
|
+
"expect": "accepted"
|
|
18
|
+
}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "app-theme-v2-keyframes-accepted",
|
|
3
|
+
"description": "Per-mode keyframes text that is exactly `@keyframes <ident> { … }` blocks (two blocks, a comment, frame selectors of every kind) is ACCEPTED by appThemeSchema — the slot carries what the card injects verbatim after the variables (ggui#987 §3.3, follower D).",
|
|
4
|
+
"theme": {
|
|
5
|
+
"overlays": {
|
|
6
|
+
"light": { "--ggui-color-primary-500": "#3355ff" },
|
|
7
|
+
"dark": { "--ggui-color-primary-500": "#99aaff" }
|
|
8
|
+
},
|
|
9
|
+
"overlayHash": "0000000000000000000000000000000000000000000000000000000000000000",
|
|
10
|
+
"keyframes": {
|
|
11
|
+
"light": "/* entrance */ @keyframes entrance { 0% { opacity: 0; transform: translateY(4px) } 100% { opacity: 1; transform: none } }\n@keyframes accent-pulse{0%,100%{opacity:1}50%{opacity:.7}}",
|
|
12
|
+
"dark": "@keyframes spin { from { transform: rotate(0deg) } to { transform: rotate(360deg) } }"
|
|
13
|
+
}
|
|
14
|
+
},
|
|
15
|
+
"expect": "accepted"
|
|
16
|
+
}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "app-theme-v2-keyframes-not-keyframes",
|
|
3
|
+
"description": "Keyframes text that carries anything other than `@keyframes` blocks — here a bare `body { … }` rule after a valid block — is REFUSED by appThemeSchema: the slot is bounded to what it is for, so the theme principal cannot ship arbitrary same-document CSS through it (ggui#987 §3.4, follower D).",
|
|
4
|
+
"theme": {
|
|
5
|
+
"overlays": {
|
|
6
|
+
"light": { "--ggui-color-primary-500": "#3355ff" },
|
|
7
|
+
"dark": { "--ggui-color-primary-500": "#99aaff" }
|
|
8
|
+
},
|
|
9
|
+
"overlayHash": "0000000000000000000000000000000000000000000000000000000000000000",
|
|
10
|
+
"keyframes": {
|
|
11
|
+
"light": "@keyframes spin { to { transform: rotate(360deg) } } body { display: none }"
|
|
12
|
+
}
|
|
13
|
+
},
|
|
14
|
+
"expect": "refused"
|
|
15
|
+
}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "app-theme-v2-label-too-long",
|
|
3
|
+
"description": "A stamped label carrying a FULL sha256 after the prefix is 77 chars and exceeds appThemeSchema's 64-char `name` cap \u2014 REFUSED at the door. The seam ggui#987 flagged for the platform's pin shape.",
|
|
4
|
+
"theme": {
|
|
5
|
+
"overlays": {
|
|
6
|
+
"light": {
|
|
7
|
+
"--ggui-color-primary-500": "#3355ff"
|
|
8
|
+
},
|
|
9
|
+
"dark": {
|
|
10
|
+
"--ggui-color-primary-500": "#99aaff"
|
|
11
|
+
}
|
|
12
|
+
},
|
|
13
|
+
"overlayHash": "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb",
|
|
14
|
+
"name": "guuey-app-v2.dddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddd"
|
|
15
|
+
},
|
|
16
|
+
"expect": "refused"
|
|
17
|
+
}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "app-theme-v2-missing-dark",
|
|
3
|
+
"description": "An overlay with only one projection cannot follow the host's mode \u2014 REFUSED.",
|
|
4
|
+
"theme": {
|
|
5
|
+
"overlayHash": "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
|
|
6
|
+
"overlays": {
|
|
7
|
+
"light": {
|
|
8
|
+
"--ggui-color-primary-500": "#3355ff"
|
|
9
|
+
}
|
|
10
|
+
}
|
|
11
|
+
},
|
|
12
|
+
"expect": "refused"
|
|
13
|
+
}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "app-theme-v2-no-hash",
|
|
3
|
+
"description": "The attestation is REQUIRED: an overlay without `overlayHash` is refused at the door.",
|
|
4
|
+
"theme": {
|
|
5
|
+
"overlays": {
|
|
6
|
+
"light": {
|
|
7
|
+
"--ggui-color-primary-500": "#3355ff"
|
|
8
|
+
},
|
|
9
|
+
"dark": {
|
|
10
|
+
"--ggui-color-primary-500": "#99aaff"
|
|
11
|
+
}
|
|
12
|
+
}
|
|
13
|
+
},
|
|
14
|
+
"expect": "refused"
|
|
15
|
+
}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "app-theme-v2-platform-pin",
|
|
3
|
+
"description": "The exact overlay shape a platform's -v2 composer sends (ggui#987 \u00a79; guuey#1128): both projections, the attestation, and a label of the form `<prefix>.<hash-prefix>`. `name` is capped at 64 chars by appThemeSchema, so a stamped label must keep its hash segment to 51 chars after a 13-char prefix. Accepted.",
|
|
4
|
+
"theme": {
|
|
5
|
+
"overlays": {
|
|
6
|
+
"light": {
|
|
7
|
+
"--ggui-color-primary-500": "#3355ff"
|
|
8
|
+
},
|
|
9
|
+
"dark": {
|
|
10
|
+
"--ggui-color-primary-500": "#99aaff"
|
|
11
|
+
}
|
|
12
|
+
},
|
|
13
|
+
"overlayHash": "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb",
|
|
14
|
+
"name": "guuey-app-v2.ccccccccccccccccccccccccccccccccccccccccccccccccccc"
|
|
15
|
+
},
|
|
16
|
+
"expect": "accepted"
|
|
17
|
+
}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "overlay-hash-canonical",
|
|
3
|
+
"description": "canonicalOverlayHash is ONE function on every minter and door: keys sorted recursively, no whitespace, absent fields omitted, sha256 lowercase hex. The expected value was computed independently with node:crypto over the canonical JSON; the input's keys are deliberately unsorted.",
|
|
4
|
+
"input": {
|
|
5
|
+
"overlays": {
|
|
6
|
+
"light": {
|
|
7
|
+
"--ggui-color-primary-500": "#3355ff",
|
|
8
|
+
"--ggui-color-ground": "#ffffff"
|
|
9
|
+
},
|
|
10
|
+
"dark": {
|
|
11
|
+
"--ggui-color-primary-500": "#99aaff",
|
|
12
|
+
"--ggui-color-ground": "#0e1014"
|
|
13
|
+
}
|
|
14
|
+
}
|
|
15
|
+
},
|
|
16
|
+
"expect": "7cc8c947beeae43de91a8e3eedc182a739ae134e3a3957958ebee4ec6cf25e96"
|
|
17
|
+
}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "theme-mode-absence",
|
|
3
|
+
"description": "The absence law (ggui#551): when no layer resolves, the projection is undefined \u2014 never a light default.",
|
|
4
|
+
"sources": {
|
|
5
|
+
"stamped": null,
|
|
6
|
+
"sessionSidecar": null,
|
|
7
|
+
"hostAnnounced": null
|
|
8
|
+
},
|
|
9
|
+
"expect": null
|
|
10
|
+
}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "theme-mode-default-when-silent-host",
|
|
3
|
+
"description": "A host that announces nothing leaves the stamp (then the sidecar) to fill the mode \u2014 the defaults still apply.",
|
|
4
|
+
"sources": {
|
|
5
|
+
"stamped": "light",
|
|
6
|
+
"sessionSidecar": "dark",
|
|
7
|
+
"hostAnnounced": null
|
|
8
|
+
},
|
|
9
|
+
"expect": "light"
|
|
10
|
+
}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "theme-mode-order",
|
|
3
|
+
"description": "The client projection of the themeMode total order since ggui#987 (D4 = A, 'follow the widget'): with a stamped mode and a host announce present, the HOST wins \u2014 the embedding host owns runtime mode; the stamp and the sidecar are defaults. Before #987 this case expected 'light' (the stamp); it was promoted into the kit at that version and FAILED when the order flipped \u2014 the VERSION-POLICY \u00a71.1 receipt that names the flip breaking.",
|
|
4
|
+
"sources": {
|
|
5
|
+
"stamped": "light",
|
|
6
|
+
"sessionSidecar": null,
|
|
7
|
+
"hostAnnounced": "dark"
|
|
8
|
+
},
|
|
9
|
+
"expect": "dark"
|
|
10
|
+
}
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Theme-binding conformance — a pure-function catalog (ggui#987).
|
|
3
|
+
*
|
|
4
|
+
* Three contracts the kit grades directly against `@ggui-ai/protocol`, so
|
|
5
|
+
* their breaking-ness is decided by a fixture and not by prose
|
|
6
|
+
* (VERSION-POLICY §2 — the kit is the arbiter):
|
|
7
|
+
*
|
|
8
|
+
* - `theme-mode-*` — the CLIENT projection of the `themeMode` total
|
|
9
|
+
* order, `effectiveThemeMode` (`integrations/theme-binding.ts`): the
|
|
10
|
+
* embedding host owns runtime mode (D4).
|
|
11
|
+
* - `app-theme-*` — which overlay shapes `appThemeSchema` accepts: both
|
|
12
|
+
* projections and the attestation required; the v1 one-palette wire and
|
|
13
|
+
* `base` refused.
|
|
14
|
+
* - `overlay-hash-*` — `canonicalOverlayHash` against an independently
|
|
15
|
+
* computed vector.
|
|
16
|
+
*
|
|
17
|
+
* History (the §1.1 receipt): `theme-mode-order`, `app-theme-v1-one-palette`
|
|
18
|
+
* and `app-theme-v1-base` were promoted from the unit pins at the version
|
|
19
|
+
* BEFORE the revision, passed there, and failed when it landed — that is
|
|
20
|
+
* what named the revision breaking. Like `../refusal-envelope-conformance`,
|
|
21
|
+
* the cases are raw JSON under `./cases/` and are NOT registered in
|
|
22
|
+
* `fixturesByContract`; the entry point is the runner below and its test.
|
|
23
|
+
*/
|
|
24
|
+
import { canonicalOverlayHash } from '@ggui-ai/protocol';
|
|
25
|
+
type Opinion = 'light' | 'dark';
|
|
26
|
+
export interface ThemeModeCase {
|
|
27
|
+
readonly name: string;
|
|
28
|
+
readonly description: string;
|
|
29
|
+
readonly sources: {
|
|
30
|
+
readonly stamped: Opinion | null;
|
|
31
|
+
readonly sessionSidecar: Opinion | null;
|
|
32
|
+
readonly hostAnnounced: Opinion | null;
|
|
33
|
+
};
|
|
34
|
+
readonly expect: Opinion | null;
|
|
35
|
+
}
|
|
36
|
+
export interface AppThemeCase {
|
|
37
|
+
readonly name: string;
|
|
38
|
+
readonly description: string;
|
|
39
|
+
readonly theme: unknown;
|
|
40
|
+
readonly expect: 'accepted' | 'refused';
|
|
41
|
+
}
|
|
42
|
+
export interface OverlayHashCase {
|
|
43
|
+
readonly name: string;
|
|
44
|
+
readonly description: string;
|
|
45
|
+
readonly input: Parameters<typeof canonicalOverlayHash>[0];
|
|
46
|
+
readonly expect: string;
|
|
47
|
+
}
|
|
48
|
+
export declare const THEME_MODE_CASES: readonly ThemeModeCase[];
|
|
49
|
+
export declare const APP_THEME_CASES: readonly AppThemeCase[];
|
|
50
|
+
export declare const OVERLAY_HASH_CASES: readonly OverlayHashCase[];
|
|
51
|
+
export interface ThemeBindingResult {
|
|
52
|
+
readonly name: string;
|
|
53
|
+
readonly pass: boolean;
|
|
54
|
+
readonly detail: string;
|
|
55
|
+
}
|
|
56
|
+
/** Grade every theme-binding case against the protocol's own functions. */
|
|
57
|
+
export declare function runThemeBindingConformance(): Promise<readonly ThemeBindingResult[]>;
|
|
58
|
+
export {};
|
|
59
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/theme-binding-conformance/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;GAsBG;AACH,OAAO,EAAkB,oBAAoB,EAAE,MAAM,mBAAmB,CAAC;AAiBzE,KAAK,OAAO,GAAG,OAAO,GAAG,MAAM,CAAC;AAEhC,MAAM,WAAW,aAAa;IAC5B,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,WAAW,EAAE,MAAM,CAAC;IAC7B,QAAQ,CAAC,OAAO,EAAE;QAChB,QAAQ,CAAC,OAAO,EAAE,OAAO,GAAG,IAAI,CAAC;QACjC,QAAQ,CAAC,cAAc,EAAE,OAAO,GAAG,IAAI,CAAC;QACxC,QAAQ,CAAC,aAAa,EAAE,OAAO,GAAG,IAAI,CAAC;KACxC,CAAC;IACF,QAAQ,CAAC,MAAM,EAAE,OAAO,GAAG,IAAI,CAAC;CACjC;AAED,MAAM,WAAW,YAAY;IAC3B,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,WAAW,EAAE,MAAM,CAAC;IAC7B,QAAQ,CAAC,KAAK,EAAE,OAAO,CAAC;IACxB,QAAQ,CAAC,MAAM,EAAE,UAAU,GAAG,SAAS,CAAC;CACzC;AAED,MAAM,WAAW,eAAe;IAC9B,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,WAAW,EAAE,MAAM,CAAC;IAC7B,QAAQ,CAAC,KAAK,EAAE,UAAU,CAAC,OAAO,oBAAoB,CAAC,CAAC,CAAC,CAAC,CAAC;IAC3D,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;CACzB;AAkDD,eAAO,MAAM,gBAAgB,EAAE,SAAS,aAAa,EAA0F,CAAC;AAChJ,eAAO,MAAM,eAAe,EAAE,SAAS,YAAY,EAAuO,CAAC;AAC3R,eAAO,MAAM,kBAAkB,EAAE,SAAS,eAAe,EAAgD,CAAC;AAE1G,MAAM,WAAW,kBAAkB;IACjC,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,IAAI,EAAE,OAAO,CAAC;IACvB,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;CACzB;AAID,2EAA2E;AAC3E,wBAAsB,0BAA0B,IAAI,OAAO,CAAC,SAAS,kBAAkB,EAAE,CAAC,CAqBzF"}
|
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Theme-binding conformance — a pure-function catalog (ggui#987).
|
|
3
|
+
*
|
|
4
|
+
* Three contracts the kit grades directly against `@ggui-ai/protocol`, so
|
|
5
|
+
* their breaking-ness is decided by a fixture and not by prose
|
|
6
|
+
* (VERSION-POLICY §2 — the kit is the arbiter):
|
|
7
|
+
*
|
|
8
|
+
* - `theme-mode-*` — the CLIENT projection of the `themeMode` total
|
|
9
|
+
* order, `effectiveThemeMode` (`integrations/theme-binding.ts`): the
|
|
10
|
+
* embedding host owns runtime mode (D4).
|
|
11
|
+
* - `app-theme-*` — which overlay shapes `appThemeSchema` accepts: both
|
|
12
|
+
* projections and the attestation required; the v1 one-palette wire and
|
|
13
|
+
* `base` refused.
|
|
14
|
+
* - `overlay-hash-*` — `canonicalOverlayHash` against an independently
|
|
15
|
+
* computed vector.
|
|
16
|
+
*
|
|
17
|
+
* History (the §1.1 receipt): `theme-mode-order`, `app-theme-v1-one-palette`
|
|
18
|
+
* and `app-theme-v1-base` were promoted from the unit pins at the version
|
|
19
|
+
* BEFORE the revision, passed there, and failed when it landed — that is
|
|
20
|
+
* what named the revision breaking. Like `../refusal-envelope-conformance`,
|
|
21
|
+
* the cases are raw JSON under `./cases/` and are NOT registered in
|
|
22
|
+
* `fixturesByContract`; the entry point is the runner below and its test.
|
|
23
|
+
*/
|
|
24
|
+
import { appThemeSchema, canonicalOverlayHash } from '@ggui-ai/protocol';
|
|
25
|
+
import { effectiveThemeMode } from '@ggui-ai/protocol/integrations/theme-binding';
|
|
26
|
+
import appThemeV1Base from './cases/app-theme-v1-base.json' with { type: 'json' };
|
|
27
|
+
import appThemeV1OnePalette from './cases/app-theme-v1-one-palette.json' with { type: 'json' };
|
|
28
|
+
import appThemeV2Accepted from './cases/app-theme-v2-accepted.json' with { type: 'json' };
|
|
29
|
+
import appThemeV2MissingDark from './cases/app-theme-v2-missing-dark.json' with { type: 'json' };
|
|
30
|
+
import appThemeV2LabelTooLong from './cases/app-theme-v2-label-too-long.json' with { type: 'json' };
|
|
31
|
+
import appThemeV2KeyframesAccepted from './cases/app-theme-v2-keyframes-accepted.json' with { type: 'json' };
|
|
32
|
+
import appThemeV2KeyframesNotKeyframes from './cases/app-theme-v2-keyframes-not-keyframes.json' with { type: 'json' };
|
|
33
|
+
import appThemeV2NoHash from './cases/app-theme-v2-no-hash.json' with { type: 'json' };
|
|
34
|
+
import appThemeV2PlatformPin from './cases/app-theme-v2-platform-pin.json' with { type: 'json' };
|
|
35
|
+
import overlayHashCanonical from './cases/overlay-hash-canonical.json' with { type: 'json' };
|
|
36
|
+
import themeModeAbsence from './cases/theme-mode-absence.json' with { type: 'json' };
|
|
37
|
+
import themeModeDefaultWhenSilentHost from './cases/theme-mode-default-when-silent-host.json' with { type: 'json' };
|
|
38
|
+
import themeModeOrder from './cases/theme-mode-order.json' with { type: 'json' };
|
|
39
|
+
const isOpinion = (v) => v === 'light' || v === 'dark';
|
|
40
|
+
const opinionOrNull = (v, where) => {
|
|
41
|
+
if (v === null || isOpinion(v))
|
|
42
|
+
return v;
|
|
43
|
+
throw new Error(`theme-binding case: ${where} must be 'light' | 'dark' | null, got ${JSON.stringify(v)}`);
|
|
44
|
+
};
|
|
45
|
+
const isRecord = (v) => typeof v === 'object' && v !== null;
|
|
46
|
+
const isVarMap = (v) => isRecord(v) && Object.values(v).every((x) => typeof x === 'string');
|
|
47
|
+
function themeModeCase(raw) {
|
|
48
|
+
if (!isRecord(raw) || typeof raw['name'] !== 'string' || typeof raw['description'] !== 'string' || !isRecord(raw['sources']))
|
|
49
|
+
throw new Error('theme-binding case: malformed theme-mode case');
|
|
50
|
+
const src = raw['sources'];
|
|
51
|
+
return {
|
|
52
|
+
name: raw['name'],
|
|
53
|
+
description: raw['description'],
|
|
54
|
+
sources: {
|
|
55
|
+
stamped: opinionOrNull(src['stamped'], 'sources.stamped'),
|
|
56
|
+
sessionSidecar: opinionOrNull(src['sessionSidecar'], 'sources.sessionSidecar'),
|
|
57
|
+
hostAnnounced: opinionOrNull(src['hostAnnounced'], 'sources.hostAnnounced'),
|
|
58
|
+
},
|
|
59
|
+
expect: opinionOrNull(raw['expect'], 'expect'),
|
|
60
|
+
};
|
|
61
|
+
}
|
|
62
|
+
function appThemeCase(raw) {
|
|
63
|
+
if (!isRecord(raw) || typeof raw['name'] !== 'string' || typeof raw['description'] !== 'string' || !('theme' in raw))
|
|
64
|
+
throw new Error('theme-binding case: malformed app-theme case');
|
|
65
|
+
const expect = raw['expect'];
|
|
66
|
+
if (expect !== 'accepted' && expect !== 'refused')
|
|
67
|
+
throw new Error(`theme-binding case: expect must be accepted|refused, got ${JSON.stringify(expect)}`);
|
|
68
|
+
return { name: raw['name'], description: raw['description'], theme: raw['theme'], expect };
|
|
69
|
+
}
|
|
70
|
+
function overlayHashCase(raw) {
|
|
71
|
+
if (!isRecord(raw) || typeof raw['name'] !== 'string' || typeof raw['description'] !== 'string' || typeof raw['expect'] !== 'string' || !isRecord(raw['input']))
|
|
72
|
+
throw new Error('theme-binding case: malformed overlay-hash case');
|
|
73
|
+
const input = raw['input'];
|
|
74
|
+
const overlays = input['overlays'];
|
|
75
|
+
if (!isRecord(overlays) || !isVarMap(overlays['light']) || !isVarMap(overlays['dark']))
|
|
76
|
+
throw new Error('theme-binding case: overlay-hash input needs overlays.light and overlays.dark');
|
|
77
|
+
return {
|
|
78
|
+
name: raw['name'],
|
|
79
|
+
description: raw['description'],
|
|
80
|
+
input: { overlays: { light: overlays['light'], dark: overlays['dark'] } },
|
|
81
|
+
expect: raw['expect'],
|
|
82
|
+
};
|
|
83
|
+
}
|
|
84
|
+
export const THEME_MODE_CASES = [themeModeOrder, themeModeAbsence, themeModeDefaultWhenSilentHost].map(themeModeCase);
|
|
85
|
+
export const APP_THEME_CASES = [appThemeV1OnePalette, appThemeV1Base, appThemeV2Accepted, appThemeV2MissingDark, appThemeV2NoHash, appThemeV2PlatformPin, appThemeV2LabelTooLong, appThemeV2KeyframesAccepted, appThemeV2KeyframesNotKeyframes].map(appThemeCase);
|
|
86
|
+
export const OVERLAY_HASH_CASES = [overlayHashCanonical].map(overlayHashCase);
|
|
87
|
+
const nullable = (v) => (v === null ? undefined : v);
|
|
88
|
+
/** Grade every theme-binding case against the protocol's own functions. */
|
|
89
|
+
export async function runThemeBindingConformance() {
|
|
90
|
+
const results = [];
|
|
91
|
+
for (const c of THEME_MODE_CASES) {
|
|
92
|
+
const got = effectiveThemeMode({
|
|
93
|
+
stamped: nullable(c.sources.stamped),
|
|
94
|
+
sessionSidecar: nullable(c.sources.sessionSidecar),
|
|
95
|
+
hostAnnounced: nullable(c.sources.hostAnnounced),
|
|
96
|
+
});
|
|
97
|
+
const want = nullable(c.expect);
|
|
98
|
+
results.push({ name: c.name, pass: got === want, detail: `effectiveThemeMode(${JSON.stringify(c.sources)}) = ${String(got)}; expected ${String(want)}` });
|
|
99
|
+
}
|
|
100
|
+
for (const c of APP_THEME_CASES) {
|
|
101
|
+
const parsed = appThemeSchema.safeParse(c.theme);
|
|
102
|
+
const got = parsed.success ? 'accepted' : 'refused';
|
|
103
|
+
results.push({ name: c.name, pass: got === c.expect, detail: `appThemeSchema: ${got}; expected ${c.expect}${parsed.success ? '' : ` (${parsed.error.issues.map((i) => i.message).join('; ')})`}` });
|
|
104
|
+
}
|
|
105
|
+
for (const c of OVERLAY_HASH_CASES) {
|
|
106
|
+
const got = await canonicalOverlayHash(c.input);
|
|
107
|
+
results.push({ name: c.name, pass: got === c.expect, detail: `canonicalOverlayHash = ${got}; expected ${c.expect}` });
|
|
108
|
+
}
|
|
109
|
+
return results;
|
|
110
|
+
}
|
|
@@ -1,11 +1,12 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "refuse-deprovisioned-endpoint",
|
|
3
|
-
"description": "A deprovisioned app answers EVERY JSON-RPC request on its per-app MCP endpoint (initialize included) with HTTP 403 and a JSON-RPC error whose data.refusal carries the registry projection
|
|
3
|
+
"description": "A deprovisioned app answers EVERY JSON-RPC request on its per-app MCP endpoint (initialize included) with HTTP 403 and a JSON-RPC error whose data.refusal carries the registry projection — code, message, fix, retry, appId — so the client reads app_deprovisioned instead of guessing between a deleted app and a bad credential. error.code is -32003 (APP_NOT_FOUND — the endpoint no longer serves this app) and error.message is App not found; an untyped authorization failure answers -32007 (UNAUTHORIZED) with no data, so a client can tell the two apart by code and can tell neither from a dropped socket by mistake — -32000 is the SDK client's ConnectionClosed and a first-party server never chooses it (ggui#836).",
|
|
4
4
|
"refusal": {
|
|
5
5
|
"code": "app_deprovisioned",
|
|
6
|
-
"message": "the app record has no owner claim any more
|
|
6
|
+
"message": "the app record has no owner claim any more — the app was deprovisioned",
|
|
7
7
|
"fix": "provision the app again; this app id never renders again",
|
|
8
|
-
"retry": "never"
|
|
8
|
+
"retry": "never",
|
|
9
|
+
"appId": "app_deprov_1"
|
|
9
10
|
},
|
|
10
11
|
"expect": {
|
|
11
12
|
"httpStatus": 403,
|
|
@@ -15,9 +16,10 @@
|
|
|
15
16
|
"data": {
|
|
16
17
|
"refusal": {
|
|
17
18
|
"code": "app_deprovisioned",
|
|
18
|
-
"message": "the app record has no owner claim any more
|
|
19
|
+
"message": "the app record has no owner claim any more — the app was deprovisioned",
|
|
19
20
|
"fix": "provision the app again; this app id never renders again",
|
|
20
|
-
"retry": "never"
|
|
21
|
+
"retry": "never",
|
|
22
|
+
"appId": "app_deprov_1"
|
|
21
23
|
}
|
|
22
24
|
}
|
|
23
25
|
}
|
|
@@ -1,11 +1,12 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "refuse-render-only-code",
|
|
3
|
-
"description": "A REGISTERED code whose surfaces exclude mcp-endpoint has no transport envelope: the per-app endpoint's authorization never speaks it, and a projector that types it anyway would put a render-gate state on a surface the registry does not list. The three auth arms (no issuer, foreign issuer, denied subject) stay the bare 403 by the same rule
|
|
3
|
+
"description": "A REGISTERED code whose surfaces exclude mcp-endpoint has no transport envelope: the per-app endpoint's authorization never speaks it, and a projector that types it anyway would put a render-gate state on a surface the registry does not list. The three auth arms (no issuer, foreign issuer, denied subject) stay the bare 403 by the same rule — naming any of them would say which is true.",
|
|
4
4
|
"refusal": {
|
|
5
5
|
"code": "insufficient_credit",
|
|
6
6
|
"message": "the configured allowance for this app is exhausted",
|
|
7
7
|
"fix": "top up the allowance for this app, then retry",
|
|
8
|
-
"retry": "after-fix"
|
|
8
|
+
"retry": "after-fix",
|
|
9
|
+
"appId": "app_live_1"
|
|
9
10
|
},
|
|
10
11
|
"expect": null
|
|
11
12
|
}
|
|
@@ -14,6 +14,8 @@ export interface TransportRefusalInput {
|
|
|
14
14
|
readonly fix: string;
|
|
15
15
|
/** Retry class (`after-fix` | `next-period` | `later` | `never`). */
|
|
16
16
|
readonly retry: string;
|
|
17
|
+
/** The app id the refused endpoint serves — equals the path's `{appId}` (ggui#870). */
|
|
18
|
+
readonly appId: string;
|
|
17
19
|
}
|
|
18
20
|
/** What the endpoint answers: the HTTP status and the JSON-RPC error object. */
|
|
19
21
|
export interface ProjectedTransportRefusal {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/transport-refusal-conformance/index.ts"],"names":[],"mappings":"AAyBA;;;;;;GAMG;AACH,MAAM,WAAW,qBAAqB;IACpC,+BAA+B;IAC/B,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,0BAA0B;IAC1B,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IACzB,6BAA6B;IAC7B,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAC;IACrB,qEAAqE;IACrE,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;CACxB;AAED,gFAAgF;AAChF,MAAM,WAAW,yBAAyB;IACxC,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAC;IAC5B,QAAQ,CAAC,KAAK,EAAE;QACd,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;QACtB,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;QACzB,QAAQ,CAAC,IAAI,EAAE;YAAE,QAAQ,CAAC,OAAO,EAAE,qBAAqB,CAAA;SAAE,CAAC;KAC5D,CAAC;CACH;AAED,qEAAqE;AACrE,MAAM,WAAW,+BAA+B;IAC9C,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,WAAW,EAAE,MAAM,CAAC;IAC7B,QAAQ,CAAC,OAAO,EAAE,qBAAqB,CAAC;IACxC,wFAAwF;IACxF,QAAQ,CAAC,MAAM,EAAE,yBAAyB,GAAG,IAAI,CAAC;CACnD;AAED,eAAO,MAAM,qBAAqB,EAAE,SAAS,+BAA+B,EAG3E,CAAC;AAEF,MAAM,WAAW,wBAAwB;IACvC,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,QAAQ,EAAE,yBAAyB,GAAG,IAAI,CAAC;IACpD,0EAA0E;IAC1E,QAAQ,CAAC,MAAM,EAAE,yBAAyB,GAAG,IAAI,GAAG,MAAM,CAAC;CAC5D;AAED,MAAM,WAAW,iCAAiC;IAChD,QAAQ,CAAC,MAAM,EAAE,SAAS,MAAM,EAAE,CAAC;IACnC,QAAQ,CAAC,MAAM,EAAE,SAAS,wBAAwB,EAAE,CAAC;CACtD;AAED;;;;GAIG;AACH,wBAAgB,8BAA8B,CAC5C,OAAO,EAAE,CAAC,OAAO,EAAE,qBAAqB,KAAK,yBAAyB,GAAG,IAAI,GAC5E,iCAAiC,CAiBnC"}
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/transport-refusal-conformance/index.ts"],"names":[],"mappings":"AAyBA;;;;;;GAMG;AACH,MAAM,WAAW,qBAAqB;IACpC,+BAA+B;IAC/B,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,0BAA0B;IAC1B,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IACzB,6BAA6B;IAC7B,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAC;IACrB,qEAAqE;IACrE,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,uFAAuF;IACvF,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;CACxB;AAED,gFAAgF;AAChF,MAAM,WAAW,yBAAyB;IACxC,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAC;IAC5B,QAAQ,CAAC,KAAK,EAAE;QACd,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;QACtB,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;QACzB,QAAQ,CAAC,IAAI,EAAE;YAAE,QAAQ,CAAC,OAAO,EAAE,qBAAqB,CAAA;SAAE,CAAC;KAC5D,CAAC;CACH;AAED,qEAAqE;AACrE,MAAM,WAAW,+BAA+B;IAC9C,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,WAAW,EAAE,MAAM,CAAC;IAC7B,QAAQ,CAAC,OAAO,EAAE,qBAAqB,CAAC;IACxC,wFAAwF;IACxF,QAAQ,CAAC,MAAM,EAAE,yBAAyB,GAAG,IAAI,CAAC;CACnD;AAED,eAAO,MAAM,qBAAqB,EAAE,SAAS,+BAA+B,EAG3E,CAAC;AAEF,MAAM,WAAW,wBAAwB;IACvC,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,QAAQ,EAAE,yBAAyB,GAAG,IAAI,CAAC;IACpD,0EAA0E;IAC1E,QAAQ,CAAC,MAAM,EAAE,yBAAyB,GAAG,IAAI,GAAG,MAAM,CAAC;CAC5D;AAED,MAAM,WAAW,iCAAiC;IAChD,QAAQ,CAAC,MAAM,EAAE,SAAS,MAAM,EAAE,CAAC;IACnC,QAAQ,CAAC,MAAM,EAAE,SAAS,wBAAwB,EAAE,CAAC;CACtD;AAED;;;;GAIG;AACH,wBAAgB,8BAA8B,CAC5C,OAAO,EAAE,CAAC,OAAO,EAAE,qBAAqB,KAAK,yBAAyB,GAAG,IAAI,GAC5E,iCAAiC,CAiBnC"}
|
|
@@ -8,8 +8,8 @@
|
|
|
8
8
|
* The contract it makes observable: a deprovisioned app answers HTTP 403
|
|
9
9
|
* with `{ code: -32003, message: "App not found", data: { refusal } }` where
|
|
10
10
|
* `refusal` is the registry projection without the render-only fields
|
|
11
|
-
* (`code`, `message`, `fix`, `retry`); an authorization failure that is
|
|
12
|
-
* NOT a registry state answers 403 with `-
|
|
11
|
+
* (`code`, `message`, `fix`, `retry`, `appId`); an authorization failure that is
|
|
12
|
+
* NOT a registry state answers 403 with `-32007` and no `data` — the untyped
|
|
13
13
|
* arms deliberately indistinguishable among themselves, so a client never
|
|
14
14
|
* learns which one it hit (codes ruled in ggui#836).
|
|
15
15
|
*
|
package/dist/types.d.ts
CHANGED
|
@@ -167,7 +167,7 @@ export interface CreateGguiSessionStep {
|
|
|
167
167
|
/** Opaque render id the host allocates. Downstream steps + the
|
|
168
168
|
* fixture's `inputEnvelope` reference it by value. */
|
|
169
169
|
readonly sessionId: string;
|
|
170
|
-
/** Optional app
|
|
170
|
+
/** Optional app id the render scopes to. Defaults to the
|
|
171
171
|
* host's implementation-defined "default app". */
|
|
172
172
|
readonly appId?: string;
|
|
173
173
|
/**
|