@ggui-ai/protocol-conformance 0.1.0-rc.1

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 (107) hide show
  1. package/LICENSE +201 -0
  2. package/README.md +89 -0
  3. package/dist/cli.d.ts +15 -0
  4. package/dist/cli.d.ts.map +1 -0
  5. package/dist/cli.js +193 -0
  6. package/dist/conformance-host.d.ts +155 -0
  7. package/dist/conformance-host.d.ts.map +1 -0
  8. package/dist/conformance-host.js +45 -0
  9. package/dist/fixtures/bootstrap-protocol/bootstrap-bundle-fetch-failed.json +26 -0
  10. package/dist/fixtures/bootstrap-protocol/bootstrap-meta-missing.json +32 -0
  11. package/dist/fixtures/bootstrap-protocol/bootstrap-success.json +17 -0
  12. package/dist/fixtures/bootstrap-protocol/index.d.ts +5 -0
  13. package/dist/fixtures/bootstrap-protocol/index.d.ts.map +1 -0
  14. package/dist/fixtures/bootstrap-protocol/index.js +24 -0
  15. package/dist/fixtures/canvas-mode-wire-shapes/canvas-bootstrap-mutual-exclusion.json +25 -0
  16. package/dist/fixtures/canvas-mode-wire-shapes/canvas-lifecycle-channel-emits-handshake-started.json +30 -0
  17. package/dist/fixtures/canvas-mode-wire-shapes/canvas-navigated-updates-active-stack-item.json +30 -0
  18. package/dist/fixtures/canvas-mode-wire-shapes/host-context-observed-persists.json +22 -0
  19. package/dist/fixtures/canvas-mode-wire-shapes/index.d.ts +5 -0
  20. package/dist/fixtures/canvas-mode-wire-shapes/index.d.ts.map +1 -0
  21. package/dist/fixtures/canvas-mode-wire-shapes/index.js +47 -0
  22. package/dist/fixtures/index.d.ts +78 -0
  23. package/dist/fixtures/index.d.ts.map +1 -0
  24. package/dist/fixtures/index.js +87 -0
  25. package/dist/fixtures/observability-events/index.d.ts +5 -0
  26. package/dist/fixtures/observability-events/index.d.ts.map +1 -0
  27. package/dist/fixtures/observability-events/index.js +22 -0
  28. package/dist/fixtures/observability-events/observability-contract-error-emitted.json +29 -0
  29. package/dist/fixtures/observability-events/observability-wired-tool-invoked.json +28 -0
  30. package/dist/fixtures/refresh-semantics/index.d.ts +5 -0
  31. package/dist/fixtures/refresh-semantics/index.d.ts.map +1 -0
  32. package/dist/fixtures/refresh-semantics/index.js +19 -0
  33. package/dist/fixtures/refresh-semantics/stream-refresh-success.json +40 -0
  34. package/dist/fixtures/reserved-channel-authority/index.d.ts +4 -0
  35. package/dist/fixtures/reserved-channel-authority/index.d.ts.map +1 -0
  36. package/dist/fixtures/reserved-channel-authority/index.js +21 -0
  37. package/dist/fixtures/reserved-channel-authority/props-update-roundtrip.json +34 -0
  38. package/dist/fixtures/reserved-channel-authority/stream-schema-violation.json +45 -0
  39. package/dist/fixtures/schema-version-handshake/index.d.ts +4 -0
  40. package/dist/fixtures/schema-version-handshake/index.d.ts.map +1 -0
  41. package/dist/fixtures/schema-version-handshake/index.js +20 -0
  42. package/dist/fixtures/schema-version-handshake/version-match.json +16 -0
  43. package/dist/fixtures/schema-version-handshake/version-mismatch.json +26 -0
  44. package/dist/fixtures/wired-action-dispatch/index.d.ts +5 -0
  45. package/dist/fixtures/wired-action-dispatch/index.d.ts.map +1 -0
  46. package/dist/fixtures/wired-action-dispatch/index.js +29 -0
  47. package/dist/fixtures/wired-action-dispatch/wired-action-success.json +36 -0
  48. package/dist/fixtures/wired-action-dispatch/wired-action-tool-not-found.json +31 -0
  49. package/dist/fixtures/wired-action-dispatch/wired-action-tool-threw.json +32 -0
  50. package/dist/fixtures/wired-action-dispatch/wired-action-tool-timeout.json +32 -0
  51. package/dist/index.d.ts +28 -0
  52. package/dist/index.d.ts.map +1 -0
  53. package/dist/index.js +40 -0
  54. package/dist/loader.d.ts +19 -0
  55. package/dist/loader.d.ts.map +1 -0
  56. package/dist/loader.js +46 -0
  57. package/dist/match-behavior.d.ts +121 -0
  58. package/dist/match-behavior.d.ts.map +1 -0
  59. package/dist/match-behavior.js +385 -0
  60. package/dist/registration-conformance/cases/gate-export-not-registered.json +19 -0
  61. package/dist/registration-conformance/cases/gate-no-gadgets-accept.json +13 -0
  62. package/dist/registration-conformance/cases/gate-package-mismatch.json +19 -0
  63. package/dist/registration-conformance/cases/gate-priority-not-registered-outranks-mismatch.json +20 -0
  64. package/dist/registration-conformance/cases/gate-registered-resolves-accept.json +25 -0
  65. package/dist/registration-conformance/cases/gate-stdlib-hook-accept.json +27 -0
  66. package/dist/registration-conformance/cases/gate-typo-classifies-as-not-registered.json +27 -0
  67. package/dist/registration-conformance/index.d.ts +114 -0
  68. package/dist/registration-conformance/index.d.ts.map +1 -0
  69. package/dist/registration-conformance/index.js +63 -0
  70. package/dist/reporter.d.ts +32 -0
  71. package/dist/reporter.d.ts.map +1 -0
  72. package/dist/reporter.js +151 -0
  73. package/dist/resolution-conformance/cases/resolve-bundle-host-computed.json +13 -0
  74. package/dist/resolution-conformance/cases/resolve-default-host.json +12 -0
  75. package/dist/resolution-conformance/cases/resolve-explicit-bundle-and-style-url.json +14 -0
  76. package/dist/resolution-conformance/cases/resolve-explicit-bundle-url.json +12 -0
  77. package/dist/resolution-conformance/cases/resolve-loopback-host-http-scheme.json +13 -0
  78. package/dist/resolution-conformance/cases/resolve-package-only-no-version.json +8 -0
  79. package/dist/resolution-conformance/cases/resolve-stdlib-no-urls.json +10 -0
  80. package/dist/resolution-conformance/index.d.ts +76 -0
  81. package/dist/resolution-conformance/index.d.ts.map +1 -0
  82. package/dist/resolution-conformance/index.js +81 -0
  83. package/dist/run-conformance.d.ts +58 -0
  84. package/dist/run-conformance.d.ts.map +1 -0
  85. package/dist/run-conformance.js +286 -0
  86. package/dist/schema-conformance/cases/wire-bad-export-name-reject.json +11 -0
  87. package/dist/schema-conformance/cases/wire-bad-package-name-reject.json +11 -0
  88. package/dist/schema-conformance/cases/wire-component-export-accept.json +10 -0
  89. package/dist/schema-conformance/cases/wire-empty-package-reject.json +11 -0
  90. package/dist/schema-conformance/cases/wire-minimal-accept.json +10 -0
  91. package/dist/schema-conformance/cases/wire-multi-package-accept.json +14 -0
  92. package/dist/schema-conformance/cases/wire-prose-accept.json +15 -0
  93. package/dist/schema-conformance/cases/wire-registry-metadata-reject.json +13 -0
  94. package/dist/schema-conformance/cases/wire-stale-libraries-reject.json +14 -0
  95. package/dist/schema-conformance/cases/wire-stdlib-accept.json +10 -0
  96. package/dist/schema-conformance/cases/wire-transport-field-reject.json +15 -0
  97. package/dist/schema-conformance/cases/wire-version-field-reject.json +13 -0
  98. package/dist/schema-conformance/index.d.ts +71 -0
  99. package/dist/schema-conformance/index.d.ts.map +1 -0
  100. package/dist/schema-conformance/index.js +103 -0
  101. package/dist/types.d.ts +434 -0
  102. package/dist/types.d.ts.map +1 -0
  103. package/dist/types.js +62 -0
  104. package/dist/ws-transport.d.ts +42 -0
  105. package/dist/ws-transport.d.ts.map +1 -0
  106. package/dist/ws-transport.js +135 -0
  107. package/package.json +83 -0
