@ggui-ai/protocol 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 (222) hide show
  1. package/LICENSE +201 -0
  2. package/README.md +46 -0
  3. package/dist/bridge/invoke-agent.d.ts +65 -0
  4. package/dist/bridge/invoke-agent.d.ts.map +1 -0
  5. package/dist/bridge/invoke-agent.js +113 -0
  6. package/dist/envelope-adapters.d.ts +24 -0
  7. package/dist/envelope-adapters.d.ts.map +1 -0
  8. package/dist/envelope-adapters.js +14 -0
  9. package/dist/envelopes/builders.d.ts +145 -0
  10. package/dist/envelopes/builders.d.ts.map +1 -0
  11. package/dist/envelopes/builders.js +113 -0
  12. package/dist/errors/unknown-permission-name.d.ts +12 -0
  13. package/dist/errors/unknown-permission-name.d.ts.map +1 -0
  14. package/dist/errors/unknown-permission-name.js +29 -0
  15. package/dist/errors/version-mismatch.d.ts +55 -0
  16. package/dist/errors/version-mismatch.d.ts.map +1 -0
  17. package/dist/errors/version-mismatch.js +52 -0
  18. package/dist/gadgets/resolve-contract-gadgets.d.ts +93 -0
  19. package/dist/gadgets/resolve-contract-gadgets.d.ts.map +1 -0
  20. package/dist/gadgets/resolve-contract-gadgets.js +119 -0
  21. package/dist/gadgets/stdlib-gadgets.d.ts +43 -0
  22. package/dist/gadgets/stdlib-gadgets.d.ts.map +1 -0
  23. package/dist/gadgets/stdlib-gadgets.js +161 -0
  24. package/dist/iframe-bridge.d.ts +63 -0
  25. package/dist/iframe-bridge.d.ts.map +1 -0
  26. package/dist/iframe-bridge.js +166 -0
  27. package/dist/index.d.ts +62 -0
  28. package/dist/index.d.ts.map +1 -0
  29. package/dist/index.js +79 -0
  30. package/dist/integrations/mcp-apps.d.ts +1218 -0
  31. package/dist/integrations/mcp-apps.d.ts.map +1 -0
  32. package/dist/integrations/mcp-apps.js +427 -0
  33. package/dist/navigation/index.d.ts +3 -0
  34. package/dist/navigation/index.d.ts.map +1 -0
  35. package/dist/navigation/index.js +1 -0
  36. package/dist/navigation/stack-navigation.d.ts +55 -0
  37. package/dist/navigation/stack-navigation.d.ts.map +1 -0
  38. package/dist/navigation/stack-navigation.js +80 -0
  39. package/dist/recommended-prompts.d.ts +56 -0
  40. package/dist/recommended-prompts.d.ts.map +1 -0
  41. package/dist/recommended-prompts.js +55 -0
  42. package/dist/registry/blueprint-key.d.ts +9 -0
  43. package/dist/registry/blueprint-key.d.ts.map +1 -0
  44. package/dist/registry/blueprint-key.js +28 -0
  45. package/dist/registry/canonicalize-contract.d.ts +35 -0
  46. package/dist/registry/canonicalize-contract.d.ts.map +1 -0
  47. package/dist/registry/canonicalize-contract.js +166 -0
  48. package/dist/registry/summarize-contract.d.ts +46 -0
  49. package/dist/registry/summarize-contract.d.ts.map +1 -0
  50. package/dist/registry/summarize-contract.js +63 -0
  51. package/dist/schema-learning/derive-contract.d.ts +67 -0
  52. package/dist/schema-learning/derive-contract.d.ts.map +1 -0
  53. package/dist/schema-learning/derive-contract.js +117 -0
  54. package/dist/schema-learning/merge.d.ts +32 -0
  55. package/dist/schema-learning/merge.d.ts.map +1 -0
  56. package/dist/schema-learning/merge.js +146 -0
  57. package/dist/schemas/blueprint.d.ts +32 -0
  58. package/dist/schemas/blueprint.d.ts.map +1 -0
  59. package/dist/schemas/blueprint.js +92 -0
  60. package/dist/schemas/data-contract.d.ts +750 -0
  61. package/dist/schemas/data-contract.d.ts.map +1 -0
  62. package/dist/schemas/data-contract.js +663 -0
  63. package/dist/schemas/gadget-name-grammar.d.ts +29 -0
  64. package/dist/schemas/gadget-name-grammar.d.ts.map +1 -0
  65. package/dist/schemas/gadget-name-grammar.js +28 -0
  66. package/dist/schemas/handshake-suggestion.d.ts +46 -0
  67. package/dist/schemas/handshake-suggestion.d.ts.map +1 -0
  68. package/dist/schemas/handshake-suggestion.js +107 -0
  69. package/dist/schemas/invoke.d.ts +337 -0
  70. package/dist/schemas/invoke.d.ts.map +1 -0
  71. package/dist/schemas/invoke.js +169 -0
  72. package/dist/schemas/mcp.d.ts +301 -0
  73. package/dist/schemas/mcp.d.ts.map +1 -0
  74. package/dist/schemas/mcp.js +373 -0
  75. package/dist/schemas/ops-blueprint.d.ts +176 -0
  76. package/dist/schemas/ops-blueprint.d.ts.map +1 -0
  77. package/dist/schemas/ops-blueprint.js +259 -0
  78. package/dist/schemas/sync-check.d.ts +11 -0
  79. package/dist/schemas/sync-check.d.ts.map +1 -0
  80. package/dist/schemas/sync-check.js +60 -0
  81. package/dist/screen-blueprints/define.d.ts +22 -0
  82. package/dist/screen-blueprints/define.d.ts.map +1 -0
  83. package/dist/screen-blueprints/define.js +3 -0
  84. package/dist/screen-blueprints/index.d.ts +4 -0
  85. package/dist/screen-blueprints/index.d.ts.map +1 -0
  86. package/dist/screen-blueprints/index.js +3 -0
  87. package/dist/screen-blueprints/match.d.ts +35 -0
  88. package/dist/screen-blueprints/match.d.ts.map +1 -0
  89. package/dist/screen-blueprints/match.js +51 -0
  90. package/dist/screen-blueprints/types.d.ts +164 -0
  91. package/dist/screen-blueprints/types.d.ts.map +1 -0
  92. package/dist/screen-blueprints/types.js +1 -0
  93. package/dist/stream/stream-parser.d.ts +62 -0
  94. package/dist/stream/stream-parser.d.ts.map +1 -0
  95. package/dist/stream/stream-parser.js +199 -0
  96. package/dist/transport/websocket.d.ts +178 -0
  97. package/dist/transport/websocket.d.ts.map +1 -0
  98. package/dist/transport/websocket.js +1 -0
  99. package/dist/types/app-config.d.ts +61 -0
  100. package/dist/types/app-config.d.ts.map +1 -0
  101. package/dist/types/app-config.js +1 -0
  102. package/dist/types/auth.d.ts +61 -0
  103. package/dist/types/auth.d.ts.map +1 -0
  104. package/dist/types/auth.js +1 -0
  105. package/dist/types/blueprint.d.ts +206 -0
  106. package/dist/types/blueprint.d.ts.map +1 -0
  107. package/dist/types/blueprint.js +1 -0
  108. package/dist/types/canvas-lifecycle.d.ts +105 -0
  109. package/dist/types/canvas-lifecycle.d.ts.map +1 -0
  110. package/dist/types/canvas-lifecycle.js +38 -0
  111. package/dist/types/capabilities.d.ts +40 -0
  112. package/dist/types/capabilities.d.ts.map +1 -0
  113. package/dist/types/capabilities.js +19 -0
  114. package/dist/types/contract-inference.d.ts +401 -0
  115. package/dist/types/contract-inference.d.ts.map +1 -0
  116. package/dist/types/contract-inference.js +44 -0
  117. package/dist/types/credential.d.ts +41 -0
  118. package/dist/types/credential.d.ts.map +1 -0
  119. package/dist/types/credential.js +32 -0
  120. package/dist/types/data-bindings.d.ts +322 -0
  121. package/dist/types/data-bindings.d.ts.map +1 -0
  122. package/dist/types/data-bindings.js +29 -0
  123. package/dist/types/data-contract.d.ts +1296 -0
  124. package/dist/types/data-contract.d.ts.map +1 -0
  125. package/dist/types/data-contract.js +111 -0
  126. package/dist/types/events.d.ts +182 -0
  127. package/dist/types/events.d.ts.map +1 -0
  128. package/dist/types/events.js +8 -0
  129. package/dist/types/feedback.d.ts +24 -0
  130. package/dist/types/feedback.d.ts.map +1 -0
  131. package/dist/types/feedback.js +7 -0
  132. package/dist/types/gadget.d.ts +121 -0
  133. package/dist/types/gadget.d.ts.map +1 -0
  134. package/dist/types/gadget.js +24 -0
  135. package/dist/types/handshake-suggestion.d.ts +264 -0
  136. package/dist/types/handshake-suggestion.d.ts.map +1 -0
  137. package/dist/types/handshake-suggestion.js +70 -0
  138. package/dist/types/host-context.d.ts +163 -0
  139. package/dist/types/host-context.d.ts.map +1 -0
  140. package/dist/types/host-context.js +142 -0
  141. package/dist/types/interface-context.d.ts +105 -0
  142. package/dist/types/interface-context.d.ts.map +1 -0
  143. package/dist/types/interface-context.js +115 -0
  144. package/dist/types/invoke.d.ts +28 -0
  145. package/dist/types/invoke.d.ts.map +1 -0
  146. package/dist/types/invoke.js +7 -0
  147. package/dist/types/live-channel.d.ts +613 -0
  148. package/dist/types/live-channel.d.ts.map +1 -0
  149. package/dist/types/live-channel.js +1 -0
  150. package/dist/types/llm.d.ts +61 -0
  151. package/dist/types/llm.d.ts.map +1 -0
  152. package/dist/types/llm.js +186 -0
  153. package/dist/types/mcp-proxy.d.ts +67 -0
  154. package/dist/types/mcp-proxy.d.ts.map +1 -0
  155. package/dist/types/mcp-proxy.js +46 -0
  156. package/dist/types/mcp.d.ts +637 -0
  157. package/dist/types/mcp.d.ts.map +1 -0
  158. package/dist/types/mcp.js +30 -0
  159. package/dist/types/openrouter-models.d.ts +22 -0
  160. package/dist/types/openrouter-models.d.ts.map +1 -0
  161. package/dist/types/openrouter-models.js +4843 -0
  162. package/dist/types/region.d.ts +26 -0
  163. package/dist/types/region.d.ts.map +1 -0
  164. package/dist/types/region.js +36 -0
  165. package/dist/types/session.d.ts +419 -0
  166. package/dist/types/session.d.ts.map +1 -0
  167. package/dist/types/session.js +1 -0
  168. package/dist/types/thread.d.ts +207 -0
  169. package/dist/types/thread.d.ts.map +1 -0
  170. package/dist/types/thread.js +57 -0
  171. package/dist/types/ui-generator.d.ts +100 -0
  172. package/dist/types/ui-generator.d.ts.map +1 -0
  173. package/dist/types/ui-generator.js +53 -0
  174. package/dist/validation/ajv-runtime.d.ts +140 -0
  175. package/dist/validation/ajv-runtime.d.ts.map +1 -0
  176. package/dist/validation/ajv-runtime.js +452 -0
  177. package/dist/validation/content-hash.d.ts +3 -0
  178. package/dist/validation/content-hash.d.ts.map +1 -0
  179. package/dist/validation/content-hash.js +21 -0
  180. package/dist/validation/contract-validator.d.ts +244 -0
  181. package/dist/validation/contract-validator.d.ts.map +1 -0
  182. package/dist/validation/contract-validator.js +711 -0
  183. package/dist/validation/cross-references.d.ts +105 -0
  184. package/dist/validation/cross-references.d.ts.map +1 -0
  185. package/dist/validation/cross-references.js +164 -0
  186. package/dist/validation/hygiene-rules.d.ts +250 -0
  187. package/dist/validation/hygiene-rules.d.ts.map +1 -0
  188. package/dist/validation/hygiene-rules.js +564 -0
  189. package/dist/validation/lint-contract.d.ts +130 -0
  190. package/dist/validation/lint-contract.d.ts.map +1 -0
  191. package/dist/validation/lint-contract.js +225 -0
  192. package/dist/validation/name-invariants.d.ts +117 -0
  193. package/dist/validation/name-invariants.d.ts.map +1 -0
  194. package/dist/validation/name-invariants.js +172 -0
  195. package/dist/validation/reserved-channels.d.ts +156 -0
  196. package/dist/validation/reserved-channels.d.ts.map +1 -0
  197. package/dist/validation/reserved-channels.js +356 -0
  198. package/dist/validation/resolve-stream-channel.d.ts +78 -0
  199. package/dist/validation/resolve-stream-channel.d.ts.map +1 -0
  200. package/dist/validation/resolve-stream-channel.js +64 -0
  201. package/dist/validation/sanitize-error.d.ts +46 -0
  202. package/dist/validation/sanitize-error.d.ts.map +1 -0
  203. package/dist/validation/sanitize-error.js +88 -0
  204. package/dist/validation/schema-compat-invariants.d.ts +140 -0
  205. package/dist/validation/schema-compat-invariants.d.ts.map +1 -0
  206. package/dist/validation/schema-compat-invariants.js +220 -0
  207. package/dist/validation/schema-meta-validation.d.ts +60 -0
  208. package/dist/validation/schema-meta-validation.d.ts.map +1 -0
  209. package/dist/validation/schema-meta-validation.js +131 -0
  210. package/dist/validation/schema-subset.d.ts +165 -0
  211. package/dist/validation/schema-subset.d.ts.map +1 -0
  212. package/dist/validation/schema-subset.js +295 -0
  213. package/dist/validation/ui-security.d.ts +54 -0
  214. package/dist/validation/ui-security.d.ts.map +1 -0
  215. package/dist/validation/ui-security.js +138 -0
  216. package/dist/validation/zod-to-json-schema.d.ts +63 -0
  217. package/dist/validation/zod-to-json-schema.d.ts.map +1 -0
  218. package/dist/validation/zod-to-json-schema.js +126 -0
  219. package/dist/version.d.ts +1458 -0
  220. package/dist/version.d.ts.map +1 -0
  221. package/dist/version.js +1459 -0
  222. package/package.json +113 -0
