@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,55 @@
1
+ /**
2
+ * Recommended agent-side system prompts for hosts running ggui.
3
+ *
4
+ * The wire protocol already carries its own self-teaching surfaces:
5
+ *
6
+ * - Per-tool `description` strings on every `ggui_*` MCP tool.
7
+ * - The server's `InitializeResult.instructions` field (set via
8
+ * `@ggui-ai/mcp-server`'s `MCP_INSTRUCTIONS_PRESETS`).
9
+ *
10
+ * Those two carry the wire flow (new_session → handshake → push →
11
+ * consume → react), the contract-authoring rules, recovery shapes,
12
+ * and the mutation rule. **Agent builders should NOT replicate any
13
+ * of that in their system prompt.** The protocol is designed to be
14
+ * self-teaching from the host's side.
15
+ *
16
+ * What an agent-side system prompt SHOULD do: set the agent's role
17
+ * and posture — "you render UIs, you don't reply in plain text" —
18
+ * so the model reaches for ggui_* tools instead of conversational
19
+ * responses. That's a *posture cue*, not a procedural script.
20
+ *
21
+ * On hosts like claude.ai, the host's own baseline system prompt
22
+ * already nudges tool usage, so a one-line user instruction
23
+ * ("Always respond using ggui_* tools") is enough. On raw SDK hosts
24
+ * (Claude Agent SDK, OpenAI Assistants, etc.) that baseline is
25
+ * absent, so the recommended prompt below carries slightly more
26
+ * role context to compensate.
27
+ *
28
+ * If an agent isn't following the wire flow correctly even with
29
+ * this prompt, the bug lives in the protocol's tool descriptions
30
+ * or server instructions — fix THOSE, not this string.
31
+ */
32
+ /**
33
+ * The recommended one-line system prompt for ggui-aware agents.
34
+ *
35
+ * Posture-setting only. Carries no procedural detail — the wire flow
36
+ * is taught by the server's `InitializeResult.instructions` and
37
+ * per-tool `description` strings.
38
+ *
39
+ * @example
40
+ * ```ts
41
+ * import { query } from '@anthropic-ai/claude-agent-sdk';
42
+ * import { GGUI_AGENT_SYSTEM_PROMPT } from '@ggui-ai/protocol/recommended-prompts';
43
+ *
44
+ * query({
45
+ * prompt: userInput,
46
+ * options: {
47
+ * systemPrompt: GGUI_AGENT_SYSTEM_PROMPT,
48
+ * mcpServers: { ggui: { type: 'http', url: 'http://localhost:6781/mcp' } },
49
+ * },
50
+ * });
51
+ * ```
52
+ *
53
+ * @public
54
+ */
55
+ export const GGUI_AGENT_SYSTEM_PROMPT = 'You are a UI agent. Respond to every user request by rendering an interactive UI via the ggui_* MCP tools instead of by replying in plain text. The tool descriptions and server instructions explain the wire flow — follow them.';
@@ -0,0 +1,9 @@
1
+ import type { DataContract } from '../types/data-contract.js';
2
+ /**
3
+ * Compute the canonical 16-char identity hash for a contract shape.
4
+ *
5
+ * Pure function, no I/O. Deterministic across runs / processes /
6
+ * Node versions (sha256 + utf-8 encoding are well-specified).
7
+ */
8
+ export declare function blueprintKey(contract: DataContract | undefined): string;
9
+ //# sourceMappingURL=blueprint-key.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"blueprint-key.d.ts","sourceRoot":"","sources":["../../src/registry/blueprint-key.ts"],"names":[],"mappings":"AAiBA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,2BAA2B,CAAC;AAG9D;;;;;GAKG;AACH,wBAAgB,YAAY,CAAC,QAAQ,EAAE,YAAY,GAAG,SAAS,GAAG,MAAM,CAGvE"}
@@ -0,0 +1,28 @@
1
+ /**
2
+ * Deterministic identity hash for a `DataContract` shape.
3
+ *
4
+ * `blueprintKey(contract)` is the Tier 1 exact-match key in the
5
+ * blueprint registry: two contract that canonicalize to the same
6
+ * string produce the same key, regardless of how the agent
7
+ * paraphrased the surrounding intent. Equal key → guaranteed
8
+ * registry lookup hit (no LLM rerank, no embedding similarity).
9
+ *
10
+ * 16-character sha256 prefix — matches the existing `blueprintHash`
11
+ * shape in `cache-trace-sink` and `generation-cache.ts`. Collision
12
+ * probability for 100s-of-thousands of distinct contract is ~10^-6
13
+ * (birthday-bound on 2^64), well below the budget for the OSS
14
+ * single-tenant scope. Hosted multi-tenant deployments scope keys
15
+ * by appId so the bound is per-tenant, never global.
16
+ */
17
+ import { createHash } from 'node:crypto';
18
+ import { canonicalizeContracts } from './canonicalize-contract.js';
19
+ /**
20
+ * Compute the canonical 16-char identity hash for a contract shape.
21
+ *
22
+ * Pure function, no I/O. Deterministic across runs / processes /
23
+ * Node versions (sha256 + utf-8 encoding are well-specified).
24
+ */
25
+ export function blueprintKey(contract) {
26
+ const canonical = canonicalizeContracts(contract);
27
+ return createHash('sha256').update(canonical).digest('hex').slice(0, 16);
28
+ }
@@ -0,0 +1,35 @@
1
+ import type { DataContract } from '../types/data-contract.js';
2
+ type JsonValue = string | number | boolean | null | JsonValue[] | {
3
+ [key: string]: JsonValue;
4
+ };
5
+ /**
6
+ * Walk `value` recursively and produce a structurally-equivalent
7
+ * tree with stripped keys removed and all strings NFC-normalized.
8
+ * Arrays preserved in order; primitives unchanged except for Unicode
9
+ * normalization on strings; `undefined` collapses to absent (mirrors
10
+ * JSON behavior). Object key sorting + JSON serialization happen in
11
+ * the downstream JCS pass — this function handles the domain-specific
12
+ * strip step PLUS Unicode normalization (RFC 8785 leaves NFC out of
13
+ * scope; we apply it ourselves so visually-identical contracts hash
14
+ * identically regardless of the agent's keyboard / IME composition).
15
+ *
16
+ * Exported for tests / debugging — production callers should use
17
+ * `canonicalizeContracts()`.
18
+ */
19
+ export declare function canonicalizeValue(value: unknown): JsonValue | undefined;
20
+ /**
21
+ * Produce the canonical bytes for a `DataContract` value, suitable
22
+ * for hashing or external content-address lookups. Stable across
23
+ * paraphrase, key order, whitespace, and description-only edits.
24
+ *
25
+ * Empty / undefined / `{}` all collapse to the same canonical bytes,
26
+ * which produces a stable `blueprintKey` for the "no-contract" case.
27
+ * That key isn't used for cache lookups (registry refuses to register
28
+ * contract-less pushes) but stays well-defined for completeness.
29
+ *
30
+ * The output is a UTF-8-encoded JSON string per RFC 8785. External
31
+ * implementations using any JCS library produce the same bytes.
32
+ */
33
+ export declare function canonicalizeContracts(contract: DataContract | undefined): string;
34
+ export {};
35
+ //# sourceMappingURL=canonicalize-contract.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"canonicalize-contract.d.ts","sourceRoot":"","sources":["../../src/registry/canonicalize-contract.ts"],"names":[],"mappings":"AAyEA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,2BAA2B,CAAC;AAW9D,KAAK,SAAS,GACV,MAAM,GACN,MAAM,GACN,OAAO,GACP,IAAI,GACJ,SAAS,EAAE,GACX;IAAE,CAAC,GAAG,EAAE,MAAM,GAAG,SAAS,CAAA;CAAE,CAAC;AAEjC;;;;;;;;;;;;;GAaG;AACH,wBAAgB,iBAAiB,CAAC,KAAK,EAAE,OAAO,GAAG,SAAS,GAAG,SAAS,CA4CvE;AAED;;;;;;;;;;;;GAYG;AACH,wBAAgB,qBAAqB,CAAC,QAAQ,EAAE,YAAY,GAAG,SAAS,GAAG,MAAM,CAOhF"}
@@ -0,0 +1,166 @@
1
+ /**
2
+ * Canonical, deterministic serialization of `DataContract` per
3
+ * RFC 8785 (JSON Canonicalization Scheme — JCS).
4
+ *
5
+ * Goal: two contracts with the same WIRE-OBSERVABLE shape produce
6
+ * the same canonical bytes, regardless of key order, whitespace, or
7
+ * description-only differences. The canonical string is the input to
8
+ * `blueprintKey()`; equal canonical strings → equal cache key →
9
+ * exact-match hit at handshake time.
10
+ *
11
+ * # Why RFC 8785
12
+ *
13
+ * JCS is the IETF standard for deterministic JSON serialization
14
+ * (published 2020). External implementers can compute the same hash
15
+ * we do using any JCS library (`canonicalize` on npm, `gocanon` in
16
+ * Go, `pyjcs` in Python, `serde_jcs` in Rust). That makes the
17
+ * `contractHash` field a portable identity claim — agents can verify
18
+ * a hash off-server without knowing our internal format.
19
+ *
20
+ * # Pipeline
21
+ *
22
+ * stripDescriptions(contract) ← domain rule, OURS
23
+ * ↓
24
+ * canonicalize(value) ← RFC 8785, vendored library
25
+ * ↓
26
+ * utf-8 bytes, then sha256 + 16-char hex prefix (in `blueprint-key.ts`)
27
+ *
28
+ * # Domain rule: informational prose is stripped
29
+ *
30
+ * `description` and `usage` are informational only — they don't alter
31
+ * the wire surface (no React.Context is mounted from a description,
32
+ * no action gets routed by a usage hint). Stripping them means a
33
+ * documentation tweak doesn't invalidate a registered blueprint, and
34
+ * an agent's intent-prose override on a gadget export-use entry (the
35
+ * `description?` / `usage?` fields of each
36
+ * `clientCapabilities.gadgets[<package>][<exportName>]`, agent-authored
37
+ * at push time) does not pollute the cache key.
38
+ *
39
+ * The strip is keyed on `description`/`usage` as STRING-VALUED fields,
40
+ * never as map keys. A gadget package, an `agentCapabilities.tools`
41
+ * entry, or a spec slot may legitimately be NAMED `usage` (npm package
42
+ * names allow it) — that key maps to an object and MUST survive, or two
43
+ * distinct contracts collide onto one blueprint cache key and the wrong
44
+ * cached UI is served. Informational prose is always a string; a data
45
+ * map key named `usage`/`description` always maps to an object/array —
46
+ * so the value-type test cleanly separates the two. If QA later shows
47
+ * the prose affects LLM-generated copy enough to be load-bearing, flip
48
+ * `STRIPPED_KEYS`.
49
+ *
50
+ * # Preserved (load-bearing for behavior)
51
+ *
52
+ * - The four typed specs: `propsSpec`, `actionSpec`, `contextSpec`,
53
+ * `streamSpec` (including `streamSpec[*].source.tool` cross-refs).
54
+ * - The two catalogs: `agentCapabilities.tools[*]` (key names + their
55
+ * `inputSchema`/`outputSchema`) and `clientCapabilities.gadgets`
56
+ * (package-keyed — npm package names as outer keys, export names as
57
+ * the inner `exports` map keys; no `version`, no transport metadata
58
+ * on the wire).
59
+ * - Inner JSON Schema fields under every `schema:` wrapper (`type`,
60
+ * `enum`, `properties`, `required`, `items`, `additionalProperties`,
61
+ * `nullable`, `format`, …).
62
+ * - `default` values, `required` flags, `nextStep` hints (cross-ref
63
+ * identity), `confirm` / `icon` / `label` / `mode` / `replay` on
64
+ * their respective spec entries.
65
+ * - `enum` array order — order matters for select UIs; JCS preserves
66
+ * array order so we don't pre-sort.
67
+ * - Slot / action / stream channel / agent-capability tool names,
68
+ * plus gadget package keys and per-package export names (every key
69
+ * in the four specs + two catalogs).
70
+ *
71
+ * Pure function — no I/O, no globals.
72
+ */
73
+ import canonicalize from 'canonicalize';
74
+ /**
75
+ * Informational-prose field names removed from the canonical form —
76
+ * but ONLY where they are string-valued (see `canonicalizeValue`); a
77
+ * same-named map key is data and is preserved. Stripping happens
78
+ * BEFORE JCS — JCS itself has no concept of "ignorable fields"; that's
79
+ * the protocol's domain rule, not the serialization standard's.
80
+ */
81
+ const STRIPPED_KEYS = new Set(['description', 'usage']);
82
+ /**
83
+ * Walk `value` recursively and produce a structurally-equivalent
84
+ * tree with stripped keys removed and all strings NFC-normalized.
85
+ * Arrays preserved in order; primitives unchanged except for Unicode
86
+ * normalization on strings; `undefined` collapses to absent (mirrors
87
+ * JSON behavior). Object key sorting + JSON serialization happen in
88
+ * the downstream JCS pass — this function handles the domain-specific
89
+ * strip step PLUS Unicode normalization (RFC 8785 leaves NFC out of
90
+ * scope; we apply it ourselves so visually-identical contracts hash
91
+ * identically regardless of the agent's keyboard / IME composition).
92
+ *
93
+ * Exported for tests / debugging — production callers should use
94
+ * `canonicalizeContracts()`.
95
+ */
96
+ export function canonicalizeValue(value) {
97
+ if (value === undefined)
98
+ return undefined;
99
+ if (value === null)
100
+ return null;
101
+ const t = typeof value;
102
+ if (t === 'string')
103
+ return value.normalize('NFC');
104
+ if (t === 'number' || t === 'boolean') {
105
+ return value;
106
+ }
107
+ if (Array.isArray(value)) {
108
+ const out = [];
109
+ for (const item of value) {
110
+ const c = canonicalizeValue(item);
111
+ if (c !== undefined)
112
+ out.push(c);
113
+ }
114
+ return out;
115
+ }
116
+ if (t === 'object') {
117
+ const obj = value;
118
+ // NFC-normalize keys before sort so two contracts with the same
119
+ // key in different normalization forms (precomposed "café" vs
120
+ // decomposed "café") collapse to one identity. Keep the original
121
+ // key for lookup; emit the normalized key in output. Keys sorted
122
+ // alphabetically here too — idempotent with the downstream JCS
123
+ // pass, but keeps the intermediate-form output predictable for
124
+ // tests/debugging that consume `canonicalizeValue` directly.
125
+ const entries = Object.keys(obj)
126
+ // Strip `description`/`usage` ONLY when string-valued — i.e. only
127
+ // where they are informational prose fields. A key that IS
128
+ // literally `description`/`usage` as a MAP key (a gadget package,
129
+ // an `agentCapabilities.tools` entry, a spec slot) maps to an
130
+ // object/array and MUST survive: stripping it collapses distinct
131
+ // contracts onto one blueprint cache key.
132
+ .filter((k) => !(STRIPPED_KEYS.has(k) && typeof obj[k] === 'string'))
133
+ .map((k) => ({ source: k, normalized: k.normalize('NFC') }))
134
+ .sort((a, b) => (a.normalized < b.normalized ? -1 : a.normalized > b.normalized ? 1 : 0));
135
+ const out = {};
136
+ for (const { source, normalized } of entries) {
137
+ const c = canonicalizeValue(obj[source]);
138
+ if (c !== undefined)
139
+ out[normalized] = c;
140
+ }
141
+ return out;
142
+ }
143
+ // Functions, symbols, bigint — not JSON-serializable; treat as absent.
144
+ return undefined;
145
+ }
146
+ /**
147
+ * Produce the canonical bytes for a `DataContract` value, suitable
148
+ * for hashing or external content-address lookups. Stable across
149
+ * paraphrase, key order, whitespace, and description-only edits.
150
+ *
151
+ * Empty / undefined / `{}` all collapse to the same canonical bytes,
152
+ * which produces a stable `blueprintKey` for the "no-contract" case.
153
+ * That key isn't used for cache lookups (registry refuses to register
154
+ * contract-less pushes) but stays well-defined for completeness.
155
+ *
156
+ * The output is a UTF-8-encoded JSON string per RFC 8785. External
157
+ * implementations using any JCS library produce the same bytes.
158
+ */
159
+ export function canonicalizeContracts(contract) {
160
+ const stripped = canonicalizeValue(contract ?? {});
161
+ // `canonicalize` may return undefined if every field was stripped;
162
+ // fall back to the empty-object canonical form so the hash function
163
+ // always receives a stable string.
164
+ const result = canonicalize(stripped);
165
+ return result ?? '{}';
166
+ }
@@ -0,0 +1,46 @@
1
+ /**
2
+ * One-line, paraphrase-stable summary of a `DataContract` shape.
3
+ *
4
+ * Two consumers share this string:
5
+ * 1. **Embedding input** — concatenated with the intent prose
6
+ * before bge-small embedding. The structured summary anchors
7
+ * retrieval to slot/action names that survive prose
8
+ * paraphrase, while the prose half feeds bge-small's
9
+ * topic-similarity awareness. Hybrid input.
10
+ * 2. **LLM rerank prompt** — shown to Haiku alongside the user's
11
+ * query so the judge has the structural context it needs to
12
+ * decide match-vs-no-match.
13
+ *
14
+ * Putting both consumers on the same string is load-bearing: if the
15
+ * embedded vector and the prompt-shown summary diverged, we'd get
16
+ * "RAG retrieved candidate X, but the prompt shows a different
17
+ * summary" — the judge would lose context the index used to
18
+ * retrieve. One source of truth eliminates that drift class.
19
+ *
20
+ * Format (deterministic):
21
+ *
22
+ * slots=<name:type,...>;
23
+ * actions=<name(payloadFields)?,...>; streams=<name,...>;
24
+ * props=<name:type,...>
25
+ *
26
+ * Format details:
27
+ * - slots: `name:type` (e.g. `count:number,draft:string`). Bare
28
+ * `name` when the schema has no `type` field.
29
+ * - actions: `name(field1,field2,...)` when the action's schema has
30
+ * non-empty `properties`; bare `name` when payload-less. This
31
+ * differentiation is load-bearing: a `sendChip` action with
32
+ * `{chipText: string}` payload must NOT match a payload-less
33
+ * `sendChip` cached blueprint, since the runtime-emitted action
34
+ * would arrive with `data: {}` instead of `data: {chipText}` —
35
+ * the agent loses the user's input. Surfacing payload field names
36
+ * in the summary lets the judge see this distinction.
37
+ * - streams: bare `name` (channel schemas vary a lot and are usually
38
+ * emitted by the agent later; including them adds noise).
39
+ * - props: `name:type` (initial render shape).
40
+ *
41
+ * Empty slots/actions/streams/props collapse to `∅`.
42
+ */
43
+ import type { DataContract } from '../types/data-contract.js';
44
+ /** Stable summary of `contract` for embedding + rerank input. */
45
+ export declare function summarizeContract(contract: DataContract | undefined): string;
46
+ //# sourceMappingURL=summarize-contract.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"summarize-contract.d.ts","sourceRoot":"","sources":["../../src/registry/summarize-contract.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAyCG;AACH,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,2BAA2B,CAAC;AAE9D,iEAAiE;AACjE,wBAAgB,iBAAiB,CAC/B,QAAQ,EAAE,YAAY,GAAG,SAAS,GACjC,MAAM,CA6CR"}
@@ -0,0 +1,63 @@
1
+ /** Stable summary of `contract` for embedding + rerank input. */
2
+ export function summarizeContract(contract) {
3
+ if (!contract)
4
+ return 'slots=∅; actions=∅; streams=∅';
5
+ const slots = contract.contextSpec
6
+ ? Object.entries(contract.contextSpec)
7
+ .sort(([a], [b]) => a.localeCompare(b))
8
+ .map(([name, entry]) => {
9
+ const t = readType(entry.schema);
10
+ return t ? `${name}:${t}` : name;
11
+ })
12
+ .join(',')
13
+ : '';
14
+ const actions = contract.actionSpec
15
+ ? Object.entries(contract.actionSpec)
16
+ .sort(([a], [b]) => a.localeCompare(b))
17
+ .map(([name, entry]) => {
18
+ const fields = readPayloadFields(entry.schema);
19
+ return fields ? `${name}(${fields})` : name;
20
+ })
21
+ .join(',')
22
+ : '';
23
+ const streams = contract.streamSpec
24
+ ? Object.keys(contract.streamSpec).sort().join(',')
25
+ : '';
26
+ const props = contract.propsSpec?.properties
27
+ ? Object.entries(contract.propsSpec.properties)
28
+ .sort(([a], [b]) => a.localeCompare(b))
29
+ .map(([name, entry]) => {
30
+ const t = readType(entry.schema);
31
+ return t ? `${name}:${t}` : name;
32
+ })
33
+ .join(',')
34
+ : '';
35
+ const parts = [];
36
+ parts.push(`slots=${slots || '∅'}`);
37
+ parts.push(`actions=${actions || '∅'}`);
38
+ parts.push(`streams=${streams || '∅'}`);
39
+ if (props)
40
+ parts.push(`props=${props}`);
41
+ return parts.join('; ');
42
+ }
43
+ function readType(schema) {
44
+ if (typeof schema !== 'object' || schema === null)
45
+ return null;
46
+ const t = schema.type;
47
+ return typeof t === 'string' ? t : null;
48
+ }
49
+ /** Sorted comma-joined property names for an action's payload schema,
50
+ * or `null` when the action takes no payload. Empty `properties` →
51
+ * null (payload-less). */
52
+ function readPayloadFields(schema) {
53
+ if (typeof schema !== 'object' || schema === null)
54
+ return null;
55
+ const props = schema.properties;
56
+ if (typeof props !== 'object' || props === null || Array.isArray(props)) {
57
+ return null;
58
+ }
59
+ const keys = Object.keys(props);
60
+ if (keys.length === 0)
61
+ return null;
62
+ return keys.sort().join(',');
63
+ }
@@ -0,0 +1,67 @@
1
+ /**
2
+ * Deterministic {@link DataContract} derivation from MCP tool schemas.
3
+ *
4
+ * Given a primary data tool (with `outputSchema`) and optional action tools
5
+ * (with `inputSchema`), produces a complete contract — no LLM needed.
6
+ *
7
+ * This is the Screen Designer's Tier 1 fast path: when a ggui-first-party MCP
8
+ * server ships native `outputSchema` on every tool, the contract falls out of
9
+ * the schema automatically. The agent supplies the wiring (data tool + action
10
+ * tools); ggui derives the rendering contract.
11
+ *
12
+ * Tier 2 (learned `generatedOutputSchema`) uses the same deriver — the caller
13
+ * passes the learned schema instead of the native one.
14
+ */
15
+ import type { ActionSpec, DataContract, JsonSchema, PropsSpec } from "../types/data-contract.js";
16
+ /** An MCP tool spec, as it appears in tools/list (subset we care about). */
17
+ export interface McpToolSpec {
18
+ name: string;
19
+ description?: string;
20
+ inputSchema?: JsonSchema;
21
+ outputSchema?: JsonSchema;
22
+ }
23
+ export interface DeriveContractInput {
24
+ /** Display name of the owning server, used to build the intent string. */
25
+ serverName: string;
26
+ /** Primary tool — its outputSchema becomes the component's props. */
27
+ dataTool: McpToolSpec;
28
+ /** Optional MCP tools the UI hints at on its gestures. Each becomes an ActionEntry with `nextStep` set to the tool name (advisory hint the agent reads on `ggui_consume` to decide which tool to call next). */
29
+ actionTools?: McpToolSpec[];
30
+ /** Optional intent override. Default: derived from serverName + dataTool.name. */
31
+ intent?: string;
32
+ /** Optional tool-name prefix to strip when generating action keys and labels.
33
+ * Only strips when the tool name actually starts with `${toolPrefix}`. Default: none —
34
+ * the full tool name is used. Supply this only for servers that prefix their tools
35
+ * (e.g. `toolPrefix: "gmail_"` turns `gmail_search_messages` into `searchMessages`).
36
+ * Bare-named tools (`get_task`, `complete_task`) must NOT use this. */
37
+ toolPrefix?: string;
38
+ }
39
+ /**
40
+ * Build a DataContract from native MCP tool schemas.
41
+ *
42
+ * Behavior:
43
+ * - `intent` — "<serverName> — <humanized tool verb>" unless overridden.
44
+ * - `props` — each top-level property of dataTool.outputSchema becomes a PropEntry.
45
+ * If outputSchema is not an object schema, a single `data` prop wraps the whole thing.
46
+ * - `actions` — one ActionEntry per actionTool, with `tool` wired to the MCP tool name
47
+ * and `schema` set to the tool's inputSchema (if present).
48
+ *
49
+ * Pure — deterministic given identical input.
50
+ */
51
+ export declare function deriveContract(input: DeriveContractInput): DataContract;
52
+ /** Convert an outputSchema into a PropsSpec. */
53
+ export declare function propsFromOutputSchema(outputSchema: JsonSchema | undefined, description?: string): PropsSpec | undefined;
54
+ /** Convert a list of action-tools into an ActionSpec, one entry per tool.
55
+ * Collisions (two tools camelCase-ing to the same key) are resolved by appending
56
+ * a numeric suffix — the raw tool name is always preserved on the
57
+ * entry's `nextStep` hint. */
58
+ export declare function actionsFromTools(actionTools: McpToolSpec[], toolPrefix?: string): ActionSpec;
59
+ /** Humanize a tool name, optionally stripping a known server prefix.
60
+ * `humanizeToolName("gmail_search_messages", "gmail_")` → "Search Messages".
61
+ * `humanizeToolName("get_task")` → "Get Task". */
62
+ export declare function humanizeToolName(name: string, toolPrefix?: string): string;
63
+ /** Strip `toolPrefix` iff `name` starts with it; otherwise return `name` unchanged. */
64
+ export declare function stripPrefix(name: string, toolPrefix?: string): string;
65
+ /** "search_messages" → "searchMessages". */
66
+ export declare function camelKey(name: string): string;
67
+ //# sourceMappingURL=derive-contract.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"derive-contract.d.ts","sourceRoot":"","sources":["../../src/schema-learning/derive-contract.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AACH,OAAO,KAAK,EAEV,UAAU,EACV,YAAY,EACZ,UAAU,EAEV,SAAS,EACV,MAAM,2BAA2B,CAAC;AAEnC,4EAA4E;AAC5E,MAAM,WAAW,WAAW;IAC1B,IAAI,EAAE,MAAM,CAAC;IACb,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,WAAW,CAAC,EAAE,UAAU,CAAC;IACzB,YAAY,CAAC,EAAE,UAAU,CAAC;CAC3B;AAED,MAAM,WAAW,mBAAmB;IAClC,0EAA0E;IAC1E,UAAU,EAAE,MAAM,CAAC;IACnB,qEAAqE;IACrE,QAAQ,EAAE,WAAW,CAAC;IACtB,gNAAgN;IAChN,WAAW,CAAC,EAAE,WAAW,EAAE,CAAC;IAC5B,kFAAkF;IAClF,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB;;;;2EAIuE;IACvE,UAAU,CAAC,EAAE,MAAM,CAAC;CACrB;AAED;;;;;;;;;;;GAWG;AACH,wBAAgB,cAAc,CAAC,KAAK,EAAE,mBAAmB,GAAG,YAAY,CAiBvE;AAED,gDAAgD;AAChD,wBAAgB,qBAAqB,CACnC,YAAY,EAAE,UAAU,GAAG,SAAS,EACpC,WAAW,CAAC,EAAE,MAAM,GACnB,SAAS,GAAG,SAAS,CA2BvB;AAED;;;8BAG8B;AAC9B,wBAAgB,gBAAgB,CAAC,WAAW,EAAE,WAAW,EAAE,EAAE,UAAU,CAAC,EAAE,MAAM,GAAG,UAAU,CAkB5F;AAUD;;kDAEkD;AAClD,wBAAgB,gBAAgB,CAAC,IAAI,EAAE,MAAM,EAAE,UAAU,CAAC,EAAE,MAAM,GAAG,MAAM,CAO1E;AAED,uFAAuF;AACvF,wBAAgB,WAAW,CAAC,IAAI,EAAE,MAAM,EAAE,UAAU,CAAC,EAAE,MAAM,GAAG,MAAM,CAGrE;AAED,4CAA4C;AAC5C,wBAAgB,QAAQ,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAK7C"}
@@ -0,0 +1,117 @@
1
+ /**
2
+ * Build a DataContract from native MCP tool schemas.
3
+ *
4
+ * Behavior:
5
+ * - `intent` — "<serverName> — <humanized tool verb>" unless overridden.
6
+ * - `props` — each top-level property of dataTool.outputSchema becomes a PropEntry.
7
+ * If outputSchema is not an object schema, a single `data` prop wraps the whole thing.
8
+ * - `actions` — one ActionEntry per actionTool, with `tool` wired to the MCP tool name
9
+ * and `schema` set to the tool's inputSchema (if present).
10
+ *
11
+ * Pure — deterministic given identical input.
12
+ */
13
+ export function deriveContract(input) {
14
+ const { dataTool, actionTools = [], toolPrefix } = input;
15
+ // `intent` is not a contract field. `input.intent` and `serverName`
16
+ // stay on the input so callers can describe the contract for their
17
+ // own purposes (logging, embedding-search keys); the returned
18
+ // contract carries no intent of its own. See {@link DataContract}.
19
+ const contract = {};
20
+ const props = propsFromOutputSchema(dataTool.outputSchema, dataTool.description);
21
+ if (props)
22
+ contract.propsSpec = props;
23
+ if (actionTools.length > 0) {
24
+ contract.actionSpec = actionsFromTools(actionTools, toolPrefix);
25
+ }
26
+ return contract;
27
+ }
28
+ /** Convert an outputSchema into a PropsSpec. */
29
+ export function propsFromOutputSchema(outputSchema, description) {
30
+ if (!outputSchema)
31
+ return undefined;
32
+ // Object schema — one PropEntry per top-level property.
33
+ if (outputSchema.type === "object" && outputSchema.properties) {
34
+ const required = new Set(outputSchema.required ?? []);
35
+ const properties = {};
36
+ for (const [key, propSchema] of Object.entries(outputSchema.properties)) {
37
+ const entry = {
38
+ schema: propSchema,
39
+ required: required.has(key),
40
+ };
41
+ if (propSchema.description)
42
+ entry.description = propSchema.description;
43
+ if (propSchema.example !== undefined)
44
+ entry.example = propSchema.example;
45
+ properties[key] = entry;
46
+ }
47
+ const spec = { properties };
48
+ if (description)
49
+ spec.description = description;
50
+ return spec;
51
+ }
52
+ // Non-object schema (array, primitive, anyOf) — wrap as single `data` prop.
53
+ const entry = { schema: outputSchema, required: true };
54
+ if (outputSchema.description)
55
+ entry.description = outputSchema.description;
56
+ const spec = { properties: { data: entry } };
57
+ if (description)
58
+ spec.description = description;
59
+ return spec;
60
+ }
61
+ /** Convert a list of action-tools into an ActionSpec, one entry per tool.
62
+ * Collisions (two tools camelCase-ing to the same key) are resolved by appending
63
+ * a numeric suffix — the raw tool name is always preserved on the
64
+ * entry's `nextStep` hint. */
65
+ export function actionsFromTools(actionTools, toolPrefix) {
66
+ const actions = {};
67
+ for (const tool of actionTools) {
68
+ let actionKey = camelKey(stripPrefix(tool.name, toolPrefix));
69
+ if (actionKey in actions) {
70
+ // Fall back to the full tool name (camelCased) before disambiguating with a suffix.
71
+ const full = camelKey(tool.name);
72
+ actionKey = full in actions ? uniqueKey(actions, full) : full;
73
+ }
74
+ const entry = {
75
+ label: humanizeToolName(tool.name, toolPrefix),
76
+ nextStep: tool.name,
77
+ };
78
+ if (tool.description)
79
+ entry.description = tool.description;
80
+ if (tool.inputSchema)
81
+ entry.schema = tool.inputSchema;
82
+ actions[actionKey] = entry;
83
+ }
84
+ return actions;
85
+ }
86
+ function uniqueKey(existing, base) {
87
+ let i = 2;
88
+ while (`${base}${i}` in existing)
89
+ i++;
90
+ return `${base}${i}`;
91
+ }
92
+ // ────────────────────────── naming helpers ──────────────────────────
93
+ /** Humanize a tool name, optionally stripping a known server prefix.
94
+ * `humanizeToolName("gmail_search_messages", "gmail_")` → "Search Messages".
95
+ * `humanizeToolName("get_task")` → "Get Task". */
96
+ export function humanizeToolName(name, toolPrefix) {
97
+ const stripped = stripPrefix(name, toolPrefix);
98
+ return stripped
99
+ .split(/[_\-\s]+/)
100
+ .filter(Boolean)
101
+ .map((w) => w.charAt(0).toUpperCase() + w.slice(1).toLowerCase())
102
+ .join(" ");
103
+ }
104
+ /** Strip `toolPrefix` iff `name` starts with it; otherwise return `name` unchanged. */
105
+ export function stripPrefix(name, toolPrefix) {
106
+ if (!toolPrefix)
107
+ return name;
108
+ return name.startsWith(toolPrefix) ? name.slice(toolPrefix.length) : name;
109
+ }
110
+ /** "search_messages" → "searchMessages". */
111
+ export function camelKey(name) {
112
+ const parts = name.split(/[_\-\s]+/).filter(Boolean);
113
+ if (parts.length === 0)
114
+ return name;
115
+ return parts[0].toLowerCase() +
116
+ parts.slice(1).map((w) => w.charAt(0).toUpperCase() + w.slice(1).toLowerCase()).join("");
117
+ }
@@ -0,0 +1,32 @@
1
+ /**
2
+ * Incremental JSON Schema learning — type union over observed samples.
3
+ *
4
+ * Used by the Screen Designer to build `generatedOutputSchema` from real MCP tool
5
+ * responses when the server doesn't ship a native `outputSchema`. Each captured
6
+ * response is merged into the running schema via {@link mergeSchema}; the result
7
+ * converges as more samples arrive.
8
+ *
9
+ * Algorithm:
10
+ * - Object properties: union of keys. A property present in one sample but missing
11
+ * from another is marked optional (dropped from `required`).
12
+ * - Array items: recursive merge across element schemas from all samples.
13
+ * - Primitives of the same type: identity.
14
+ * - `null` + any type: sets `nullable: true` on that type.
15
+ * - Type conflicts (e.g. string + number): collapsed into `anyOf`.
16
+ *
17
+ * Pure — no I/O, no DB, no clock. Safe to call from Lambda, edge, or tests.
18
+ */
19
+ import type { JsonSchema, JsonValue } from "../types/data-contract.js";
20
+ /** Infer a JSON Schema from a single JSON value. */
21
+ export declare function inferSchema(value: JsonValue): JsonSchema;
22
+ /**
23
+ * Merge a new observed sample into an existing schema. If `existing` is null,
24
+ * returns the schema inferred from `sample` alone.
25
+ */
26
+ export declare function mergeSchema(existing: JsonSchema | null | undefined, sample: JsonValue): JsonSchema;
27
+ /**
28
+ * Merge two schemas into one that accepts values satisfying either. Exported
29
+ * for the seeder path (combining two known schemas without sampling).
30
+ */
31
+ export declare function mergeTwoSchemas(a: JsonSchema, b: JsonSchema): JsonSchema;
32
+ //# sourceMappingURL=merge.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"merge.d.ts","sourceRoot":"","sources":["../../src/schema-learning/merge.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;GAiBG;AACH,OAAO,KAAK,EAAc,UAAU,EAAE,SAAS,EAAE,MAAM,2BAA2B,CAAC;AAEnF,oDAAoD;AACpD,wBAAgB,WAAW,CAAC,KAAK,EAAE,SAAS,GAAG,UAAU,CA4BxD;AAED;;;GAGG;AACH,wBAAgB,WAAW,CAAC,QAAQ,EAAE,UAAU,GAAG,IAAI,GAAG,SAAS,EAAE,MAAM,EAAE,SAAS,GAAG,UAAU,CAIlG;AAED;;;GAGG;AACH,wBAAgB,eAAe,CAAC,CAAC,EAAE,UAAU,EAAE,CAAC,EAAE,UAAU,GAAG,UAAU,CA0BxE"}