@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
@@ -0,0 +1,214 @@
1
+ /**
2
+ * Binding-conformance catalog — MCP tool-binding resolution + search
3
+ * filter semantics (SPEC §7.7.4 — tool bindings & discovery).
4
+ *
5
+ * ## Why this is a pure-function catalog
6
+ *
7
+ * Two pure obligations connect a registry artifact to the MCP tools
8
+ * it renders:
9
+ *
10
+ * 1. RESOLUTION — given a manifest, which binding set is
11
+ * effective? A declared `mcpTools` list wins entirely; a
12
+ * blueprint without one derives bare tool names from its
13
+ * contract's lineage fields
14
+ * (`propsSpec.properties[*].sourceTool`,
15
+ * `streamSpec[*].source.tool`); a schema-invalid list rejects
16
+ * with the manifest-validation code. Bindings are search
17
+ * metadata only — they never enter contract canonicalization
18
+ * or any cache identity.
19
+ * 2. FILTERING — given an artifact's stored binding set and a
20
+ * search query's `tool` / `server` params, does the artifact
21
+ * match? Both dimensions are case-sensitive exact and must land
22
+ * on a single entry; a bare entry never satisfies a server
23
+ * dimension; no dimensions given passes everything.
24
+ *
25
+ * Both are deterministic functions — no render, transport, or wire
26
+ * frame — so they get the same treatment as the registry gate
27
+ * (`../registration-conformance`) and bundle-URL resolution
28
+ * (`../resolution-conformance`): catalogs of JSON cases graded
29
+ * against caller-supplied functions.
30
+ *
31
+ * ## Polyglot, decoupled
32
+ *
33
+ * Each case ships as raw JSON under `./cases/`.
34
+ * {@link runBindingResolutionCases} / {@link runBindingFilterCases}
35
+ * take the implementation as a callback — the kit never imports a
36
+ * concrete resolver or search filter, so adopters drive their OWN.
37
+ * The reference binding (`./binding-conformance.test.ts`) restates
38
+ * faithful minimal implementations in-test.
39
+ */
40
+ import type { DataContract } from "@ggui-ai/protocol";
41
+ /**
42
+ * One MCP tool binding as authored on a manifest / stored on search
43
+ * metadata. `server` carries `serverInfo.name` semantics; a bare
44
+ * entry (no `server`) is server-agnostic.
45
+ */
46
+ export interface McpToolBindingDecl {
47
+ readonly server?: string;
48
+ readonly tool: string;
49
+ }
50
+ /**
51
+ * The binding-relevant subset of an artifact manifest the resolver
52
+ * reads. Authored kit-local — decoupled from any shipping manifest
53
+ * schema on purpose; `contract` reuses the protocol's
54
+ * {@link DataContract} type the way `../registration-conformance`
55
+ * does.
56
+ */
57
+ export interface BindingManifestEntry {
58
+ /** Which manifest kind the entry models. Only blueprints derive. */
59
+ readonly kind: "gadget" | "blueprint";
60
+ /** The `mcpTools` list as authored — possibly invalid. */
61
+ readonly mcpTools?: readonly McpToolBindingDecl[];
62
+ /** The blueprint's contract, when it has one. */
63
+ readonly contract?: DataContract;
64
+ }
65
+ /**
66
+ * The code a schema-invalid `mcpTools` list classifies into.
67
+ * Extensibly-closed `(string & {})` tail so a later protocol
68
+ * revision's code rides through without a kit bump — same
69
+ * discipline as `GadgetGateRejectCode` in
70
+ * `../registration-conformance`.
71
+ */
72
+ export type BindingRejectCode = "manifest_invalid" | (string & {});
73
+ /**
74
+ * What resolving one manifest's bindings produces: accept with a
75
+ * `source` + `bindings` pair (declared or derived — present together
76
+ * or absent together), accept with NEITHER (the artifact has no
77
+ * bindings and is not findable by tool), or reject with the
78
+ * manifest-validation code.
79
+ */
80
+ export type BindingResolutionOutcome = {
81
+ readonly outcome: "accept";
82
+ readonly source?: "declared" | "derived";
83
+ readonly bindings?: readonly McpToolBindingDecl[];
84
+ } | {
85
+ readonly outcome: "reject";
86
+ readonly code: BindingRejectCode;
87
+ };
88
+ /**
89
+ * A search query's tool/server params as one filter input. Both
90
+ * optional — an absent dimension does not constrain. Matching is
91
+ * case-sensitive exact on both dimensions.
92
+ */
93
+ export interface McpToolFilterDecl {
94
+ readonly tool?: string;
95
+ readonly server?: string;
96
+ }
97
+ /**
98
+ * One binding-resolution case. Authored as JSON under `./cases/`,
99
+ * consumed via {@link bindingResolutionCases}, graded by
100
+ * {@link runBindingResolutionCases}.
101
+ *
102
+ * The shape IS the public API — additive changes only.
103
+ */
104
+ export interface BindingResolutionCase {
105
+ /** Unique case name. MUST match the JSON filename without `.json`. */
106
+ readonly name: string;
107
+ /** Which binding obligation this case proves. */
108
+ readonly description: string;
109
+ /** The manifest subset fed to the resolver. */
110
+ readonly entry: BindingManifestEntry;
111
+ /**
112
+ * The outcome a spec-conformant resolver MUST produce. Binding
113
+ * lists are compared order-sensitively: declared bindings carry
114
+ * authored order; derived bindings carry first-appearance order
115
+ * (props in declaration order, then stream channels), duplicates
116
+ * collapsed to the first occurrence.
117
+ */
118
+ readonly expect: BindingResolutionOutcome;
119
+ }
120
+ /**
121
+ * One filter-semantics case. Authored as JSON under `./cases/`,
122
+ * consumed via {@link bindingFilterCases}, graded by
123
+ * {@link runBindingFilterCases}.
124
+ *
125
+ * The shape IS the public API — additive changes only.
126
+ */
127
+ export interface BindingFilterCase {
128
+ /** Unique case name. MUST match the JSON filename without `.json`. */
129
+ readonly name: string;
130
+ /** Which filter obligation this case proves. */
131
+ readonly description: string;
132
+ /** The artifact's stored binding set — absent models an unbound artifact. */
133
+ readonly bindings?: readonly McpToolBindingDecl[];
134
+ /** The query's tool/server params — `{}` models neither given. */
135
+ readonly filters: McpToolFilterDecl;
136
+ /** Whether a spec-conformant filter matches the artifact. */
137
+ readonly expect: boolean;
138
+ }
139
+ /**
140
+ * Every binding-resolution case the kit ships, in deterministic
141
+ * order — declared first, then derived, then the no-binding edges,
142
+ * then the schema rejects.
143
+ */
144
+ export declare const bindingResolutionCases: readonly BindingResolutionCase[];
145
+ /**
146
+ * Every filter-semantics case the kit ships, in deterministic
147
+ * order — tool-dimension, server-dimension, pair, no-filter
148
+ * passthrough, then case-sensitivity.
149
+ */
150
+ export declare const bindingFilterCases: readonly BindingFilterCase[];
151
+ /** One resolution case the resolver under test graded wrong. */
152
+ export interface BindingResolutionMismatch {
153
+ readonly name: string;
154
+ /** The outcome the catalog says a conformant resolver MUST produce. */
155
+ readonly expected: BindingResolutionOutcome;
156
+ /** The outcome the resolver under test actually produced. */
157
+ readonly actual: BindingResolutionOutcome;
158
+ }
159
+ /** Outcome of grading a resolver against the resolution catalog. */
160
+ export interface BindingResolutionResult {
161
+ /** Names of cases the resolver graded correctly. */
162
+ readonly passed: readonly string[];
163
+ /** Cases the resolver graded wrong — empty iff fully conformant. */
164
+ readonly failed: readonly BindingResolutionMismatch[];
165
+ }
166
+ /** One filter case the predicate under test graded wrong. */
167
+ export interface BindingFilterMismatch {
168
+ readonly name: string;
169
+ /** Whether the catalog says a conformant filter matches. */
170
+ readonly expected: boolean;
171
+ /** What the predicate under test actually returned. */
172
+ readonly actual: boolean;
173
+ }
174
+ /** Outcome of grading a filter predicate against the filter catalog. */
175
+ export interface BindingFilterResult {
176
+ /** Names of cases the predicate graded correctly. */
177
+ readonly passed: readonly string[];
178
+ /** Cases the predicate graded wrong — empty iff fully conformant. */
179
+ readonly failed: readonly BindingFilterMismatch[];
180
+ }
181
+ /**
182
+ * Grade a binding resolver against the resolution catalog.
183
+ *
184
+ * `resolve` MUST be a pure computation of `(entry) →
185
+ * BindingResolutionOutcome` — schema validation of a declared list
186
+ * plus declared-wins/derivation precedence, composed however the
187
+ * implementation likes. The kit deliberately does NOT import a
188
+ * concrete resolver — adopters pass their own (the reference
189
+ * binding uses a faithful in-test implementation; see
190
+ * `./binding-conformance.test.ts`). A conformant resolver produces
191
+ * an empty `failed` array.
192
+ *
193
+ * `resolve` is invoked exactly once per case, in
194
+ * {@link bindingResolutionCases} order; it MUST be pure and MUST
195
+ * NOT throw (a throwing resolver is itself non-conformant — wrap it
196
+ * so a classified rejection becomes a returned outcome).
197
+ */
198
+ export declare function runBindingResolutionCases(resolve: (entry: BindingManifestEntry) => BindingResolutionOutcome): BindingResolutionResult;
199
+ /**
200
+ * Grade a search filter predicate against the filter catalog.
201
+ *
202
+ * `matches` MUST be a pure computation of `(bindings, filters) →
203
+ * boolean` — the same predicate every storage implementation shares
204
+ * so `tool=` / `server=` behave identically everywhere. The kit
205
+ * deliberately does NOT import a concrete predicate — adopters pass
206
+ * their own. A conformant predicate produces an empty `failed`
207
+ * array.
208
+ *
209
+ * `matches` is invoked exactly once per case, in
210
+ * {@link bindingFilterCases} order; it MUST be pure and MUST NOT
211
+ * throw.
212
+ */
213
+ export declare function runBindingFilterCases(matches: (bindings: readonly McpToolBindingDecl[] | undefined, filters: McpToolFilterDecl) => boolean): BindingFilterResult;
214
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/binding-conformance/index.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,153 @@
1
+ import bindBlueprintNoContractNone from "./cases/bind-blueprint-no-contract-none.json" with { type: "json" };
2
+ import bindContractWithoutToolNamesNone from "./cases/bind-contract-without-tool-names-none.json" with { type: "json" };
3
+ import bindDeclaredGadget from "./cases/bind-declared-gadget.json" with { type: "json" };
4
+ import bindDeclaredWinsOverContract from "./cases/bind-declared-wins-over-contract.json" with { type: "json" };
5
+ import bindDerivedCapsAtSixteen from "./cases/bind-derived-caps-at-sixteen.json" with { type: "json" };
6
+ import bindDerivedPropsAndStream from "./cases/bind-derived-props-and-stream.json" with { type: "json" };
7
+ import bindDerivedSkipsInvalidNames from "./cases/bind-derived-skips-invalid-names.json" with { type: "json" };
8
+ import bindDerivedUnionDedupes from "./cases/bind-derived-union-dedupes.json" with { type: "json" };
9
+ import bindGadgetUndeclaredNone from "./cases/bind-gadget-undeclared-none.json" with { type: "json" };
10
+ import bindRejectCharset from "./cases/bind-reject-charset.json" with { type: "json" };
11
+ import bindRejectDuplicatePair from "./cases/bind-reject-duplicate-pair.json" with { type: "json" };
12
+ import bindRejectEmptyList from "./cases/bind-reject-empty-list.json" with { type: "json" };
13
+ import bindRejectOverlongName from "./cases/bind-reject-overlong-name.json" with { type: "json" };
14
+ import bindRejectServerCharset from "./cases/bind-reject-server-charset.json" with { type: "json" };
15
+ import bindRejectTooManyEntries from "./cases/bind-reject-too-many-entries.json" with { type: "json" };
16
+ import filterCaseSensitiveServer from "./cases/filter-case-sensitive-server.json" with { type: "json" };
17
+ import filterCaseSensitiveTool from "./cases/filter-case-sensitive-tool.json" with { type: "json" };
18
+ import filterNonePassesBound from "./cases/filter-none-passes-bound.json" with { type: "json" };
19
+ import filterNonePassesUnbound from "./cases/filter-none-passes-unbound.json" with { type: "json" };
20
+ import filterPairBareNeverMatches from "./cases/filter-pair-bare-never-matches.json" with { type: "json" };
21
+ import filterPairExact from "./cases/filter-pair-exact.json" with { type: "json" };
22
+ import filterPairNotCrossEntry from "./cases/filter-pair-not-cross-entry.json" with { type: "json" };
23
+ import filterServerExcludesBare from "./cases/filter-server-excludes-bare.json" with { type: "json" };
24
+ import filterServerMatch from "./cases/filter-server-match.json" with { type: "json" };
25
+ import filterToolCrossServer from "./cases/filter-tool-cross-server.json" with { type: "json" };
26
+ import filterToolExcludesUnbound from "./cases/filter-tool-excludes-unbound.json" with { type: "json" };
27
+ import filterToolMatchesBareEntry from "./cases/filter-tool-matches-bare-entry.json" with { type: "json" };
28
+ import filterToolNoMatch from "./cases/filter-tool-no-match.json" with { type: "json" };
29
+ /**
30
+ * Every binding-resolution case the kit ships, in deterministic
31
+ * order — declared first, then derived, then the no-binding edges,
32
+ * then the schema rejects.
33
+ */
34
+ export const bindingResolutionCases = [
35
+ bindDeclaredGadget,
36
+ bindDeclaredWinsOverContract,
37
+ bindDerivedPropsAndStream,
38
+ bindDerivedUnionDedupes,
39
+ bindDerivedSkipsInvalidNames,
40
+ bindDerivedCapsAtSixteen,
41
+ bindBlueprintNoContractNone,
42
+ bindContractWithoutToolNamesNone,
43
+ bindGadgetUndeclaredNone,
44
+ bindRejectCharset,
45
+ bindRejectServerCharset,
46
+ bindRejectDuplicatePair,
47
+ bindRejectOverlongName,
48
+ bindRejectTooManyEntries,
49
+ bindRejectEmptyList,
50
+ ];
51
+ /**
52
+ * Every filter-semantics case the kit ships, in deterministic
53
+ * order — tool-dimension, server-dimension, pair, no-filter
54
+ * passthrough, then case-sensitivity.
55
+ */
56
+ export const bindingFilterCases = [
57
+ filterToolCrossServer,
58
+ filterToolMatchesBareEntry,
59
+ filterToolNoMatch,
60
+ filterServerMatch,
61
+ filterServerExcludesBare,
62
+ filterPairExact,
63
+ filterPairNotCrossEntry,
64
+ filterPairBareNeverMatches,
65
+ filterNonePassesBound,
66
+ filterNonePassesUnbound,
67
+ filterToolExcludesUnbound,
68
+ filterCaseSensitiveTool,
69
+ filterCaseSensitiveServer,
70
+ ];
71
+ function bindingListsEqual(a, b) {
72
+ if (a === undefined || b === undefined)
73
+ return a === b;
74
+ if (a.length !== b.length)
75
+ return false;
76
+ return a.every((entry, i) => {
77
+ const other = b[i];
78
+ return other !== undefined && entry.tool === other.tool && entry.server === other.server;
79
+ });
80
+ }
81
+ function resolutionOutcomeEquals(a, b) {
82
+ if (a.outcome === "reject" || b.outcome === "reject") {
83
+ return a.outcome === "reject" && b.outcome === "reject" && a.code === b.code;
84
+ }
85
+ return a.source === b.source && bindingListsEqual(a.bindings, b.bindings);
86
+ }
87
+ /**
88
+ * Grade a binding resolver against the resolution catalog.
89
+ *
90
+ * `resolve` MUST be a pure computation of `(entry) →
91
+ * BindingResolutionOutcome` — schema validation of a declared list
92
+ * plus declared-wins/derivation precedence, composed however the
93
+ * implementation likes. The kit deliberately does NOT import a
94
+ * concrete resolver — adopters pass their own (the reference
95
+ * binding uses a faithful in-test implementation; see
96
+ * `./binding-conformance.test.ts`). A conformant resolver produces
97
+ * an empty `failed` array.
98
+ *
99
+ * `resolve` is invoked exactly once per case, in
100
+ * {@link bindingResolutionCases} order; it MUST be pure and MUST
101
+ * NOT throw (a throwing resolver is itself non-conformant — wrap it
102
+ * so a classified rejection becomes a returned outcome).
103
+ */
104
+ export function runBindingResolutionCases(resolve) {
105
+ const passed = [];
106
+ const failed = [];
107
+ for (const testCase of bindingResolutionCases) {
108
+ const actual = resolve(testCase.entry);
109
+ if (resolutionOutcomeEquals(actual, testCase.expect)) {
110
+ passed.push(testCase.name);
111
+ }
112
+ else {
113
+ failed.push({
114
+ name: testCase.name,
115
+ expected: testCase.expect,
116
+ actual,
117
+ });
118
+ }
119
+ }
120
+ return { passed, failed };
121
+ }
122
+ /**
123
+ * Grade a search filter predicate against the filter catalog.
124
+ *
125
+ * `matches` MUST be a pure computation of `(bindings, filters) →
126
+ * boolean` — the same predicate every storage implementation shares
127
+ * so `tool=` / `server=` behave identically everywhere. The kit
128
+ * deliberately does NOT import a concrete predicate — adopters pass
129
+ * their own. A conformant predicate produces an empty `failed`
130
+ * array.
131
+ *
132
+ * `matches` is invoked exactly once per case, in
133
+ * {@link bindingFilterCases} order; it MUST be pure and MUST NOT
134
+ * throw.
135
+ */
136
+ export function runBindingFilterCases(matches) {
137
+ const passed = [];
138
+ const failed = [];
139
+ for (const testCase of bindingFilterCases) {
140
+ const actual = matches(testCase.bindings, testCase.filters);
141
+ if (actual === testCase.expect) {
142
+ passed.push(testCase.name);
143
+ }
144
+ else {
145
+ failed.push({
146
+ name: testCase.name,
147
+ expected: testCase.expect,
148
+ actual,
149
+ });
150
+ }
151
+ }
152
+ return { passed, failed };
153
+ }
package/dist/index.d.ts CHANGED
@@ -20,6 +20,8 @@ export { gadgetRegistrationCases, runRegistrationConformance, } from './registra
20
20
  export type { GadgetGateRejectCode, GateOutcome, RegistrationConformanceCase, RegistrationConformanceMismatch, RegistrationConformanceResult, } from './registration-conformance/index.js';
