@ggui-ai/protocol-conformance 0.7.0 → 0.9.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.
Files changed (46) hide show
  1. package/README.md +15 -12
  2. package/dist/binding-conformance/cases/bind-blueprint-no-contract-none.json +6 -0
  3. package/dist/binding-conformance/cases/bind-contract-without-tool-names-none.json +18 -0
  4. package/dist/binding-conformance/cases/bind-declared-gadget.json +13 -0
  5. package/dist/binding-conformance/cases/bind-declared-wins-over-contract.json +23 -0
  6. package/dist/binding-conformance/cases/bind-derived-caps-at-sixteen.json +54 -0
  7. package/dist/binding-conformance/cases/bind-derived-props-and-stream.json +36 -0
  8. package/dist/binding-conformance/cases/bind-derived-skips-invalid-names.json +40 -0
  9. package/dist/binding-conformance/cases/bind-derived-union-dedupes.json +38 -0
  10. package/dist/binding-conformance/cases/bind-gadget-undeclared-none.json +6 -0
  11. package/dist/binding-conformance/cases/bind-reject-charset.json +9 -0
  12. package/dist/binding-conformance/cases/bind-reject-duplicate-pair.json +12 -0
  13. package/dist/binding-conformance/cases/bind-reject-empty-list.json +9 -0
  14. package/dist/binding-conformance/cases/bind-reject-overlong-name.json +13 -0
  15. package/dist/binding-conformance/cases/bind-reject-server-charset.json +9 -0
  16. package/dist/binding-conformance/cases/bind-reject-too-many-entries.json +27 -0
  17. package/dist/binding-conformance/cases/filter-case-sensitive-server.json +7 -0
  18. package/dist/binding-conformance/cases/filter-case-sensitive-tool.json +7 -0
  19. package/dist/binding-conformance/cases/filter-none-passes-bound.json +7 -0
  20. package/dist/binding-conformance/cases/filter-none-passes-unbound.json +6 -0
  21. package/dist/binding-conformance/cases/filter-pair-bare-never-matches.json +7 -0
  22. package/dist/binding-conformance/cases/filter-pair-exact.json +10 -0
  23. package/dist/binding-conformance/cases/filter-pair-not-cross-entry.json +10 -0
  24. package/dist/binding-conformance/cases/filter-server-excludes-bare.json +7 -0
  25. package/dist/binding-conformance/cases/filter-server-match.json +7 -0
  26. package/dist/binding-conformance/cases/filter-tool-cross-server.json +7 -0
  27. package/dist/binding-conformance/cases/filter-tool-excludes-unbound.json +6 -0
  28. package/dist/binding-conformance/cases/filter-tool-matches-bare-entry.json +7 -0
  29. package/dist/binding-conformance/cases/filter-tool-no-match.json +7 -0
  30. package/dist/binding-conformance/index.d.ts +214 -0
  31. package/dist/binding-conformance/index.d.ts.map +1 -0
  32. package/dist/binding-conformance/index.js +153 -0
  33. package/dist/index.d.ts +2 -0
  34. package/dist/index.d.ts.map +1 -1
  35. package/dist/index.js +5 -0
  36. package/dist/resolution-conformance/cases/resolve-bundle-host-computed.json +3 -3
  37. package/dist/resolution-conformance/cases/resolve-default-host.json +2 -2
  38. package/dist/resolution-conformance/cases/resolve-loopback-host-http-scheme.json +2 -2
  39. package/dist/resource-read-conformance/cases/channel-less-read-mounts-inline.json +24 -0
  40. package/dist/resource-read-conformance/cases/pin-past-the-head-answers-not-found.json +29 -0
  41. package/dist/resource-read-conformance/cases/pinned-superseded-record-serves-its-reign-immutably.json +42 -0
  42. package/dist/resource-read-conformance/cases/read-without-a-delivery-channel-answers-not-mountable.json +15 -4
  43. package/dist/resource-read-conformance/index.d.ts +60 -4
  44. package/dist/resource-read-conformance/index.d.ts.map +1 -1
  45. package/dist/resource-read-conformance/index.js +144 -11
  46. package/package.json +8 -3