@@ -0,0 +1,103 @@
1
+ /**
2
+ * Schema-conformance catalog — pure-schema accept / reject cases for
3
+ * the gadget WIRE shape (`DataContract.clientCapabilities`, SPEC
4
+ * §7.7.2).
5
+ *
6
+ * ## Why this is SEPARATE from the `./fixtures` behavioral catalog
7
+ *
8
+ * The `./fixtures` catalog asserts *behavioral* obligations — what a
9
+ * live implementation DOES when driven by an input envelope (a
10
+ * push-gate rejection, a bootstrap projection). Those need a host, a
11
+ * transport, and a runner.
12
+ *
13
+ * This catalog asserts a *structural* obligation: which
14
+ * `clientCapabilities` payloads a spec-conformant parser MUST accept
15
+ * and which it MUST reject — a property of the schema alone, with no
16
+ * session, no transport, no host. Folding it into the behavioral
17
+ * fixture shape (`TestCase`) would mean inventing a no-op
18
+ * `inputEnvelope` and a fake behavior kind for every case. Keeping it
19
+ * a distinct, smaller artifact is the honest modeling.
20
+ *
21
+ * ## Polyglot, decoupled
22
+ *
23
+ * Each case ships as raw JSON under `./cases/` so a non-TypeScript
24
+ * implementer (Python, Go, Rust) can read the catalog and grade their
25
+ * own `clientCapabilities` parser. {@link runSchemaConformance} takes
26
+ * the validator as a callback — the kit never imports a concrete
27
+ * schema here, so adopters drive their OWN parser. The reference
28
+ * binding (the kit's own meta-test) wires `@ggui-ai/protocol`'s
29
+ * `clientCapabilitiesSpecSchema`; see `./schema-conformance.test.ts`.
30
+ *
31
+ * The authored vocabulary is pinned here, deliberately decoupled from
32
+ * the live `@ggui-ai/protocol` source tree — the same drift discipline
33
+ * `../types.ts` applies to the behavioral fixture surface.
34
+ */
35
+ import wireBadExportNameReject from './cases/wire-bad-export-name-reject.json' with { type: 'json' };
36
+ import wireBadPackageNameReject from './cases/wire-bad-package-name-reject.json' with { type: 'json' };
37
+ import wireComponentExportAccept from './cases/wire-component-export-accept.json' with { type: 'json' };
38
+ import wireEmptyPackageReject from './cases/wire-empty-package-reject.json' with { type: 'json' };
39
+ import wireMinimalAccept from './cases/wire-minimal-accept.json' with { type: 'json' };
40
+ import wireMultiPackageAccept from './cases/wire-multi-package-accept.json' with { type: 'json' };
41
+ import wireProseAccept from './cases/wire-prose-accept.json' with { type: 'json' };
42
+ import wireRegistryMetadataReject from './cases/wire-registry-metadata-reject.json' with { type: 'json' };
43
+ import wireStaleLibrariesReject from './cases/wire-stale-libraries-reject.json' with { type: 'json' };
44
+ import wireStdlibAccept from './cases/wire-stdlib-accept.json' with { type: 'json' };
45
+ import wireTransportFieldReject from './cases/wire-transport-field-reject.json' with { type: 'json' };
46
+ import wireVersionFieldReject from './cases/wire-version-field-reject.json' with { type: 'json' };
47
+ /**
48
+ * Every gadget-wire schema-conformance case the kit ships, in
49
+ * deterministic order — accepts first, then rejects, each group
50
+ * lexicographic by `name`.
51
+ */
52
+ export const gadgetWireSchemaCases = [
53
+ wireComponentExportAccept,
54
+ wireMinimalAccept,
55
+ wireMultiPackageAccept,
56
+ wireProseAccept,
57
+ wireStdlibAccept,
58
+ wireBadExportNameReject,
59
+ wireBadPackageNameReject,
60
+ wireEmptyPackageReject,
61
+ wireRegistryMetadataReject,
62
+ wireStaleLibrariesReject,
63
+ wireTransportFieldReject,
64
+ wireVersionFieldReject,
65
+ ];
66
+ /**
67
+ * Grade a `clientCapabilities` validator against the gadget-wire
68
+ * schema-conformance catalog.
69
+ *
70
+ * `validate` MUST return `true` iff its parser accepts the input as a
71
+ * well-formed `DataContract.clientCapabilities`, `false` otherwise.
72
+ * The kit deliberately does NOT import a concrete schema — adopters
73
+ * pass their own parser (a zod `safeParse`, a Go struct decode, a
74
+ * pydantic model) and the catalog grades it. A conformant validator
75
+ * produces an empty `failed` array.
76
+ *
77
+ * `validate` is invoked exactly once per case; it MUST be pure and
78
+ * MUST NOT throw (a throwing parser is itself non-conformant — wrap
79
+ * it). Cases are graded in {@link gadgetWireSchemaCases} order.
80
+ */
81
+ export function runSchemaConformance(validate) {
82
+ const passed = [];
83
+ const failed = [];
84
+ for (const testCase of gadgetWireSchemaCases) {
85
+ const actual = validate(testCase.clientCapabilities)
86
+ ? 'accept'
87
+ : 'reject';
88
+ if (actual === testCase.expect) {
89
+ passed.push(testCase.name);
90
+ }
91
+ else {
92
+ failed.push({
93
+ name: testCase.name,
94
+ expected: testCase.expect,
95
+ actual,
96
+ ...(testCase.rejectReason !== undefined
97
+ ? { rejectReason: testCase.rejectReason }
98
+ : {}),
99
+ });
100
+ }
101
+ }
102
+ return { passed, failed };
103
+ }
@@ -0,0 +1,434 @@
1
+ /**
2
+ * `@ggui-ai/protocol-conformance` fixture format — the authoring
3
+ * contract third-party implementers compile against.
4
+ *
5
+ * This module is the public, wire-stable shape of every fixture case
6
+ * under `./fixtures/**`. These declarations are the single source of
7
+ * truth for the fixture-authoring vocabulary; other consumers in the
8
+ * project re-export from this package rather than redefining it.
9
+ *
10
+ * ## Naming-load-bearing rule
11
+ *
12
+ * The shape of {@link TestCase}, {@link SetupStep},
13
+ * {@link TeardownStep}, and {@link ExpectedBehavior} IS the public
14
+ * API. Renaming a field breaks every third-party consumer of the
15
+ * conformance kit. Treat this file the way `@ggui-ai/protocol`'s
16
+ * type declarations get treated: additive changes only, extensibility
17
+ * via `(string & {})` tails.
18
+ *
19
+ * ## Extensibly-closed unions
20
+ *
21
+ * Every discriminator in this module carries a `(string & {})` tail —
22
+ * see SPEC §extensibility and `ContractErrorCode` in
23
+ * `@ggui-ai/protocol`. Third-party conformance-host implementations
24
+ * MAY introduce setup / teardown / expectation kinds the reference
25
+ * kit doesn't recognize; the runner SHOULD skip such cases with a
26
+ * warning rather than failing loudly, exactly the way
27
+ * `ContractErrorCode`'s extensibility works.
28
+ *
29
+ * Only these discriminators are extensibly closed:
30
+ * - `SetupStep['type']`
31
+ * - `TeardownStep['type']`
32
+ * - `ExpectedBehavior['kind']`
33
+ * - `ExpectedObservabilityEvent['kind']`
34
+ * - `TransportConfig['kind']`
35
+ *
36
+ * Everything else — tool names, envelope field names, contract error
37
+ * codes — is passed verbatim and the implementation under test owns
38
+ * the recognition set.
39
+ *
40
+ * ## Why JSON + a loader, not pure TS
41
+ *
42
+ * Third-party consumers of the conformance kit are not all
43
+ * TypeScript. The JSON format means a Python / Go / Rust implementer
44
+ * can parse the same fixture catalog without shipping a TS compiler.
45
+ * The loader exists so in-process TS consumers don't re-invent the
46
+ * parser.
47
+ *
48
+ * ## Why inline authored vocabulary (not `import type` from `@ggui-ai/protocol`)
49
+ *
50
+ * The conformance kit is the AUTHORED artifact — its vocabulary is
51
+ * pinned here, deliberately decoupled from the live source tree. A
52
+ * drift check between these authored unions and the live protocol /
53
+ * renderer types belongs in the kit's own meta-tests, NOT in this
54
+ * file's import graph. If `@ggui-ai/protocol` adds a new
55
+ * `ContractErrorCode` canonical member tomorrow, conformance kit
56
+ * consumers MUST NOT be forced to recompile — their fixtures
57
+ * continue to reference the value they authored, and the runner's
58
+ * extensible `(string & {})` tail handles recognition. This mirrors
59
+ * the protocol's own evolution discipline: authored shapes stay
60
+ * additive.
61
+ */
62
+ /**
63
+ * Transport binding the kit uses to reach the implementation under
64
+ * test. v1.0 ships WS-only — WebSocket is the canonical ggui
65
+ * transport.
66
+ *
67
+ * The union is extensibly-closed (`(string & {})` tail) so post-v1.1
68
+ * transports (stdio MCP, HTTP long-poll) can land without breaking
69
+ * the kit's public API. The known arm is the full config object;
70
+ * unknown arms are accepted as opaque records so third-party runners
71
+ * can extend without a package bump.
72
+ *
73
+ * Pattern mirrored from `ContractErrorCode` in
74
+ * `@ggui-ai/protocol/types/data-contract`.
75
+ */
76
+ export type TransportConfig = WebSocketTransportConfig | UnknownTransportConfig;
77
+ export interface WebSocketTransportConfig {
78
+ readonly kind: 'ws';
79
+ /** Fully-qualified WebSocket URL, e.g. `ws://localhost:3000/ws`. */
80
+ readonly url: string;
81
+ /** Auth carried on the upgrade request. See {@link AuthConfig}. */
82
+ readonly auth: AuthConfig;
83
+ }
84
+ /**
85
+ * Future-compat catch. Runners that receive a transport `kind` they
86
+ * don't recognize MUST fail loudly — an unrecognized transport is a
87
+ * configuration error, not a runtime extension point.
88
+ */
89
+ export interface UnknownTransportConfig {
90
+ readonly kind: string & {};
91
+ readonly [field: string]: unknown;
92
+ }
93
+ /**
94
+ * Authentication carried to the implementation under test. v1.0 names
95
+ * two concrete shapes. Extensibility follows the transport pattern —
96
+ * new auth shapes land as additional arms, not as a free-form record.
97
+ */
98
+ export type AuthConfig = {
99
+ readonly kind: 'bearer';
100
+ readonly token: string;
101
+ } | {
102
+ readonly kind: 'session-cookie';
103
+ readonly cookie: string;
104
+ };
105
+ /**
106
+ * Directive a conformance host interprets BEFORE driving a fixture's
107
+ * `inputEnvelope`. The runner dispatches each step against the host's
108
+ * injected `ConformanceHost` interface (see `./conformance-host`) —
109
+ * this module authors the declarative intent; the host owns the
110
+ * implementation.
111
+ *
112
+ * Discriminator is extensibly closed: unknown `type` values skip the
113
+ * fixture with a warning rather than erroring. Third-party hosts MAY
114
+ * extend the setup vocabulary without a kit version bump.
115
+ */
116
+ export type SetupStep = CreateSessionStep | RegisterToolStep | EmitEnvelopeStep | SeedChannelStep | UnknownSetupStep;
117
+ export interface CreateSessionStep {
118
+ readonly type: 'create-session';
119
+ /** Opaque session id the host allocates. Downstream steps + the
120
+ * fixture's `inputEnvelope` reference it by value. */
121
+ readonly sessionId: string;
122
+ /** Optional app (tenant) id the session scopes to. Defaults to the
123
+ * host's implementation-defined "default app". */
124
+ readonly appId?: string;
125
+ }
126
+ export interface RegisterToolStep {
127
+ readonly type: 'register-tool';
128
+ readonly toolName: string;
129
+ /** Handler identifier the host maps to a real implementation. The
130
+ * kit is authoring-only — it doesn't ship handler bodies. Known
131
+ * values the reference `@ggui-ai/mcp-server`-backed host supports:
132
+ *
133
+ * - `'echo'` — returns `{received: args}`.
134
+ * - `'throw'` — rejects with `Error('tool_threw_for_fixture')`.
135
+ * - `'timeout'`— never resolves (host enforces its own timeout).
136
+ * - `'malformed-stream'` — returns `{wrong:'shape'}`.
137
+ *
138
+ * Third-party hosts MAY recognize additional handlers; the kit
139
+ * records the string verbatim. */
140
+ readonly handler: string;
141
+ }
142
+ export interface EmitEnvelopeStep {
143
+ readonly type: 'emit-envelope';
144
+ /** Reserved-channel or declared-channel name. */
145
+ readonly channel: string;
146
+ /** Raw envelope body. Host is responsible for wrapping in the wire
147
+ * format (sequence stamp, timestamp, etc.). */
148
+ readonly payload: unknown;
149
+ }
150
+ export interface SeedChannelStep {
151
+ readonly type: 'seed-channel';
152
+ readonly sessionId: string;
153
+ readonly channel: string;
154
+ readonly value: unknown;
155
+ }
156
+ /**
157
+ * Future-compat catch. Hosts that do not recognize the `type` SHOULD
158
+ * skip the case with a warning.
159
+ */
160
+ export interface UnknownSetupStep {
161
+ readonly type: string & {};
162
+ readonly [field: string]: unknown;
163
+ }
164
+ /**
165
+ * Teardown directive — mirrors {@link SetupStep}'s vocabulary, scoped
166
+ * to cleanup.
167
+ */
168
+ export type TeardownStep = CloseSessionStep | UnregisterToolStep | UnknownTeardownStep;
169
+ export interface CloseSessionStep {
170
+ readonly type: 'close-session';
171
+ readonly sessionId: string;
172
+ }
173
+ export interface UnregisterToolStep {
174
+ readonly type: 'unregister-tool';
175
+ readonly toolName: string;
176
+ }
177
+ export interface UnknownTeardownStep {
178
+ readonly type: string & {};
179
+ readonly [field: string]: unknown;
180
+ }
181
+ /**
182
+ * What the test asserts after the input envelope drives the host.
183
+ *
184
+ * Discriminator is extensibly closed via `(string & {})`. Each arm
185
+ * carries only the fields the assertion semantically requires — no
186
+ * optional-everything-bags. Cross-cuts (e.g. every failure-path case
187
+ * also expects a matching observability event) are authored as
188
+ * {@link TestCase.expectedBehavior} of one kind PLUS
189
+ * {@link TestCase.expectedObservability} alongside, not as nested
190
+ * unions.
191
+ */
192
+ export type ExpectedBehavior = ContractErrorBehavior | StreamUpdateBehavior | ObservabilityBehavior | BootstrapFailureBehavior | BootstrapSuccessBehavior | VersionMismatchBehavior | PropsUpdateBehavior | NoOpBehavior | UnknownBehavior;
193
+ /**
194
+ * Expect a `_ggui:contract-error` envelope with the given code + tool
195
+ * + optional actionName + optional sourceAction.type. Matches the
196
+ * renderer's {@link ProtocolError} kind `'contract'` variant AND the
197
+ * server-wire `ContractErrorPayload` shape.
198
+ */
199
+ export interface ContractErrorBehavior {
200
+ readonly kind: 'contract-error';
201
+ readonly code: ContractErrorCode;
202
+ readonly toolName: string;
203
+ readonly actionName?: string;
204
+ readonly sourceAction?: 'wired-action' | 'refresh-stream' | (string & {});
205
+ /**
206
+ * Optional observability co-assertion. Most contract-error paths
207
+ * also emit a `contract-error-emitted` observability event with the
208
+ * same `code` + `toolName`. Authoring this here keeps the
209
+ * cross-cut in one place.
210
+ */
211
+ readonly observability?: ExpectedObservabilityEvent;
212
+ }
213
+ /** Expect a stream-update on the named channel matching the given value
214
+ * shape (deep-equal, via the host's matcher). */
215
+ export interface StreamUpdateBehavior {
216
+ readonly kind: 'stream-update';
217
+ readonly channel: string;
218
+ readonly value: unknown;
219
+ }
220
+ /** Expect a standalone observability event (no wired-action / refresh
221
+ * side-effect paired). Mostly used by happy-path rows + the
222
+ * `wired-tool-invoked` event. */
223
+ export interface ObservabilityBehavior {
224
+ readonly kind: 'observability-event';
225
+ readonly event: ExpectedObservabilityEvent;
226
+ }
227
+ /**
228
+ * Expect a bootstrap-failure postMessage envelope + the renderer's
229
+ * {@link ProtocolError} of kind `'bootstrap'` with the matching
230
+ * reason. Post-wire paths (`BUNDLE_FETCH_FAILED`,
231
+ * `BOOTSTRAP_META_MISSING`) are pre-WS — they surface via
232
+ * `postMessage({type:'ggui:bootstrap-failed', …})` only, NOT on
233
+ * the live channel.
234
+ */
235
+ export interface BootstrapFailureBehavior {
236
+ readonly kind: 'bootstrap-failure';
237
+ readonly reason: BootstrapFailureReason;
238
+ /** Optional substring the renderer's `message` field should contain.
239
+ * Keeps assertions resilient to message copy edits. */
240
+ readonly messageContains?: string;
241
+ }
242
+ /**
243
+ * Expect the renderer to reach the "ready to accept actions" state —
244
+ * no bootstrap failure, no version rejection, the first stack item
245
+ * rendered with `data-ggui-code-ready="true"` (or the host's
246
+ * equivalent signal).
247
+ */
248
+ export interface BootstrapSuccessBehavior {
249
+ readonly kind: 'bootstrap-success';
250
+ /** Optional: observability events the host MUST emit in order. */
251
+ readonly observabilitySequence?: readonly ExpectedObservabilityEvent[];
252
+ }
253
+ /**
254
+ * Expect a version-handshake rejection. Maps to the
255
+ * `{type:'ggui:upgrade-required', server, client}` postMessage
256
+ * envelope + the renderer's {@link ProtocolError} kind `'version'`.
257
+ */
258
+ export interface VersionMismatchBehavior {
259
+ readonly kind: 'version-mismatch';
260
+ readonly serverVersion: string;
261
+ readonly clientAccepts: readonly string[];
262
+ }
263
+ /**
264
+ * Expect a `props_update` round-trip — hosts deliver new props via
265
+ * channel 0, the iframe DOM reflects the update within `timeoutMs`.
266
+ */
267
+ export interface PropsUpdateBehavior {
268
+ readonly kind: 'props-update';
269
+ readonly channel: string;
270
+ /** Props values the host MUST deliver. */
271
+ readonly props: Record<string, unknown>;
272
+ /** DOM-level attribute / text evidence the update landed. */
273
+ readonly evidence: {
274
+ readonly selector: string;
275
+ readonly attribute?: string;
276
+ readonly expected: string;
277
+ };
278
+ }
279
+ /**
280
+ * No observable side-effect expected. Used for inputs the protocol
281
+ * intentionally drops (malformed envelopes, unrecognized JSON-RPC
282
+ * methods). The host SHOULD warn internally; the wire stays silent.
283
+ */
284
+ export interface NoOpBehavior {
285
+ readonly kind: 'no-op';
286
+ /** Human-readable justification — why silence is the correct
287
+ * outcome. Surfaces in the reporter so operators can audit it. */
288
+ readonly reason: string;
289
+ }
290
+ /** Future-compat catch. Runner skips with a warning. */
291
+ export interface UnknownBehavior {
292
+ readonly kind: string & {};
293
+ readonly [field: string]: unknown;
294
+ }
295
+ /**
296
+ * Narrowed observability-event shape for fixture assertions. Parallel
297
+ * to `@ggui-ai/iframe-runtime`'s `ObservabilityEvent` but with every field
298
+ * optional-by-default so fixtures can assert "at least these values"
299
+ * without over-specifying (e.g. `latencyMs` is nondeterministic).
300
+ */
301
+ export interface ExpectedObservabilityEvent {
302
+ readonly kind: 'wired-tool-invoked' | 'contract-error-emitted' | 'schema-version-mismatch' | 'subscribe-failed' | (string & {});
303
+ readonly toolName?: string;
304
+ readonly actionName?: string;
305
+ readonly code?: string;
306
+ }
307
+ /**
308
+ * One conformance-kit fixture. Authored as JSON under `./fixtures/**`,
309
+ * consumed via the kit's loader, driven by either this package's
310
+ * runner or third-party consumers of the JSON catalog.
311
+ */
312
+ export interface TestCase {
313
+ /**
314
+ * Unique fixture name. MUST match the JSON filename without `.json`.
315
+ * Reporter output uses this as the primary identifier.
316
+ */
317
+ readonly name: string;
318
+ /** Human-readable description of what this fixture proves. */
319
+ readonly description: string;
320
+ /**
321
+ * If non-null, the runner SKIPS this fixture and prints the reason.
322
+ * Used for fixtures authored against a `ConformanceHost` directive
323
+ * the implementation under test hasn't wired yet.
324
+ */
325
+ readonly skipReason: string | null;
326
+ /**
327
+ * Setup directives the host executes BEFORE the input envelope. May
328
+ * be empty. See {@link SetupStep}.
329
+ */
330
+ readonly setup: readonly SetupStep[];
331
+ /**
332
+ * The envelope the runner feeds to the system under test. Opaque
333
+ * shape — the runner passes it verbatim to the host's transport.
334
+ *
335
+ * Typically an object like
336
+ * `{type: 'action', channel: 0, sessionId: 'test-s1', action: {…}}`
337
+ * (channel 0 = wired-action dispatch) or
338
+ * `{type: 'render', sessionId: 'test-s1', resource: {…}}` for
339
+ * bootstrap-path fixtures.
340
+ *
341
+ * `unknown` rather than a typed union because the kit may drive
342
+ * fixtures over multiple transport kinds post-v1.1 (stdio, HTTP
343
+ * long-poll) and each transport's envelope shape is its own.
344
+ */
345
+ readonly inputEnvelope: unknown;
346
+ /** What the runner asserts after the envelope is dispatched. */
347
+ readonly expectedBehavior: ExpectedBehavior;
348
+ /**
349
+ * Optional observability assertion, independent of
350
+ * {@link expectedBehavior}. When `expectedBehavior.kind ===
351
+ * 'contract-error'` and `expectedBehavior.observability` is set,
352
+ * this field SHOULD be omitted — the nested one is canonical.
353
+ */
354
+ readonly expectedObservability?: readonly ExpectedObservabilityEvent[];
355
+ /** Cleanup directives the host executes AFTER the assertion. */
356
+ readonly teardown: readonly TeardownStep[];
357
+ }
358
+ /**
359
+ * Canonical contract-error codes. Mirrors `@ggui-ai/protocol`'s
360
+ * `ContractErrorCode` at the time this kit version was authored. The
361
+ * `(string & {})` tail lets fixtures reference codes introduced by
362
+ * later protocol revisions without a kit bump — the runner's
363
+ * recognition set is its own concern.
364
+ *
365
+ * Drift discipline: the kit ships a meta-test that walks
366
+ * `@ggui-ai/protocol`'s `ContractErrorCode` union against this
367
+ * declaration and warns when a new canonical member appears. The
368
+ * runner's behaviour on unknown codes (skip with warning) is
369
+ * additive-safe: adding a code here is a kit minor; removing one is
370
+ * a major.
371
+ */
372
+ export type ContractErrorCode = 'TOOL_NOT_FOUND' | 'TOOL_THREW' | 'TOOL_TIMEOUT' | 'SCHEMA_VIOLATION' | 'SCHEMA_MISMATCH_ERROR' | 'SESSION_NOT_FOUND' | 'AUTH_REJECTED' | (string & {});
373
+ /**
374
+ * Boot-path failure reasons. Mirrors `@ggui-ai/iframe-runtime`'s
375
+ * `BootstrapFailureReason` — combines parse-time, post-parse
376
+ * orchestration, and pre-WS transport-observable failures. Authored
377
+ * copy for the same drift-decoupling reason as
378
+ * {@link ContractErrorCode}.
379
+ */
380
+ export type BootstrapFailureReason = 'MISSING_TOOL_OUTPUT' | 'MISSING_META_GGUI_BOOTSTRAP' | 'BOOTSTRAP_META_MISSING' | 'MALFORMED_BOOTSTRAP' | 'EXPIRED_BOOTSTRAP' | 'UI_INITIALIZE_FAILED' | 'WS_HANDSHAKE_FAILED' | 'UPGRADE_REQUIRED' | 'BUNDLE_FETCH_FAILED' | 'CSP_VIOLATION' | 'SESSION_NOT_FOUND' | 'AUTH_REJECTED' | (string & {});
381
+ /**
382
+ * Narrow ProtocolError shape mirroring `@ggui-ai/iframe-runtime`'s public
383
+ * union at the time this kit version was authored. Fixture authors
384
+ * do NOT typically reference this type — it's exported so conformance
385
+ * runners can assert on the typed shape the host under test emits.
386
+ * Every member mirrors the renderer's canonical arm; the kit freezes
387
+ * the shape at this kit version and evolves additively per the
388
+ * drift-decoupling posture above.
389
+ */
390
+ export type ProtocolError = {
391
+ readonly kind: 'transport';
392
+ readonly code: 'DISCONNECTED' | 'TIMEOUT';
393
+ readonly retryable: boolean;
394
+ readonly message?: string;
395
+ } | {
396
+ readonly kind: 'auth';
397
+ readonly code: 'SESSION_NOT_FOUND' | 'TOKEN_EXPIRED' | 'AUTH_REJECTED';
398
+ readonly message?: string;
399
+ } | {
400
+ readonly kind: 'protocol';
401
+ readonly code: 'SESSION_MISMATCH' | 'APP_MISMATCH' | 'MALFORMED_ENVELOPE' | (string & {});
402
+ readonly message?: string;
403
+ readonly details?: unknown;
404
+ } | {
405
+ readonly kind: 'contract';
406
+ readonly payload: {
407
+ readonly toolName: string;
408
+ readonly actionName?: string;
409
+ readonly sourceAction?: {
410
+ readonly type: 'wired-action' | 'refresh-stream' | (string & {});
411
+ readonly dispatchedAt: string;
412
+ };
413
+ readonly error: {
414
+ readonly code: ContractErrorCode;
415
+ readonly message: string;
416
+ readonly causedBy?: string;
417
+ };
418
+ readonly timestamp: string;
419
+ readonly schemaVersion?: string;
420
+ };
421
+ } | {
422
+ readonly kind: 'bootstrap';
423
+ readonly reason: BootstrapFailureReason;
424
+ readonly message: string;
425
+ } | {
426
+ readonly kind: 'version';
427
+ readonly serverVersion?: string;
428
+ readonly clientSupports: readonly string[];
429
+ readonly message?: string;
430
+ } | {
431
+ readonly kind: 'unknown';
432
+ readonly raw: unknown;
433
+ };
434
+ //# sourceMappingURL=types.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"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"}
package/dist/types.js ADDED
@@ -0,0 +1,62 @@
1
+ /**
2
+ * `@ggui-ai/protocol-conformance` fixture format — the authoring
3
+ * contract third-party implementers compile against.
4
+ *
5
+ * This module is the public, wire-stable shape of every fixture case
6
+ * under `./fixtures/**`. These declarations are the single source of
7
+ * truth for the fixture-authoring vocabulary; other consumers in the
8
+ * project re-export from this package rather than redefining it.
9
+ *
10
+ * ## Naming-load-bearing rule
11
+ *
12
+ * The shape of {@link TestCase}, {@link SetupStep},
13
+ * {@link TeardownStep}, and {@link ExpectedBehavior} IS the public
14
+ * API. Renaming a field breaks every third-party consumer of the
15
+ * conformance kit. Treat this file the way `@ggui-ai/protocol`'s
16
+ * type declarations get treated: additive changes only, extensibility
17
+ * via `(string & {})` tails.
18
+ *
19
+ * ## Extensibly-closed unions
20
+ *
21
+ * Every discriminator in this module carries a `(string & {})` tail —
22
+ * see SPEC §extensibility and `ContractErrorCode` in
23
+ * `@ggui-ai/protocol`. Third-party conformance-host implementations
24
+ * MAY introduce setup / teardown / expectation kinds the reference
25
+ * kit doesn't recognize; the runner SHOULD skip such cases with a
26
+ * warning rather than failing loudly, exactly the way
27
+ * `ContractErrorCode`'s extensibility works.
28
+ *
29
+ * Only these discriminators are extensibly closed:
30
+ * - `SetupStep['type']`
31
+ * - `TeardownStep['type']`
32
+ * - `ExpectedBehavior['kind']`
33
+ * - `ExpectedObservabilityEvent['kind']`
34
+ * - `TransportConfig['kind']`
35
+ *
36
+ * Everything else — tool names, envelope field names, contract error
37
+ * codes — is passed verbatim and the implementation under test owns
38
+ * the recognition set.
39
+ *
40
+ * ## Why JSON + a loader, not pure TS
41
+ *
42
+ * Third-party consumers of the conformance kit are not all
43
+ * TypeScript. The JSON format means a Python / Go / Rust implementer
44
+ * can parse the same fixture catalog without shipping a TS compiler.
45
+ * The loader exists so in-process TS consumers don't re-invent the
46
+ * parser.
47
+ *
48
+ * ## Why inline authored vocabulary (not `import type` from `@ggui-ai/protocol`)
49
+ *
50
+ * The conformance kit is the AUTHORED artifact — its vocabulary is
51
+ * pinned here, deliberately decoupled from the live source tree. A
52
+ * drift check between these authored unions and the live protocol /
53
+ * renderer types belongs in the kit's own meta-tests, NOT in this
54
+ * file's import graph. If `@ggui-ai/protocol` adds a new
55
+ * `ContractErrorCode` canonical member tomorrow, conformance kit
56
+ * consumers MUST NOT be forced to recompile — their fixtures
57
+ * continue to reference the value they authored, and the runner's
58
+ * extensible `(string & {})` tail handles recognition. This mirrors
59
+ * the protocol's own evolution discipline: authored shapes stay
60
+ * additive.
61
+ */
62
+ export {};
@@ -0,0 +1,42 @@
1
+ import type { WebSocketTransportConfig } from './types.js';
2
+ /**
3
+ * One observed frame. Preserves the raw bytes + the parsed shape so
4
+ * matchers can assert on either. Parse errors surface as `kind:
5
+ * 'unparseable'` — the kit never swallows them.
6
+ */
7
+ export type ObservedFrame = {
8
+ readonly kind: 'frame';
9
+ readonly raw: string;
10
+ readonly parsed: Record<string, unknown>;
11
+ } | {
12
+ readonly kind: 'unparseable';
13
+ readonly raw: string;
14
+ readonly error: string;
15
+ };
16
+ /**
17
+ * Opaque handle returned by {@link openWsTransport}. Closed explicitly
18
+ * via {@link WsTransport.close} — the runner closes after each
19
+ * fixture so sessions don't leak between cases.
20
+ */
21
+ export interface WsTransport {
22
+ /** Send one frame over the wire. Serializes `frame` as JSON. */
23
+ send(frame: unknown): void;
24
+ /**
25
+ * Collect every frame that arrives within `timeoutMs`. Resolves
26
+ * with the buffered frames when the timeout elapses, or earlier if
27
+ * `predicate` is provided and matches a frame.
28
+ */
29
+ observe(options: {
30
+ readonly timeoutMs: number;
31
+ readonly predicate?: (frame: ObservedFrame) => boolean;
32
+ }): Promise<readonly ObservedFrame[]>;
33
+ /** Close the socket. Idempotent. */
34
+ close(): Promise<void>;
35
+ }
36
+ /**
37
+ * Open a WS transport against `config.url`, wait for the socket to
38
+ * reach `OPEN` state, and return the transport handle. Throws if the
39
+ * socket errors or closes before reaching OPEN.
40
+ */
41
+ export declare function openWsTransport(config: WebSocketTransportConfig): Promise<WsTransport>;
42
+ //# sourceMappingURL=ws-transport.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ws-transport.d.ts","sourceRoot":"","sources":["../src/ws-transport.ts"],"names":[],"mappings":"AAwBA,OAAO,KAAK,EAAc,wBAAwB,EAAE,MAAM,YAAY,CAAC;AAEvE;;;;GAIG;AACH,MAAM,MAAM,aAAa,GACrB;IAAE,QAAQ,CAAC,IAAI,EAAE,OAAO,CAAC;IAAC,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAC;IAAC,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAA;CAAE,GAC1F;IAAE,QAAQ,CAAC,IAAI,EAAE,aAAa,CAAC;IAAC,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAC;IAAC,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAA;CAAE,CAAC;AAEnF;;;;GAIG;AACH,MAAM,WAAW,WAAW;IAC1B,gEAAgE;IAChE,IAAI,CAAC,KAAK,EAAE,OAAO,GAAG,IAAI,CAAC;IAC3B;;;;OAIG;IACH,OAAO,CAAC,OAAO,EAAE;QACf,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;QAC3B,QAAQ,CAAC,SAAS,CAAC,EAAE,CAAC,KAAK,EAAE,aAAa,KAAK,OAAO,CAAC;KACxD,GAAG,OAAO,CAAC,SAAS,aAAa,EAAE,CAAC,CAAC;IACtC,oCAAoC;IACpC,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC;CACxB;AAED;;;;GAIG;AACH,wBAAsB,eAAe,CACnC,MAAM,EAAE,wBAAwB,GAC/B,OAAO,CAAC,WAAW,CAAC,CA6FtB"}