21
21
  export { gadgetResolutionCases, runResolutionConformance, } from './resolution-conformance/index.js';
22
22
  export type { GadgetUrlEntry, ResolvedGadgetUrls, ResolutionConformanceCase, ResolutionConformanceMismatch, ResolutionConformanceResult, } from './resolution-conformance/index.js';
23
+ export { bindingFilterCases, bindingResolutionCases, runBindingFilterCases, runBindingResolutionCases, } from './binding-conformance/index.js';
24
+ export type { BindingFilterCase, BindingFilterMismatch, BindingFilterResult, BindingManifestEntry, BindingRejectCode, BindingResolutionCase, BindingResolutionMismatch, BindingResolutionOutcome, BindingResolutionResult, McpToolBindingDecl, McpToolFilterDecl, } from './binding-conformance/index.js';
23
25
  export { declaresDeliveryChannel, renderLocatorUri, resourceReadCases, runResourceReadConformance, GGUI_RENDER_RESOURCE_URI, } from './resource-read-conformance/index.js';
24
26
  export type { DurableSubstrateWiring, JsonRpcErrorFrame, PreparedResourceReadScenario, ResourceReadCaller, ResourceReadConformanceCase, ResourceReadConformanceFailure, ResourceReadConformanceResult, ResourceReadConformanceSkip, ResourceReadErrorCodeDecl, ResourceReadExpectation, ResourceReadKey, ResourceReadLocator, ResourceReadOutcome, ResourceReadProbe, ResourceReadRenderMeta, ResourceReadScenario, ResourceReadScenarioDriver, ResourceReadSeed, ResourceReadServerShape, RunResourceReadConformanceOptions, } from './resource-read-conformance/index.js';