@@ -76,6 +76,15 @@ export type ResourceReadSeed = {
76
76
  /** A live render row that has committed a component. */
77
77
  readonly kind: 'committed-render';
78
78
  readonly session: string;
79
+ /**
80
+ * Size class of the committed component relative to the inline
81
+ * `codeB64` cap. `'under-inline-cap'` (the default) seeds a
82
+ * component the server can deliver inline even with no wired
83
+ * channels; `'over-inline-cap'` seeds one it cannot — the shape
84
+ * that still exercises the no-delivery-channel failure arm on a
85
+ * channel-less server.
86
+ */
87
+ readonly size: 'under-inline-cap' | 'over-inline-cap';
79
88
  } | {
80
89
  /** A live render row whose generation has not committed yet. */
81
90
  readonly kind: 'uncommitted-render';
@@ -89,6 +98,19 @@ export type ResourceReadSeed = {
89
98
  */
90
99
  readonly kind: 'registered-blueprint';
91
100
  readonly as: string;
101
+ } | {
102
+ /**
103
+ * A session with epoch HISTORY (#483): `records[0]` is the mint
104
+ * (epoch 0), each later entry one `ggui_update` (epoch N). The
105
+ * driver seeds the ledger so every superseded record is
106
+ * reconstructable (an in-reign props event per record) and the
107
+ * row carries the head epoch.
108
+ */
109
+ readonly kind: 'epoch-history';
110
+ readonly session: string;
111
+ readonly records: ReadonlyArray<{
112
+ readonly props: Readonly<Record<string, unknown>>;
113
+ }>;
92
114
  };
93
115
  /**
94
116
  * The blueprint-key segment of a locator.
@@ -118,6 +140,14 @@ export type ResourceReadLocator = {
118
140
  readonly kind: 'render';
119
141
  readonly session: string;
120
142
  readonly key?: ResourceReadKey;
143
+ /**
144
+ * Epoch pin (#483, SPEC §7.1.2.2): present ⇒ the kit reads the
145
+ * PINNED history record (`…#N`); absent ⇒ the live head. The
146
+ * fragment encoding is pinned HERE deliberately — the kit is
147
+ * the arbiter of the wire grammar, so an encoding change is a
148
+ * conscious, visible kit change.
149
+ */
150
+ readonly epoch?: number;
121
151
  } | {
122
152
  readonly kind: 'raw';
123
153
  readonly uri: string;
@@ -160,6 +190,15 @@ export type ResourceReadExpectation = {
160
190
  } | {
161
191
  /** A result whose contents declare a delivery channel. */
162
192
  readonly kind: 'live-mount';
193
+ } | {
194
+ /**
195
+ * A mount serving a HISTORY record (#483): still a real mount
196
+ * (delivery channel required), graded additionally on its
197
+ * props — every `propsInclude` entry must appear in the parsed
198
+ * `propsJson` with the authored value.
199
+ */
200
+ readonly kind: 'pinned-mount';
201
+ readonly propsInclude?: Readonly<Record<string, unknown>>;
163
202
  };
164
203
  /** One read within a case. */
165
204
  export interface ResourceReadProbe {
@@ -200,6 +239,14 @@ export interface ResourceReadConformanceCase {
200
239
  * not excused here.
201
240
  */
202
241
  readonly indistinguishable?: readonly string[];
242
+ /**
243
+ * Probe names whose MOUNT outcomes must carry byte-identical
244
+ * `propsJson` (#483 — pinned-record immutability). Deliberately
245
+ * compares ONLY the props: live-channel tokens and expiries on the
246
+ * same meta legitimately differ per read; the RECORD is what may
247
+ * not.
248
+ */
249
+ readonly identicalMounts?: readonly string[];
203
250
  /**
204
251
  * Literal substrings that MUST NOT appear anywhere in any error frame
205
252
  * this case produces. The values a refusal would leak if a diagnostic
@@ -225,14 +272,22 @@ export interface ResourceReadScenario {
225
272
  * markup that transports it is not.
226
273
  */
227
274
  export interface ResourceReadRenderMeta {
228
- /** A fetchable component URL — the static delivery channel. */
275
+ /** A fetchable component URL — the fetched static delivery channel. */
229
276
  readonly codeUrl?: string;
277
+ /** Base64 component source — the inline static delivery channel. */
278
+ readonly codeB64?: string;
230
279
  /** The live channel's endpoint. Only a channel WITH a token. */
231
280
  readonly wsUrl?: string;
232
281
  /** The live channel's token. Only a channel WITH an endpoint. */
233
282
  readonly wsToken?: string;
234
283
  /** A server-emitted system card, which carries its own content. */
235
284
  readonly kind?: string;
285
+ /**
286
+ * The mount's props as a JSON string (#483). Graded by the
287
+ * `pinned-mount` expectation and the `identicalMounts` group;
288
+ * ungraded elsewhere.
289
+ */
290
+ readonly propsJson?: string;
236
291
  }
237
292
  /** The raw JSON-RPC error body a server put on the wire. */
238
293
  export interface JsonRpcErrorFrame {
@@ -280,7 +335,7 @@ export type ResourceReadScenarioDriver = (scenario: ResourceReadScenario) => Pro
280
335
  /**
281
336
  * Every resource-read case the kit ships, in deterministic order: the
282
337
  * four typed classes, then the disclosure fusions, then the boundary of
283
- * the classification, then the two mount cases.
338
+ * the classification, then the three mount cases.
284
339
  */
285
340
  export declare const resourceReadCases: readonly ResourceReadConformanceCase[];
286
341
  /** The resource URI every render locator hangs off. */
@@ -293,8 +348,9 @@ export declare const GGUI_RENDER_RESOURCE_URI = "ui://ggui/render";
293
348
  export declare function renderLocatorUri(locator: ResourceReadLocator, registeredKeys?: Readonly<Record<string, string>>): string;
294
349
  /**
295
350
  * Does this render meta carry enough to paint something? One of the
296
- * three delivery channels a runtime knows: a static component URL, a
297
- * live channel, or a server-emitted system card.
351
+ * four delivery channels a runtime knows: a fetchable static component
352
+ * URL, inline base64 component source, a live channel, or a
353
+ * server-emitted system card.
298
354
  *
299
355
  * The live channel needs BOTH halves — an endpoint with no token cannot
300
356
  * be opened, and a token with no endpoint has nowhere to go — so a
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/resource-read-conformance/index.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/resource-read-conformance/index.ts"],"names":[],"mappings":"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"}
@@ -110,13 +110,16 @@ import registryFusion from './cases/registry-match-fuses-on-not-mountable.json'
110
110
  import malformedLocator from './cases/malformed-locator-stays-outside-the-typed-set.json' with { type: 'json' };
111
111
  import liveRowMount from './cases/live-row-read-returns-mount-material.json' with { type: 'json' };
112
112
  import remintMount from './cases/evicted-row-remints-from-the-durable-record.json' with { type: 'json' };
113
+ import inlineMount from './cases/channel-less-read-mounts-inline.json' with { type: 'json' };
114
+ import pinnedImmutable from './cases/pinned-superseded-record-serves-its-reign-immutably.json' with { type: 'json' };
115
+ import pinPastHead from './cases/pin-past-the-head-answers-not-found.json' with { type: 'json' };
113
116
  // =============================================================================
114
117
  // Catalog
115
118
  // =============================================================================
116
119
  /**
117
120
  * Every resource-read case the kit ships, in deterministic order: the
118
121
  * four typed classes, then the disclosure fusions, then the boundary of
119
- * the classification, then the two mount cases.
122
+ * the classification, then the three mount cases.
120
123
  */
121
124
  export const resourceReadCases = [
122
125
  notFoundOnAMiss,
@@ -132,6 +135,9 @@ export const resourceReadCases = [
132
135
  malformedLocator,
133
136
  liveRowMount,
134
137
  remintMount,
138
+ inlineMount,
139
+ pinnedImmutable,
140
+ pinPastHead,
135
141
  ];
136
142
  // =============================================================================
137
143
  // Locator grammar + the mount predicate
@@ -146,22 +152,24 @@ export const GGUI_RENDER_RESOURCE_URI = 'ui://ggui/render';
146
152
  export function renderLocatorUri(locator, registeredKeys = {}) {
147
153
  if (locator.kind === 'raw')
148
154
  return locator.uri;
155
+ const pin = locator.epoch === undefined ? '' : `#${locator.epoch}`;
149
156
  const base = `${GGUI_RENDER_RESOURCE_URI}/${locator.session}`;
150
157
  const keyRef = locator.key;
151
158
  if (keyRef === undefined)
152
- return base;
159
+ return `${base}${pin}`;
153
160
  if (keyRef.kind === 'literal')
154
- return `${base}/${keyRef.value}`;
161
+ return `${base}/${keyRef.value}${pin}`;
155
162
  const key = registeredKeys[keyRef.seed];
156
163
  if (key === undefined) {
157
164
  throw new Error(`no key was reported for registry seed '${keyRef.seed}'`);
158
165
  }
159
- return `${base}/${key}`;
166
+ return `${base}/${key}${pin}`;
160
167
  }
161
168
  /**
162
169
  * Does this render meta carry enough to paint something? One of the
163
- * three delivery channels a runtime knows: a static component URL, a
164
- * live channel, or a server-emitted system card.
170
+ * four delivery channels a runtime knows: a fetchable static component
171
+ * URL, inline base64 component source, a live channel, or a
172
+ * server-emitted system card.
165
173
  *
166
174
  * The live channel needs BOTH halves — an endpoint with no token cannot
167
175
  * be opened, and a token with no endpoint has nowhere to go — so a
@@ -170,6 +178,8 @@ export function renderLocatorUri(locator, registeredKeys = {}) {
170
178
  export function declaresDeliveryChannel(meta) {
171
179
  if (typeof meta.codeUrl === 'string' && meta.codeUrl.length > 0)
172
180
  return true;
181
+ if (typeof meta.codeB64 === 'string' && meta.codeB64.length > 0)
182
+ return true;
173
183
  if (typeof meta.kind === 'string' && meta.kind.length > 0)
174
184
  return true;
175
185
  return (typeof meta.wsUrl === 'string' &&
@@ -284,6 +294,9 @@ async function gradeCase(testCase, prepared) {
284
294
  const fusion = gradeIndistinguishable(testCase, outcomes);
285
295
  if (fusion !== null)
286
296
  return { kind: 'fail', failure: fusion };
297
+ const pinned = gradeIdenticalMounts(testCase, outcomes);
298
+ if (pinned !== null)
299
+ return { kind: 'fail', failure: pinned };
287
300
  return { kind: 'pass' };
288
301
  }
289
302
  function gradeProbe(testCase, probe, uri, outcome) {
@@ -304,6 +317,38 @@ function gradeProbe(testCase, probe, uri, outcome) {
304
317
  }
305
318
  return null;
306
319
  }
320
+ if (probe.expect.kind === 'pinned-mount') {
321
+ if (outcome.kind !== 'mount') {
322
+ return fail('a pinned history record within the servable window mounts (SPEC §7.1.2.2)', 'a result whose contents declare a delivery channel and carry the record props', `reading '${uri}' failed, but this pinned record resolves on this server.`);
323
+ }
324
+ if (!declaresDeliveryChannel(outcome.renderMeta)) {
325
+ return fail('a pinned mount is still a mount', 'render meta declaring a delivery channel', `reading '${uri}' succeeded with contents that declare no delivery channel.`);
326
+ }
327
+ const include = probe.expect.propsInclude;
328
+ if (include !== undefined) {
329
+ const raw = outcome.renderMeta.propsJson;
330
+ let parsed = null;
331
+ if (typeof raw === 'string') {
332
+ try {
333
+ const candidate = JSON.parse(raw);
334
+ if (isRecord(candidate))
335
+ parsed = candidate;
336
+ }
337
+ catch {
338
+ parsed = null;
339
+ }
340
+ }
341
+ if (parsed === null) {
342
+ return fail('a pinned mount carries its record props (SPEC §7.1.2.2)', 'parseable propsJson on the render meta', `reading '${uri}' mounted, but its render meta carries no parseable propsJson to grade the record against.`);
343
+ }
344
+ for (const [key, expected] of Object.entries(include)) {
345
+ if (JSON.stringify(parsed[key]) !== JSON.stringify(expected)) {
346
+ return fail('a pinned record serves the props of ITS epoch, never a neighbor\'s', `propsJson entry '${key}' = ${JSON.stringify(expected)}`, `reading '${uri}' served '${key}' = ${JSON.stringify(parsed[key])}.`);
347
+ }
348
+ }
349
+ }
350
+ return null;
351
+ }
307
352
  if (outcome.kind !== 'error') {
308
353
  return fail('a read that cannot be mounted answers with a typed error, never a success-shaped result', probe.expect, `reading '${uri}' returned a result, but this locator cannot be mounted on this server.`);
309
354
  }
@@ -371,6 +416,40 @@ function gradeDisclosure(testCase, outcomes) {
371
416
  }
372
417
  return null;
373
418
  }
419
+ function gradeIdenticalMounts(testCase, outcomes) {
420
+ const group = testCase.identicalMounts ?? [];
421
+ if (group.length < 2)
422
+ return null;
423
+ let reference = null;
424
+ for (const probeName of group) {
425
+ const outcome = outcomes.get(probeName);
426
+ if (outcome === undefined || outcome.kind !== 'mount' || outcome.renderMeta.propsJson === undefined) {
427
+ return {
428
+ name: testCase.name,
429
+ probe: probeName,
430
+ obligation: 'a pinned history record serves identical props on every read (SPEC §7.1.2.2)',
431
+ expected: 'a mount outcome carrying propsJson to compare',
432
+ actual: outcome ?? '(no outcome recorded)',
433
+ message: `probe '${probeName}' is named in this case's identicalMounts group but did not produce a props-carrying mount, so immutability cannot even be compared.`,
434
+ };
435
+ }
436
+ if (reference === null) {
437
+ reference = { probe: probeName, propsJson: outcome.renderMeta.propsJson };
438
+ continue;
439
+ }
440
+ if (outcome.renderMeta.propsJson !== reference.propsJson) {
441
+ return {
442
+ name: testCase.name,
443
+ probe: probeName,
444
+ obligation: 'a pinned history record serves identical props on every read (SPEC §7.1.2.2)',
445
+ expected: reference.propsJson,
446
+ actual: outcome.renderMeta.propsJson,
447
+ message: `probes '${reference.probe}' and '${probeName}' read the same pinned record and got different props — the record mutated between reads.`,
448
+ };
449
+ }
450
+ }
451
+ return null;
452
+ }
374
453
  function gradeIndistinguishable(testCase, outcomes) {
375
454
  const group = testCase.indistinguishable ?? [];
376
455
  if (group.length < 2)
@@ -526,6 +605,14 @@ export function parseCase(input) {
526
605
  probeNames.add(parsed.as);
527
606
  return parsed;
528
607
  });
608
+ const mountGroup = input['identicalMounts'];
609
+ if (mountGroup !== undefined) {
610
+ if (!Array.isArray(mountGroup))
611
+ throw bad("'identicalMounts' must be an array of probe names");
612
+ if (mountGroup.length < 2) {
613
+ throw bad("'identicalMounts' names a single probe — a group of one asserts nothing, which reads as an obligation being graded when it is not");
614
+ }
615
+ }
529
616
  const group = input['indistinguishable'];
530
617
  if (group !== undefined) {
531
618
  if (!Array.isArray(group))
@@ -560,6 +647,7 @@ export function parseCase(input) {
560
647
  seeds,
561
648
  reads,
562
649
  ...(group !== undefined ? { indistinguishable: group.map(String) } : {}),
650
+ ...(mountGroup !== undefined ? { identicalMounts: mountGroup.map(String) } : {}),
563
651
  ...(secrets !== undefined ? { disclosesNothing: secrets.map(String) } : {}),
564
652
  };
565
653
  }
@@ -588,6 +676,7 @@ const CASE_KEYS = [
588
676
  'seeds',
589
677
  'reads',
590
678
  'indistinguishable',
679
+ 'identicalMounts',
591
680
  'disclosesNothing',
592
681
  ];
593
682
  const SERVER_KEYS = [
@@ -600,12 +689,13 @@ const PROBE_KEYS = ['as', 'locator', 'expect'];
600
689
  const SEED_KEYS = {
601
690
  'identity-record': ['kind', 'session', 'key', 'blueprint'],
602
691
  'durable-blueprint': ['kind', 'componentRef', 'body'],
603
- 'committed-render': ['kind', 'session'],
692
+ 'committed-render': ['kind', 'session', 'size'],
693
+ 'epoch-history': ['kind', 'session', 'records'],
604
694
  'uncommitted-render': ['kind', 'session'],
605
695
  'registered-blueprint': ['kind', 'as'],
606
696
  };
607
697
  const LOCATOR_KEYS = {
608
- render: ['kind', 'session', 'key'],
698
+ render: ['kind', 'session', 'key', 'epoch'],
609
699
  raw: ['kind', 'uri'],
610
700
  };
611
701
  const KEY_KEYS = {
@@ -616,6 +706,7 @@ const EXPECTATION_KEYS = {
616
706
  'typed-error': ['kind', 'jsonRpcCode', 'dataCode', 'detailAbsent'],
617
707
  'outside-typed-set': ['kind'],
618
708
  'live-mount': ['kind'],
709
+ 'pinned-mount': ['kind', 'propsInclude'],
619
710
  };
620
711
  function parseSubstrate(bad, value) {
621
712
  switch (value) {
@@ -665,16 +756,37 @@ function parseSeed(bad, seed) {
665
756
  }
666
757
  return { kind: 'durable-blueprint', componentRef, body };
667
758
  }
668
- case 'committed-render':
759
+ case 'committed-render': {
760
+ const size = seed['size'] ?? 'under-inline-cap';
761
+ if (size !== 'under-inline-cap' && size !== 'over-inline-cap') {
762
+ throw bad("a committed-render seed's 'size' must be 'under-inline-cap' or 'over-inline-cap' (or omitted for the default)");
763
+ }
669
764
  return {
670
765
  kind: 'committed-render',
671
766
  session: requireText(bad, seed['session'], "a committed-render seed's 'session'"),
767
+ size,
672
768
  };
769
+ }
673
770
  case 'uncommitted-render':
674
771
  return {
675
772
  kind: 'uncommitted-render',
676
773
  session: requireText(bad, seed['session'], "an uncommitted-render seed's 'session'"),
677
774
  };
775
+ case 'epoch-history': {
776
+ const session = requireText(bad, seed['session'], "epoch-history seed 'session'");
777
+ const rawRecords = seed['records'];
778
+ if (!Array.isArray(rawRecords) || rawRecords.length < 2) {
779
+ throw bad("epoch-history seed 'records' must be an array of at least TWO entries — one mint plus one update; a single-record history has nothing superseded to pin");
780
+ }
781
+ const records = rawRecords.map((entry, i) => {
782
+ if (!isRecord(entry) || !isRecord(entry['props'])) {
783
+ throw bad(`epoch-history seed record ${i} authors no 'props' object`);
784
+ }
785
+ rejectUnknownKeys(bad, entry, ['props'], `epoch-history seed record ${i}`);
786
+ return { props: entry['props'] };
787
+ });
788
+ return { kind: 'epoch-history', session, records };
789
+ }
678
790
  case 'registered-blueprint':
679
791
  return {
680
792
  kind: 'registered-blueprint',
@@ -705,10 +817,19 @@ function parseLocator(bad, as, locator) {
705
817
  switch (locator['kind']) {
706
818
  case 'render': {
707
819
  const session = requireText(bad, locator['session'], `probe '${as}' locator 'session'`);
820
+ const rawEpoch = locator['epoch'];
821
+ let epoch;
822
+ if (rawEpoch !== undefined) {
823
+ if (typeof rawEpoch !== 'number' || !Number.isInteger(rawEpoch) || rawEpoch < 0) {
824
+ throw bad(`probe '${as}' locator 'epoch' must be a non-negative integer`);
825
+ }
826
+ epoch = rawEpoch;
827
+ }
708
828
  const key = locator['key'];
829
+ const base = { kind: 'render', session, ...(epoch !== undefined ? { epoch } : {}) };
709
830
  if (key === undefined)
710
- return { kind: 'render', session };
711
- return { kind: 'render', session, key: parseKey(bad, as, key) };
831
+ return base;
832
+ return { ...base, key: parseKey(bad, as, key) };
712
833
  }
713
834
  case 'raw':
714
835
  return { kind: 'raw', uri: requireText(bad, locator['uri'], `probe '${as}' locator 'uri'`) };
@@ -744,6 +865,18 @@ function parseExpectation(bad, as, expect) {
744
865
  switch (expect['kind']) {
745
866
  case 'outside-typed-set':
746
867
  return { kind: 'outside-typed-set' };
868
+ case 'pinned-mount': {
869
+ const rawInclude = expect['propsInclude'];
870
+ if (rawInclude !== undefined && !isRecord(rawInclude)) {
871
+ throw bad(`probe '${as}' 'propsInclude' must be an object of expected prop entries`);
872
+ }
873
+ return {
874
+ kind: 'pinned-mount',
875
+ ...(rawInclude !== undefined
876
+ ? { propsInclude: rawInclude }
877
+ : {}),
878
+ };
879
+ }
747
880
  case 'live-mount':
748
881
  return { kind: 'live-mount' };
749
882
  case 'typed-error': {
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@ggui-ai/protocol-conformance",
3
- "version": "0.7.0",
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 three pure-function catalogs (schema, registration, resolution) for the gadget obligations, reporting a scorecard-style pass / fail / skip. MCP builders adopt this kit to prove their implementation is protocol-conformant.",
3
+ "version": "0.9.0",
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 four pure-function catalogs (schema, registration, resolution, binding) for the gadget and tool-binding 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": [
7
7
  "ggui",
@@ -50,11 +50,16 @@
50
50
  "types": "./dist/resource-read-conformance/index.d.ts",
51
51
  "import": "./dist/resource-read-conformance/index.js",
52
52
  "default": "./dist/resource-read-conformance/index.js"
53
+ },
54
+ "./binding-conformance": {
55
+ "types": "./dist/binding-conformance/index.d.ts",
56
+ "import": "./dist/binding-conformance/index.js",
57
+ "default": "./dist/binding-conformance/index.js"
53
58
  }
54
59
  },
55
60
  "dependencies": {
56
61
  "ws": "^8.20.1",
57
- "@ggui-ai/protocol": "0.7.0"
62
+ "@ggui-ai/protocol": "0.9.0"
58
63
  },
59
64
  "devDependencies": {
60
65
  "@types/node": "^22.0.0",