@@ -0,0 +1,46 @@
1
+ /**
2
+ * Patterns stripped from stringified errors before emission on
3
+ * `_ggui:contract-error`. The default set covers the common credential
4
+ * shapes that show up in stack traces by accident (URLs with token query
5
+ * params, Bearer headers captured by retry libraries, env-var dumps).
6
+ *
7
+ * Each regex replaces the matched span with `[REDACTED]`. Replacement is
8
+ * conservative — we'd rather over-redact than leak. Operators who need
9
+ * finer control inject a custom sanitizer via
10
+ * `createSessionChannelServer({ sanitizeCausedBy })`.
11
+ */
12
+ export declare const DEFAULT_CREDENTIAL_PATTERNS: readonly RegExp[];
13
+ /** Default max length for a sanitized `causedBy` string. */
14
+ export declare const DEFAULT_CAUSED_BY_MAX_LENGTH = 2048;
15
+ /** Truncation marker appended when `causedBy` exceeds the max length. */
16
+ export declare const TRUNCATION_MARKER = "\n\u2026[truncated]";
17
+ /**
18
+ * Sanitize a stringified error (typically `err.stack`) before it's
19
+ * written to `ContractErrorPayload.error.causedBy`.
20
+ *
21
+ * Applies every pattern in {@link DEFAULT_CREDENTIAL_PATTERNS} in order,
22
+ * replacing matches with `[REDACTED]`. If the result exceeds `maxLength`,
23
+ * the tail is replaced with {@link TRUNCATION_MARKER}.
24
+ *
25
+ * The function is pure and deterministic — the same `raw` produces the
26
+ * same output across runs. Safe to call in hot paths; patterns are
27
+ * pre-compiled module-level regexes.
28
+ *
29
+ * @param raw - The stringified error (usually `err.stack`).
30
+ * @param maxLength - Maximum length of returned string. Default 2048.
31
+ * @param patterns - Override the default credential-pattern set. Useful
32
+ * for tests or operators who want stricter behavior. Passing an empty
33
+ * array disables pattern replacement (truncation still applies).
34
+ */
35
+ export declare function sanitizeCausedBy(raw: string, maxLength?: number, patterns?: readonly RegExp[]): string;
36
+ /**
37
+ * Signature of the sanitizer hook operators can inject into
38
+ * `createSessionChannelServer`. Receives the raw stringified error
39
+ * (typically `err.stack`) and MUST return a safe-to-emit string.
40
+ *
41
+ * Defaults to {@link sanitizeCausedBy} when no override is supplied. A
42
+ * pass-through function that returns `raw` unchanged is valid but
43
+ * discouraged — it re-enables the leak this module was added to close.
44
+ */
45
+ export type SanitizeCausedBy = (raw: string) => string;
46
+ //# sourceMappingURL=sanitize-error.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"sanitize-error.d.ts","sourceRoot":"","sources":["../../src/validation/sanitize-error.ts"],"names":[],"mappings":"AA0BA;;;;;;;;;;GAUG;AACH,eAAO,MAAM,2BAA2B,EAAE,SAAS,MAAM,EAWxD,CAAC;AAEF,4DAA4D;AAC5D,eAAO,MAAM,4BAA4B,OAAO,CAAC;AAEjD,yEAAyE;AACzE,eAAO,MAAM,iBAAiB,wBAAmB,CAAC;AAElD;;;;;;;;;;;;;;;;;GAiBG;AACH,wBAAgB,gBAAgB,CAC9B,GAAG,EAAE,MAAM,EACX,SAAS,GAAE,MAAqC,EAChD,QAAQ,GAAE,SAAS,MAAM,EAAgC,GACxD,MAAM,CAgBR;AAED;;;;;;;;GAQG;AACH,MAAM,MAAM,gBAAgB,GAAG,CAAC,GAAG,EAAE,MAAM,KAAK,MAAM,CAAC"}
@@ -0,0 +1,88 @@
1
+ // packages/protocol/src/validation/sanitize-error.ts
2
+ //
3
+ // Credential-leak sanitizer for `ContractErrorPayload.error.causedBy`.
4
+ //
5
+ // The `causedBy` field on a contract-error envelope carries the
6
+ // stringified original error — typically `Error.stack`. Stacks frequently
7
+ // contain URLs with query-param tokens (`?token=...`, `?api_key=...`),
8
+ // Authorization header values captured by over-eager logging libraries,
9
+ // and env-var dumps. The envelope flows on the reserved
10
+ // `_ggui:contract-error` channel with `replay: 'all'`, so anything that
11
+ // lands in `causedBy` persists in the session ring buffer and is visible
12
+ // in operator tools (SessionInspector activity panels). If an operator
13
+ // shares a bug report with that data, the credential leaks externally.
14
+ //
15
+ // This module's default posture is "sanitize before emission": the
16
+ // session-channel router pipes every `err.stack` through
17
+ // {@link sanitizeCausedBy} before populating `causedBy`. Operators who
18
+ // need stricter sanitization can inject their own function via
19
+ // `createSessionChannelServer({ sanitizeCausedBy })`.
20
+ //
21
+ // Scope: this is a DEFENSE-IN-DEPTH belt, not the sole line. Libraries
22
+ // that handle secrets should already avoid embedding them in error
23
+ // messages. This sanitizer exists because stack traces are produced
24
+ // opportunistically by code that didn't think about leaking and we'd
25
+ // rather redact conservatively than ship raw stacks.
26
+ /**
27
+ * Patterns stripped from stringified errors before emission on
28
+ * `_ggui:contract-error`. The default set covers the common credential
29
+ * shapes that show up in stack traces by accident (URLs with token query
30
+ * params, Bearer headers captured by retry libraries, env-var dumps).
31
+ *
32
+ * Each regex replaces the matched span with `[REDACTED]`. Replacement is
33
+ * conservative — we'd rather over-redact than leak. Operators who need
34
+ * finer control inject a custom sanitizer via
35
+ * `createSessionChannelServer({ sanitizeCausedBy })`.
36
+ */
37
+ export const DEFAULT_CREDENTIAL_PATTERNS = [
38
+ // Bearer / Basic header values. `Bearer <token>` / `Authorization: Basic <base64>`.
39
+ /Bearer\s+[A-Za-z0-9._\-~+/]+=*/gi,
40
+ /Basic\s+[A-Za-z0-9+/]+=*/gi,
41
+ /Authorization:\s*\S+/gi,
42
+ // Query-param-style secrets — matches `?token=...` / `&api_key=...` etc.
43
+ // Up to the next `&`, space, quote, or end-of-string.
44
+ /([?&](?:token|api[_-]?key|access[_-]?token|refresh[_-]?token|password|secret|session[_-]?id)=)[^&\s"']+/gi,
45
+ // Env-var-style `KEY=value` where KEY is obviously secret-ish and value
46
+ // is non-empty. Bounds the value to the next whitespace/quote.
47
+ /\b(AWS_[A-Z_]+|ANTHROPIC_API_KEY|OPENAI_API_KEY|GOOGLE_API_KEY|OPENROUTER_API_KEY|COGNITO_[A-Z_]+|DATABASE_URL|DB_PASSWORD|REDIS_URL)=\S+/gi,
48
+ ];
49
+ /** Default max length for a sanitized `causedBy` string. */
50
+ export const DEFAULT_CAUSED_BY_MAX_LENGTH = 2048;
51
+ /** Truncation marker appended when `causedBy` exceeds the max length. */
52
+ export const TRUNCATION_MARKER = '\n…[truncated]';
53
+ /**
54
+ * Sanitize a stringified error (typically `err.stack`) before it's
55
+ * written to `ContractErrorPayload.error.causedBy`.
56
+ *
57
+ * Applies every pattern in {@link DEFAULT_CREDENTIAL_PATTERNS} in order,
58
+ * replacing matches with `[REDACTED]`. If the result exceeds `maxLength`,
59
+ * the tail is replaced with {@link TRUNCATION_MARKER}.
60
+ *
61
+ * The function is pure and deterministic — the same `raw` produces the
62
+ * same output across runs. Safe to call in hot paths; patterns are
63
+ * pre-compiled module-level regexes.
64
+ *
65
+ * @param raw - The stringified error (usually `err.stack`).
66
+ * @param maxLength - Maximum length of returned string. Default 2048.
67
+ * @param patterns - Override the default credential-pattern set. Useful
68
+ * for tests or operators who want stricter behavior. Passing an empty
69
+ * array disables pattern replacement (truncation still applies).
70
+ */
71
+ export function sanitizeCausedBy(raw, maxLength = DEFAULT_CAUSED_BY_MAX_LENGTH, patterns = DEFAULT_CREDENTIAL_PATTERNS) {
72
+ let out = raw;
73
+ for (const p of patterns) {
74
+ // Special-case the query-param pattern: we want to keep the key name
75
+ // so callers can still see WHERE the secret was, just not what it
76
+ // was. Every other pattern is replaced whole.
77
+ if (p.source.includes('token|api')) {
78
+ out = out.replace(p, '$1[REDACTED]');
79
+ }
80
+ else {
81
+ out = out.replace(p, '[REDACTED]');
82
+ }
83
+ }
84
+ if (out.length > maxLength) {
85
+ out = out.slice(0, maxLength) + TRUNCATION_MARKER;
86
+ }
87
+ return out;
88
+ }
@@ -0,0 +1,140 @@
1
+ /**
2
+ * Protocol-level schema-compatibility invariants for `DataContract`.
3
+ *
4
+ * Ships one stable error code:
5
+ *
6
+ * - `CTR_SCHEMA_INCOMPAT` — an `actionSpec[*].schema` is not a
7
+ * subset of the referenced `agentCapabilities.tools[*].inputSchema`,
8
+ * OR a `streamSpec[*].schema` is not a superset of the
9
+ * referenced `agentCapabilities.tools[*].outputSchema`. Direction is
10
+ * fixed by the data flow: action payloads travel UI → tool, so
11
+ * the action's accepted values must fit the tool's accepted
12
+ * inputs; stream payloads travel tool → channel, so the
13
+ * channel's accepted values must cover the tool's possible
14
+ * outputs.
15
+ *
16
+ * **Scope vs server-level F4.** This invariant runs PURELY against
17
+ * the contract's own catalog — `actionSpec[*].schema` /
18
+ * `streamSpec[*].schema` vs `agentCapabilities.tools[*].inputSchema` /
19
+ * `outputSchema`, all of which are author-declared JSON Schemas
20
+ * already on the contract. No tool registry, no zod conversion, no
21
+ * server state. The server-level F4 check (`checkStackItemSchemaCompat`
22
+ * in `@ggui-ai/mcp-server`) compares the same `actionSpec` /
23
+ * `streamSpec` schemas against the SERVER-REGISTERED tools' actual
24
+ * zod schemas; it covers the operator-side "did the deployed tool
25
+ * change schema since the contract was authored?" failure mode. Both
26
+ * checks compose:
27
+ *
28
+ * - Protocol-level CTR_SCHEMA_INCOMPAT: author-visible bug.
29
+ * "Your contract's action.schema doesn't fit the inputSchema
30
+ * you yourself declared on this tool entry."
31
+ * - Server-level SchemaCompatError: operator-visible bug. "The
32
+ * deployed tool's actual inputSchema doesn't match what the
33
+ * contract declares."
34
+ *
35
+ * Skipped silently when the referenced agentTool has no declared
36
+ * `inputSchema`/`outputSchema` (the catalog entry is incomplete; the
37
+ * check has no anchor and degrades to "no opinion"). Skipped when
38
+ * the action/channel has no `schema` (void-payload entries — nothing
39
+ * to compare).
40
+ *
41
+ * Companion to `cross-references` and `name-invariants` — together
42
+ * they ship companion rule registries to cross-references and
43
+ * name-invariants under the unified `lintContract` API.
44
+ */
45
+ import type { AgentCapabilitiesSpec, DataContract } from '../types/data-contract';
46
+ import type { ContractViolation } from './contract-validator';
47
+ /**
48
+ * Stable error code for protocol-level schema-compatibility
49
+ * violations on action ⊆ inputSchema or channel ⊇ outputSchema.
50
+ */
51
+ export declare const CTR_SCHEMA_INCOMPAT = "CTR_SCHEMA_INCOMPAT";
52
+ /**
53
+ * Discriminator on the side of the contract the violation came
54
+ * from. Surfaced on the violation so consumers can render the
55
+ * action vs. stream cases differently without parsing the field
56
+ * path.
57
+ */
58
+ export type SchemaCompatSide = 'action' | 'stream';
59
+ /**
60
+ * Schema-compat invariant violation. Carries the stable error code
61
+ * plus side / specName / toolName so consumers can pivot rendering
62
+ * on the action vs stream case without parsing the field path.
63
+ *
64
+ * The granular `isSchemaSubset` violation list is NOT carried on the
65
+ * violation (the rich `SubsetViolation` shape doesn't fit the
66
+ * `ContractViolation extends JsonObject` constraint). The message
67
+ * includes the first mismatch's reason + path; callers needing the
68
+ * full list can re-run {@link checkSchemaCompat} subcomponents
69
+ * directly.
70
+ */
71
+ export interface SchemaCompatViolation extends ContractViolation {
72
+ code: typeof CTR_SCHEMA_INCOMPAT;
73
+ /** Which side of the contract was checked. */
74
+ side: SchemaCompatSide;
75
+ /** The action / channel name on the contract. */
76
+ specName: string;
77
+ /** The agentCapabilities.tools key resolved to perform the check. */
78
+ toolName: string;
79
+ }
80
+ /**
81
+ * Validate every `actionSpec[*].schema` is a subset of the
82
+ * referenced `agentCapabilities.tools[nextStep].inputSchema`.
83
+ *
84
+ * Skips entries where:
85
+ * - no `nextStep` is declared (pure event signal — the agent owns
86
+ * dispatch; nothing to compare),
87
+ * - the referenced agentTool has no declared `inputSchema` (the
88
+ * catalog entry is incomplete; the check has no anchor),
89
+ * - the referenced agentTool is missing entirely (a separate
90
+ * invariant `CTR_REF_NEXT_STEP` covers that).
91
+ *
92
+ * When the action has no `schema`, the wire shape is modeled as
93
+ * `{type: 'object', properties: {}, additionalProperties: false}` —
94
+ * "void payload." This matches the F4 convention so the protocol-
95
+ * level check stays compatible with the server-level posture.
96
+ */
97
+ export declare function checkActionSchemaCompat(actionSpec: DataContract['actionSpec'] | undefined, agentCapabilities: AgentCapabilitiesSpec | undefined): SchemaCompatViolation[];
98
+ /**
99
+ * Validate every `streamSpec[*].schema` is a SUPERSET of the
100
+ * referenced `agentCapabilities.tools[source.tool].outputSchema`. Direction
101
+ * inverts: streams travel tool → channel, so the channel schema
102
+ * must accept everything the tool can return.
103
+ *
104
+ * Skips entries where:
105
+ * - no `source` is declared,
106
+ * - the referenced agentTool has no declared `outputSchema`,
107
+ * - the referenced agentTool is missing entirely
108
+ * (`CTR_REF_STREAM_SOURCE` covers that),
109
+ * - the channel has no `schema` (declarative validation is
110
+ * impossible without the channel's accepted shape).
111
+ */
112
+ export declare function checkStreamSchemaCompat(streamSpec: DataContract['streamSpec'] | undefined, agentCapabilities: AgentCapabilitiesSpec | undefined): SchemaCompatViolation[];
113
+ /**
114
+ * Run every protocol-level schema-compat invariant. Aggregates action
115
+ * + stream violations; order is stable (action checks first).
116
+ */
117
+ export declare function checkSchemaCompat(contract: DataContract): SchemaCompatViolation[];
118
+ /**
119
+ * Throwable form of {@link checkSchemaCompat}. Use at protocol
120
+ * boundaries where a schema-compat violation is a contract bug the
121
+ * author must fix.
122
+ */
123
+ export declare class SchemaCompatInvariantError extends Error {
124
+ readonly code: "schema_compat_incompat";
125
+ readonly violations: readonly SchemaCompatViolation[];
126
+ constructor(violations: readonly SchemaCompatViolation[]);
127
+ }
128
+ /**
129
+ * Throw-on-violation wrapper around {@link checkSchemaCompat}.
130
+ * No-op when the contract's schemas align with its own
131
+ * agentCapabilities catalog.
132
+ *
133
+ * Slots alongside `assertCrossReferences` + `assertNameInvariants`
134
+ * at push time. Different scope from the server-level
135
+ * `SchemaCompatError` thrown by `checkStackItemSchemaCompat` in
136
+ * `@ggui-ai/mcp-server`: this check uses ONLY the contract's own
137
+ * catalog; the server-level check uses the runtime tool registry.
138
+ */
139
+ export declare function assertSchemaCompat(contract: DataContract): void;
140
+ //# sourceMappingURL=schema-compat-invariants.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"schema-compat-invariants.d.ts","sourceRoot":"","sources":["../../src/validation/schema-compat-invariants.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA2CG;AAEH,OAAO,KAAK,EAGV,qBAAqB,EACrB,YAAY,EAGb,MAAM,wBAAwB,CAAC;AAChC,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,sBAAsB,CAAC;AAQ9D;;;GAGG;AACH,eAAO,MAAM,mBAAmB,wBAAwB,CAAC;AAEzD;;;;;GAKG;AACH,MAAM,MAAM,gBAAgB,GAAG,QAAQ,GAAG,QAAQ,CAAC;AAEnD;;;;;;;;;;;GAWG;AACH,MAAM,WAAW,qBAAsB,SAAQ,iBAAiB;IAC9D,IAAI,EAAE,OAAO,mBAAmB,CAAC;IACjC,8CAA8C;IAC9C,IAAI,EAAE,gBAAgB,CAAC;IACvB,iDAAiD;IACjD,QAAQ,EAAE,MAAM,CAAC;IACjB,qEAAqE;IACrE,QAAQ,EAAE,MAAM,CAAC;CAClB;AASD;;;;;;;;;;;;;;;;GAgBG;AACH,wBAAgB,uBAAuB,CACrC,UAAU,EAAE,YAAY,CAAC,YAAY,CAAC,GAAG,SAAS,EAClD,iBAAiB,EAAE,qBAAqB,GAAG,SAAS,GACnD,qBAAqB,EAAE,CAqCzB;AAED;;;;;;;;;;;;;GAaG;AACH,wBAAgB,uBAAuB,CACrC,UAAU,EAAE,YAAY,CAAC,YAAY,CAAC,GAAG,SAAS,EAClD,iBAAiB,EAAE,qBAAqB,GAAG,SAAS,GACnD,qBAAqB,EAAE,CAuCzB;AAQD;;;GAGG;AACH,wBAAgB,iBAAiB,CAC/B,QAAQ,EAAE,YAAY,GACrB,qBAAqB,EAAE,CAKzB;AAED;;;;GAIG;AACH,qBAAa,0BAA2B,SAAQ,KAAK;IACnD,QAAQ,CAAC,IAAI,EAAG,wBAAwB,CAAU;IAClD,QAAQ,CAAC,UAAU,EAAE,SAAS,qBAAqB,EAAE,CAAC;gBAE1C,UAAU,EAAE,SAAS,qBAAqB,EAAE;CAQzD;AAED;;;;;;;;;;GAUG;AACH,wBAAgB,kBAAkB,CAAC,QAAQ,EAAE,YAAY,GAAG,IAAI,CAK/D"}
@@ -0,0 +1,220 @@
1
+ /**
2
+ * Protocol-level schema-compatibility invariants for `DataContract`.
3
+ *
4
+ * Ships one stable error code:
5
+ *
6
+ * - `CTR_SCHEMA_INCOMPAT` — an `actionSpec[*].schema` is not a
7
+ * subset of the referenced `agentCapabilities.tools[*].inputSchema`,
8
+ * OR a `streamSpec[*].schema` is not a superset of the
9
+ * referenced `agentCapabilities.tools[*].outputSchema`. Direction is
10
+ * fixed by the data flow: action payloads travel UI → tool, so
11
+ * the action's accepted values must fit the tool's accepted
12
+ * inputs; stream payloads travel tool → channel, so the
13
+ * channel's accepted values must cover the tool's possible
14
+ * outputs.
15
+ *
16
+ * **Scope vs server-level F4.** This invariant runs PURELY against
17
+ * the contract's own catalog — `actionSpec[*].schema` /
18
+ * `streamSpec[*].schema` vs `agentCapabilities.tools[*].inputSchema` /
19
+ * `outputSchema`, all of which are author-declared JSON Schemas
20
+ * already on the contract. No tool registry, no zod conversion, no
21
+ * server state. The server-level F4 check (`checkStackItemSchemaCompat`
22
+ * in `@ggui-ai/mcp-server`) compares the same `actionSpec` /
23
+ * `streamSpec` schemas against the SERVER-REGISTERED tools' actual
24
+ * zod schemas; it covers the operator-side "did the deployed tool
25
+ * change schema since the contract was authored?" failure mode. Both
26
+ * checks compose:
27
+ *
28
+ * - Protocol-level CTR_SCHEMA_INCOMPAT: author-visible bug.
29
+ * "Your contract's action.schema doesn't fit the inputSchema
30
+ * you yourself declared on this tool entry."
31
+ * - Server-level SchemaCompatError: operator-visible bug. "The
32
+ * deployed tool's actual inputSchema doesn't match what the
33
+ * contract declares."
34
+ *
35
+ * Skipped silently when the referenced agentTool has no declared
36
+ * `inputSchema`/`outputSchema` (the catalog entry is incomplete; the
37
+ * check has no anchor and degrades to "no opinion"). Skipped when
38
+ * the action/channel has no `schema` (void-payload entries — nothing
39
+ * to compare).
40
+ *
41
+ * Companion to `cross-references` and `name-invariants` — together
42
+ * they ship companion rule registries to cross-references and
43
+ * name-invariants under the unified `lintContract` API.
44
+ */
45
+ import { isSchemaSubset } from './schema-subset.js';
46
+ // `SubsetViolation` is used as the parameter type for the local
47
+ // `describe` helper below; it is intentionally NOT carried on the
48
+ // public `SchemaCompatViolation` (the rich shape doesn't fit
49
+ // `ContractViolation extends JsonObject`).
50
+ /**
51
+ * Stable error code for protocol-level schema-compatibility
52
+ * violations on action ⊆ inputSchema or channel ⊇ outputSchema.
53
+ */
54
+ export const CTR_SCHEMA_INCOMPAT = 'CTR_SCHEMA_INCOMPAT';
55
+ function buildToolMap(agentCapabilities) {
56
+ if (!agentCapabilities)
57
+ return new Map();
58
+ return new Map(Object.entries(agentCapabilities.tools));
59
+ }
60
+ /**
61
+ * Validate every `actionSpec[*].schema` is a subset of the
62
+ * referenced `agentCapabilities.tools[nextStep].inputSchema`.
63
+ *
64
+ * Skips entries where:
65
+ * - no `nextStep` is declared (pure event signal — the agent owns
66
+ * dispatch; nothing to compare),
67
+ * - the referenced agentTool has no declared `inputSchema` (the
68
+ * catalog entry is incomplete; the check has no anchor),
69
+ * - the referenced agentTool is missing entirely (a separate
70
+ * invariant `CTR_REF_NEXT_STEP` covers that).
71
+ *
72
+ * When the action has no `schema`, the wire shape is modeled as
73
+ * `{type: 'object', properties: {}, additionalProperties: false}` —
74
+ * "void payload." This matches the F4 convention so the protocol-
75
+ * level check stays compatible with the server-level posture.
76
+ */
77
+ export function checkActionSchemaCompat(actionSpec, agentCapabilities) {
78
+ if (!actionSpec)
79
+ return [];
80
+ const tools = buildToolMap(agentCapabilities);
81
+ const violations = [];
82
+ for (const [actionName, entry] of Object.entries(actionSpec)) {
83
+ if (!entry || typeof entry !== 'object')
84
+ continue;
85
+ const toolName = entry.nextStep;
86
+ if (typeof toolName !== 'string' || toolName.length === 0)
87
+ continue;
88
+ const tool = tools.get(toolName);
89
+ if (!tool)
90
+ continue; // CTR_REF_NEXT_STEP covers this
91
+ const toolInput = tool.inputSchema;
92
+ if (!toolInput)
93
+ continue; // catalog entry incomplete — no anchor
94
+ const actionSchema = entry.schema ?? {
95
+ type: 'object',
96
+ properties: {},
97
+ additionalProperties: false,
98
+ };
99
+ const result = isSchemaSubset(toolInput, actionSchema);
100
+ if (result.compatible)
101
+ continue;
102
+ violations.push({
103
+ code: CTR_SCHEMA_INCOMPAT,
104
+ side: 'action',
105
+ specName: actionName,
106
+ toolName,
107
+ field: `actionSpec.${actionName}.schema`,
108
+ message: `actionSpec.${actionName}.schema is not a subset of agentCapabilities.tools.${toolName}.inputSchema — values the action accepts would be rejected by the tool. ${describe(result.violations)}`,
109
+ expected: `subset of agentCapabilities.tools.${toolName}.inputSchema`,
110
+ received: 'incompatible',
111
+ });
112
+ }
113
+ return violations;
114
+ }
115
+ /**
116
+ * Validate every `streamSpec[*].schema` is a SUPERSET of the
117
+ * referenced `agentCapabilities.tools[source.tool].outputSchema`. Direction
118
+ * inverts: streams travel tool → channel, so the channel schema
119
+ * must accept everything the tool can return.
120
+ *
121
+ * Skips entries where:
122
+ * - no `source` is declared,
123
+ * - the referenced agentTool has no declared `outputSchema`,
124
+ * - the referenced agentTool is missing entirely
125
+ * (`CTR_REF_STREAM_SOURCE` covers that),
126
+ * - the channel has no `schema` (declarative validation is
127
+ * impossible without the channel's accepted shape).
128
+ */
129
+ export function checkStreamSchemaCompat(streamSpec, agentCapabilities) {
130
+ if (!streamSpec)
131
+ return [];
132
+ const tools = buildToolMap(agentCapabilities);
133
+ const violations = [];
134
+ for (const [channelName, entry] of Object.entries(streamSpec)) {
135
+ if (!entry || typeof entry !== 'object')
136
+ continue;
137
+ const source = entry.source;
138
+ if (!source)
139
+ continue;
140
+ const toolName = source.tool;
141
+ if (typeof toolName !== 'string' || toolName.length === 0)
142
+ continue;
143
+ const tool = tools.get(toolName);
144
+ if (!tool)
145
+ continue; // CTR_REF_STREAM_SOURCE covers this
146
+ const toolOutput = tool.outputSchema;
147
+ if (!toolOutput)
148
+ continue;
149
+ const channelSchema = entry.schema;
150
+ if (!channelSchema)
151
+ continue;
152
+ // Direction: every tool-returned value must be accepted by the
153
+ // channel schema. Channel is the superset / permissive side,
154
+ // tool-return is the restricted side.
155
+ const result = isSchemaSubset(channelSchema, toolOutput);
156
+ if (result.compatible)
157
+ continue;
158
+ violations.push({
159
+ code: CTR_SCHEMA_INCOMPAT,
160
+ side: 'stream',
161
+ specName: channelName,
162
+ toolName,
163
+ field: `streamSpec.${channelName}.schema`,
164
+ message: `streamSpec.${channelName}.schema does not accept all values agentCapabilities.tools.${toolName}.outputSchema produces — some tool outputs would fail channel validation. ${describe(result.violations)}`,
165
+ expected: `superset of agentCapabilities.tools.${toolName}.outputSchema`,
166
+ received: 'incompatible',
167
+ });
168
+ }
169
+ return violations;
170
+ }
171
+ function describe(violations) {
172
+ if (violations.length === 0)
173
+ return '';
174
+ const first = violations[0];
175
+ return `First mismatch: ${first.reason} at ${first.path || '<root>'}.`;
176
+ }
177
+ /**
178
+ * Run every protocol-level schema-compat invariant. Aggregates action
179
+ * + stream violations; order is stable (action checks first).
180
+ */
181
+ export function checkSchemaCompat(contract) {
182
+ return [
183
+ ...checkActionSchemaCompat(contract.actionSpec, contract.agentCapabilities),
184
+ ...checkStreamSchemaCompat(contract.streamSpec, contract.agentCapabilities),
185
+ ];
186
+ }
187
+ /**
188
+ * Throwable form of {@link checkSchemaCompat}. Use at protocol
189
+ * boundaries where a schema-compat violation is a contract bug the
190
+ * author must fix.
191
+ */
192
+ export class SchemaCompatInvariantError extends Error {
193
+ code = 'schema_compat_incompat';
194
+ violations;
195
+ constructor(violations) {
196
+ const summary = violations
197
+ .map((v) => `[${v.code}] ${v.message}`)
198
+ .join(' | ');
199
+ super(`Contract schema-compat invariants failed: ${summary}`);
200
+ this.name = 'SchemaCompatInvariantError';
201
+ this.violations = violations;
202
+ }
203
+ }
204
+ /**
205
+ * Throw-on-violation wrapper around {@link checkSchemaCompat}.
206
+ * No-op when the contract's schemas align with its own
207
+ * agentCapabilities catalog.
208
+ *
209
+ * Slots alongside `assertCrossReferences` + `assertNameInvariants`
210
+ * at push time. Different scope from the server-level
211
+ * `SchemaCompatError` thrown by `checkStackItemSchemaCompat` in
212
+ * `@ggui-ai/mcp-server`: this check uses ONLY the contract's own
213
+ * catalog; the server-level check uses the runtime tool registry.
214
+ */
215
+ export function assertSchemaCompat(contract) {
216
+ const violations = checkSchemaCompat(contract);
217
+ if (violations.length > 0) {
218
+ throw new SchemaCompatInvariantError(violations);
219
+ }
220
+ }
@@ -0,0 +1,60 @@
1
+ /**
2
+ * Layer B (inner JSON Schema meta-validation) for a `DataContract`.
3
+ *
4
+ * Walks the six inner JSON Schema fields the agent authors:
5
+ * 1. `propsSpec.properties[*].schema`
6
+ * 2. `actionSpec[*].schema` (optional per entry)
7
+ * 3. `streamSpec[*].schema`
8
+ * 4. `contextSpec[*].schema`
9
+ * 5. `agentCapabilities.tools[*].inputSchema` (optional per entry)
10
+ * 6. `agentCapabilities.tools[*].outputSchema` (optional per entry)
11
+ *
12
+ * For each present schema, runs `compileForValidation()` from
13
+ * `ajv-runtime`. Ajv's `strict: true` mode throws on malformed JSON
14
+ * Schemas at compile-time — unknown keywords, missing `items` on
15
+ * array nodes, properties values that are not schemas, etc. This is
16
+ * the meta-validation pass: it asserts the contract's own type
17
+ * descriptions are well-formed BEFORE any runtime data flows.
18
+ *
19
+ * Failures collect into a single throw with every malformed field
20
+ * named — agents fix all of them in one round rather than retry-
21
+ * per-field.
22
+ *
23
+ * Designed to slot at:
24
+ * - `ggui_handshake` entry — validates `blueprintDraft.contract`
25
+ * before the negotiator runs.
26
+ * - `ggui_push` entry — validates the `effectiveContract` (either
27
+ * synth-amended or override) before any state mutation.
28
+ *
29
+ * Both call sites already run cross-reference and name-invariant
30
+ * assertions for the same author-recoverable failure class; this
31
+ * sits alongside them.
32
+ */
33
+ import type { DataContract } from '../types/data-contract';
34
+ export interface SchemaMetaViolation {
35
+ /** Path to the malformed schema field (e.g., `propsSpec.properties.todos.schema`). */
36
+ field: string;
37
+ /** Ajv's compile-error message (truncated to fit the wire). */
38
+ message: string;
39
+ }
40
+ /**
41
+ * Typed error for contract schema meta-validation failures. Mirrors
42
+ * the shape of `CrossReferenceError` / `ContractViolationError` for
43
+ * symmetry — push/handshake catch and surface a structured
44
+ * `contract_schema_invalid` error to the agent.
45
+ */
46
+ export declare class ContractSchemaMetaError extends Error {
47
+ readonly code: "contract_schema_invalid";
48
+ readonly violations: readonly SchemaMetaViolation[];
49
+ readonly hint: string;
50
+ constructor(violations: readonly SchemaMetaViolation[]);
51
+ }
52
+ /**
53
+ * Walks the contract's six inner JSON Schema fields and asserts
54
+ * each compiles cleanly under Ajv's strict mode. Throws
55
+ * {@link ContractSchemaMetaError} on the first push attempt that
56
+ * smuggles a malformed schema; collects every offender in one pass
57
+ * before throwing so the agent sees the full list.
58
+ */
59
+ export declare function assertContractSchemasValid(contract: DataContract): void;
60
+ //# sourceMappingURL=schema-meta-validation.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"schema-meta-validation.d.ts","sourceRoot":"","sources":["../../src/validation/schema-meta-validation.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA+BG;AAEH,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,wBAAwB,CAAC;AAG3D,MAAM,WAAW,mBAAmB;IAClC,sFAAsF;IACtF,KAAK,EAAE,MAAM,CAAC;IACd,+DAA+D;IAC/D,OAAO,EAAE,MAAM,CAAC;CACjB;AAED;;;;;GAKG;AACH,qBAAa,uBAAwB,SAAQ,KAAK;IAChD,QAAQ,CAAC,IAAI,EAAG,yBAAyB,CAAU;IACnD,QAAQ,CAAC,UAAU,EAAE,SAAS,mBAAmB,EAAE,CAAC;IACpD,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;gBAEV,UAAU,EAAE,SAAS,mBAAmB,EAAE;CAWvD;AAmCD;;;;;;GAMG;AACH,wBAAgB,0BAA0B,CAAC,QAAQ,EAAE,YAAY,GAAG,IAAI,CAuDvE"}