25
27
  export { loadFixture, listFixtures, loadAllFixtures } from './loader.js';
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAKH,YAAY,EAEV,eAAe,EACf,wBAAwB,EACxB,sBAAsB,EACtB,UAAU,EAEV,QAAQ,EACR,qBAAqB,EAErB,SAAS,EACT,qBAAqB,EACrB,mBAAmB,EACnB,cAAc,EACd,uBAAuB,EACvB,yBAAyB,EACzB,gCAAgC,EAChC,gBAAgB,EAEhB,YAAY,EAEZ,gBAAgB,EAChB,iBAAiB,EACjB,kBAAkB,EAClB,oBAAoB,EACpB,wBAAwB,EACxB,wBAAwB,EACxB,uBAAuB,EACvB,mBAAmB,EACnB,oBAAoB,EACpB,YAAY,EACZ,eAAe,EAEf,aAAa,EACb,sBAAsB,GACvB,MAAM,YAAY,CAAC;AAMpB,YAAY,EACV,eAAe,EAEf,SAAS,IAAI,aAAa,EAC1B,sBAAsB,EACtB,wBAAwB,EACxB,0BAA0B,EAC1B,iCAAiC,EACjC,iBAAiB,EAEjB,YAAY,IAAI,gBAAgB,GACjC,MAAM,uBAAuB,CAAC;AAK/B,OAAO,EACL,WAAW,EACX,kBAAkB,EAClB,yBAAyB,EACzB,qBAAqB,EACrB,mBAAmB,EACnB,gCAAgC,EAChC,8BAA8B,EAC9B,wBAAwB,GACzB,MAAM,qBAAqB,CAAC;AAC7B,YAAY,EAAE,YAAY,EAAE,MAAM,qBAAqB,CAAC;AASxD,OAAO,EACL,qBAAqB,EACrB,oBAAoB,GACrB,MAAM,+BAA+B,CAAC;AACvC,YAAY,EACV,qBAAqB,EACrB,yBAAyB,EACzB,uBAAuB,GACxB,MAAM,+BAA+B,CAAC;AAKvC,OAAO,EACL,uBAAuB,EACvB,0BAA0B,GAC3B,MAAM,qCAAqC,CAAC;AAC7C,YAAY,EACV,oBAAoB,EACpB,WAAW,EACX,2BAA2B,EAC3B,+BAA+B,EAC/B,6BAA6B,GAC9B,MAAM,qCAAqC,CAAC;AAK7C,OAAO,EACL,qBAAqB,EACrB,wBAAwB,GACzB,MAAM,mCAAmC,CAAC;AAC3C,YAAY,EACV,cAAc,EACd,kBAAkB,EAClB,yBAAyB,EACzB,6BAA6B,EAC7B,2BAA2B,GAC5B,MAAM,mCAAmC,CAAC;AAQ3C,OAAO,EACL,uBAAuB,EACvB,gBAAgB,EAChB,iBAAiB,EACjB,0BAA0B,EAC1B,wBAAwB,GACzB,MAAM,sCAAsC,CAAC;AAC9C,YAAY,EACV,sBAAsB,EACtB,iBAAiB,EACjB,4BAA4B,EAC5B,kBAAkB,EAClB,2BAA2B,EAC3B,8BAA8B,EAC9B,6BAA6B,EAC7B,2BAA2B,EAC3B,yBAAyB,EACzB,uBAAuB,EACvB,eAAe,EACf,mBAAmB,EACnB,mBAAmB,EACnB,iBAAiB,EACjB,sBAAsB,EACtB,oBAAoB,EACpB,0BAA0B,EAC1B,gBAAgB,EAChB,uBAAuB,EACvB,iCAAiC,GAClC,MAAM,sCAAsC,CAAC;AAG9C,OAAO,EAAE,WAAW,EAAE,YAAY,EAAE,eAAe,EAAE,MAAM,aAAa,CAAC;AAGzE,OAAO,EAAE,cAAc,EAAE,MAAM,sBAAsB,CAAC;AACtD,YAAY,EACV,oBAAoB,EACpB,iBAAiB,EACjB,kBAAkB,EAClB,cAAc,EACd,mBAAmB,EAInB,qBAAqB,EACrB,gCAAgC,GACjC,MAAM,sBAAsB,CAAC;AAK9B,OAAO,EACL,qBAAqB,EACrB,eAAe,EACf,aAAa,EACb,cAAc,EACd,WAAW,GACZ,MAAM,eAAe,CAAC;AACvB,YAAY,EAAE,sBAAsB,EAAE,MAAM,eAAe,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
package/dist/index.js CHANGED
@@ -30,6 +30,11 @@ export { gadgetRegistrationCases, runRegistrationConformance, } from './registra
30
30
  // compute for a gadget descriptor's transport fields (`bundleHost`
31
31
  // precedence, default host, loopback scheme).
32
32
  export { gadgetResolutionCases, runResolutionConformance, } from './resolution-conformance/index.js';
33
+ // Binding-conformance — MCP tool-binding resolution (declared wins,
34
+ // contract-lineage derivation, schema-invalid rejects) + the search
35
+ // filter semantics (`tool=` / `server=` case-sensitive exact,
36
+ // AND-composed per entry).
37
+ export { bindingFilterCases, bindingResolutionCases, runBindingFilterCases, runBindingResolutionCases, } from './binding-conformance/index.js';
33
38
  // Resource-read conformance — the kit's first MCP-binding driver, and
34
39
  // it binds `resources/read` ONLY (`tools/call` has none). A read of a
35
40
  // render locator has exactly two exits: a result whose contents
@@ -1,13 +1,13 @@
1
1
  {
2
2
  "name": "resolve-bundle-host-computed",
3
- "description": "A descriptor with a hostname-only `bundleHost` (no scheme, no path) and no explicit `bundleUrl`. The resolver MUST compute both the bundle and style URLs as `https://<bundleHost>/bundles/<package>/<version>/<file>` — the path layout matching `@ggui-ai/registry-core`'s `BundleStorage` so a bundle published to one registry resolves cleanly from the other.",
3
+ "description": "A descriptor with a hostname-only `bundleHost` (no scheme, no path) and no explicit `bundleUrl`. The resolver MUST compute both the bundle and style URLs as `https://<bundleHost>/bundles/public/<package>/<version>/<file>` — the PUBLIC prefix of `@ggui-ai/registry-core`'s visibility-split `BundleStorage` layout (H1). bundleHost-tier composition is anonymous render-time fetching, which only the public prefix serves; private artifacts reach the iframe via an explicit presigned `bundleUrl`, never via host composition. Keeping the shapes aligned means a bundle published to one registry resolves cleanly from the other.",
4
4
  "entry": {
5
5
  "package": "@acme/leaflet-wrapper",
6
6
  "version": "2.3.1",
7
7
  "bundleHost": "cdn.example.com"
8
8
  },
9
9
  "expect": {
10
- "bundleUrl": "https://cdn.example.com/bundles/@acme/leaflet-wrapper/2.3.1/bundle.js",
11
- "styleUrl": "https://cdn.example.com/bundles/@acme/leaflet-wrapper/2.3.1/style.css"
10
+ "bundleUrl": "https://cdn.example.com/bundles/public/@acme/leaflet-wrapper/2.3.1/bundle.js",
11
+ "styleUrl": "https://cdn.example.com/bundles/public/@acme/leaflet-wrapper/2.3.1/style.css"
12
12
  }
13
13
  }
@@ -6,7 +6,7 @@
6
6
  "version": "2.3.1"
7
7
  },
8
8
  "expect": {
9
- "bundleUrl": "https://registry.ggui.ai/bundles/@acme/leaflet-wrapper/2.3.1/bundle.js",
10
- "styleUrl": "https://registry.ggui.ai/bundles/@acme/leaflet-wrapper/2.3.1/style.css"
9
+ "bundleUrl": "https://registry.ggui.ai/bundles/public/@acme/leaflet-wrapper/2.3.1/bundle.js",
10
+ "styleUrl": "https://registry.ggui.ai/bundles/public/@acme/leaflet-wrapper/2.3.1/style.css"
11
11
  }
12
12
  }
@@ -7,7 +7,7 @@
7
7
  "bundleHost": "localhost:4873"
8
8
  },
9
9
  "expect": {
10
- "bundleUrl": "http://localhost:4873/bundles/@acme/leaflet-wrapper/2.3.1/bundle.js",
11
- "styleUrl": "http://localhost:4873/bundles/@acme/leaflet-wrapper/2.3.1/style.css"
10
+ "bundleUrl": "http://localhost:4873/bundles/public/@acme/leaflet-wrapper/2.3.1/bundle.js",
11
+ "styleUrl": "http://localhost:4873/bundles/public/@acme/leaflet-wrapper/2.3.1/style.css"
12
12
  }
13
13
  }
@@ -0,0 +1,24 @@
1
+ {
2
+ "name": "channel-less-read-mounts-inline",
3
+ "description": "The inline static channel makes a channel-less server mountable: no static component URL is wired, no live channel is wired, but the committed component fits the inline codeB64 cap — so the read returns a shell whose projected render meta declares the inline channel, not a typed failure. This is the success half of the size split whose failure half is read-without-a-delivery-channel-answers-not-mountable: the SAME server wiring answers by component size class, which is what makes the inline channel a real delivery channel rather than a serving detail.",
4
+ "server": { "durableSubstrate": "all" },
5
+ "caller": "owner",
6
+ "seeds": [
7
+ {
8
+ "kind": "committed-render",
9
+ "session": "3f1a9c22-7b4e-4d18-9a05-6c2e8b1d4f70",
10
+ "size": "under-inline-cap"
11
+ }
12
+ ],
13
+ "reads": [
14
+ {
15
+ "as": "inline",
16
+ "locator": {
17
+ "kind": "render",
18
+ "session": "3f1a9c22-7b4e-4d18-9a05-6c2e8b1d4f70",
19
+ "key": { "kind": "literal", "value": "fedcba9876543210" }
20
+ },
21
+ "expect": { "kind": "live-mount" }
22
+ }
23
+ ]
24
+ }
@@ -0,0 +1,29 @@
1
+ {
2
+ "name": "pin-past-the-head-answers-not-found",
3
+ "description": "Epoch history (#483, SPEC §7.1.2.2): a pinned locator whose epoch exceeds the session's head names a record that does not exist — the same NOT_FOUND on -32002, detail-free, that a missing session answers. Nothing about the head's value may leak through the error shape: a caller must not be able to binary-search the history depth off error variance.",
4
+ "server": { "durableSubstrate": "all", "liveChannel": true },
5
+ "caller": "owner",
6
+ "seeds": [
7
+ {
8
+ "kind": "epoch-history",
9
+ "session": "7a4d9c15-8e2f-4b06-a3c9-5d1f8e0b2c47",
10
+ "records": [{ "props": { "count": 1 } }, { "props": { "count": 5 } }]
11
+ }
12
+ ],
13
+ "reads": [
14
+ {
15
+ "as": "past-head",
16
+ "locator": {
17
+ "kind": "render",
18
+ "session": "7a4d9c15-8e2f-4b06-a3c9-5d1f8e0b2c47",
19
+ "epoch": 7
20
+ },
21
+ "expect": {
22
+ "kind": "typed-error",
23
+ "jsonRpcCode": -32002,
24
+ "dataCode": "NOT_FOUND",
25
+ "detailAbsent": true
26
+ }
27
+ }
28
+ ]
29
+ }
@@ -0,0 +1,42 @@
1
+ {
2
+ "name": "pinned-superseded-record-serves-its-reign-immutably",
3
+ "description": "Epoch history (#483, SPEC §7.1.2.2): a session whose head advanced to epoch 1 still serves its superseded epoch-0 record on the pinned locator — with the props of ITS reign (the amended mint state), never the successor's — and serves them byte-identically on a second read. The bare locator meanwhile tracks the live head. Immutability is graded on propsJson only: live-channel tokens on the same meta legitimately differ per read; the RECORD is what may not.",
4
+ "server": { "durableSubstrate": "all", "liveChannel": true },
5
+ "caller": "owner",
6
+ "seeds": [
7
+ {
8
+ "kind": "epoch-history",
9
+ "session": "5e2c7a90-4f1b-4c3d-8a67-9b0e2d4f6a81",
10
+ "records": [{ "props": { "count": 1 } }, { "props": { "count": 5 } }]
11
+ }
12
+ ],
13
+ "reads": [
14
+ {
15
+ "as": "pin0-first",
16
+ "locator": {
17
+ "kind": "render",
18
+ "session": "5e2c7a90-4f1b-4c3d-8a67-9b0e2d4f6a81",
19
+ "epoch": 0
20
+ },
21
+ "expect": { "kind": "pinned-mount", "propsInclude": { "count": 1 } }
22
+ },
23
+ {
24
+ "as": "pin0-second",
25
+ "locator": {
26
+ "kind": "render",
27
+ "session": "5e2c7a90-4f1b-4c3d-8a67-9b0e2d4f6a81",
28
+ "epoch": 0
29
+ },
30
+ "expect": { "kind": "pinned-mount", "propsInclude": { "count": 1 } }
31
+ },
32
+ {
33
+ "as": "head",
34
+ "locator": {
35
+ "kind": "render",
36
+ "session": "5e2c7a90-4f1b-4c3d-8a67-9b0e2d4f6a81"
37
+ },
38
+ "expect": { "kind": "pinned-mount", "propsInclude": { "count": 5 } }
39
+ }
40
+ ],
41
+ "identicalMounts": ["pin0-first", "pin0-second"]
42
+ }
@@ -1,16 +1,27 @@
1
1
  {
2
2
  "name": "read-without-a-delivery-channel-answers-not-mountable",
3
- "description": "The locator resolves and the caller owns it, but the server wires no way to deliver a component — neither a static component URL nor a live channel. The read answers NOT_MOUNTABLE on -32006 rather than handing back a shell with nothing to mount. This is the branch that proves the invariant is about the RESULT, not the lookup: the lookup succeeded.",
4
- "server": { "durableSubstrate": "all" },
3
+ "description": "The locator resolves and the caller owns it, but the server wires no way to deliver the component \u2014 neither a static component URL nor a live channel, and the committed component is too large for the inline codeB64 channel (an under-cap component would mount inline; that arm has its own case). The read answers NOT_MOUNTABLE on -32006 rather than handing back a shell with nothing to mount. This is the branch that proves the invariant is about the RESULT, not the lookup: the lookup succeeded.",
4
+ "server": {
5
+ "durableSubstrate": "all"
6
+ },
5
7
  "caller": "owner",
6
- "seeds": [{ "kind": "committed-render", "session": "3f1a9c22-7b4e-4d18-9a05-6c2e8b1d4f70" }],
8
+ "seeds": [
9
+ {
10
+ "kind": "committed-render",
11
+ "session": "3f1a9c22-7b4e-4d18-9a05-6c2e8b1d4f70",
12
+ "size": "over-inline-cap"
13
+ }
14
+ ],
7
15
  "reads": [
8
16
  {
9
17
  "as": "resolvable",
10
18
  "locator": {
11
19
  "kind": "render",
12
20
  "session": "3f1a9c22-7b4e-4d18-9a05-6c2e8b1d4f70",
13
- "key": { "kind": "literal", "value": "fedcba9876543210" }
21
+ "key": {
22
+ "kind": "literal",
23
+ "value": "fedcba9876543210"
24
+ }
14
25
  },
15
26
  "expect": {
16
27
  "kind": "typed-error",