/** * Schema-subset algorithm — answers "can every value the `subset` * schema accepts also pass the `superset` schema?". * * **Why this lives in the protocol package.** The schema-alignment * contract is enforced at render-time + blueprint-registration; the * canonical failure is the named `'schema_mismatch_error'` rejection * the push / registration call answers with — fail-loud at * declaration time, before a malformed payload reaches the agentic * loop. See the schema-compat docstrings * on {@link ActionEntry.schema} and {@link StreamChannelEntry.schema} * for the author-invariant the check enforces. * * **Check points.** * * - Pre-commit of a `GguiSession` with `actionSpec` / `streamSpec` * entries that reference tools: each action's declared schema * MUST be a subset of the tool's inputSchema (what the action * payload is allowed to send ⊆ what the tool accepts). Each * stream channel's declared schema MUST be a subset of the * tool's return schema (what the channel emits ⊆ what the tool * returns — inverted because the DIRECTION reverses). * - Policy via {@link CreateGguiServerOptions.schemaCompatCheck}: * `'reject'` (default) / `'warn'` / `'off'`. * * **Algorithm scope (P0).** * * - `type` match — primitive types must agree; only `undefined` * on the superset side is a wildcard. * - `required` — subset's required set MUST be a subset of * superset's required set (tighter required on the subset side * = strictly fewer values accepted ⇒ OK; tighter on the * superset side would accept FEWER values than the subset ⇒ * violation). * - `properties` — recursion: every subset property MUST be a * subset of the matching superset property. * - `additionalProperties` — semantics: * * - superset `true` (default when omitted) → subset is * unconstrained on extra keys — OK. * - superset `false` → subset MUST also be `false` (anything * else widens). * - superset JsonSchema → subset's additionalProperties MUST * be a subset of the superset's (recurse), OR `false` * (never emits extras, always fits). * - `items` — arrays: subset's `items` MUST be a subset of * superset's `items`. When either side omits `items`, the check * is permissive in that direction. * * **P1 scope (deferred).** * * - `oneOf` / `anyOf` covering — subset union members must each * be covered by at least one superset member. * - `enum` — subset's enum values must all be in the superset's * enum (or superset has no enum constraint). * - `const` — subset's const must equal superset's const (or * superset has no const constraint). * * **P2 scope (deferred — known limitations documented for * third-party authors).** * * - `$ref` — no local or remote resolution; schemas with `$ref` * are flagged as {@link SubsetViolationReason.unsupported}. * - `allOf` — not merged before comparison. * - String / number constraints — `minimum` / `maximum` / * `minLength` / `maxLength` / `pattern` / `format` are NOT * compared. A superset's narrower bound is not detected as a * violation. * - Tuple items (`items: JsonSchema[]`) — not in the current * {@link JsonSchema} type, so not supported here. * * **Determinism contract.** No randomness, no IO, no thrown * exceptions for normal violations. Every incompatibility is * reported as a {@link SubsetViolation} with enough field-path * context for the `schema_mismatch_error` rejection to name the * mismatch cleanly. Thrown errors are reserved for programmer- * bug conditions (a caller passes `null` where a JsonSchema is * expected). * * @see ./schema-compat-invariants.ts — the protocol-level invariants * that call this algorithm at render-time. */ import type { JsonSchema, JsonValue } from '../types/data-contract.js'; /** * Category of subset violation. Narrow enough that a downstream * consumer can pattern-match on it if it wants to render a * specialized message; wide enough to admit future P1/P2 reasons * without a protocol-level bump. */ export type SubsetViolationReason = 'type-mismatch' /** Subset declares a property the superset does not allow (via * `properties` or `additionalProperties: false`). */ | 'extra-property' /** Subset marks a property required that is not required on the * superset — accepted, but only when the superset also allows the * property at all. The combined check produces this reason only * when the superset REJECTS the property entirely (missing from * properties AND additionalProperties: false). */ | 'required-widens' /** Superset marks a property required that the subset does not * require. The subset may omit a value the superset would reject. */ | 'missing-required' /** Array items schema mismatch. */ | 'items-mismatch' /** `additionalProperties: false` on superset, non-false on subset. */ | 'additional-properties-widens' /** Schema uses a construct this P0 implementation does not support * (e.g. `$ref`, `allOf`, `oneOf`/`anyOf`, `enum`, `const`). The * pair is flagged instead of silently passing. */ | 'unsupported'; /** * A single point of incompatibility between `superset` and `subset`. * Carries enough context for the caller to render a message that * names the field path + both sides. */ export interface SubsetViolation { /** * Dotted field path from the root of the compared schemas. * `''` (empty) means the root schemas themselves mismatched. * `'properties.foo.items'` means the `items` of the `foo` property * mismatched. Uses `.items` for array element descent and `.` * for object property descent. No escaping — property names * containing `.` will produce ambiguous paths but are valid JSON. */ readonly path: string; /** Category of violation. */ readonly reason: SubsetViolationReason; /** The superset side's value at `path`, as a short JSON string * (stringified, truncated at 120 chars). `undefined` when the * superset has no explicit value at the path. */ readonly superset?: string; /** The subset side's value at `path`, same formatting rules as * {@link SubsetViolation.superset}. */ readonly subset?: string; /** Human-readable summary suitable for inclusion in a * `schema_mismatch_error` rejection. Producers MAY ignore this * and render their own message from `path` + `reason` if they * prefer a consistent localized format. */ readonly message: string; } /** * Result of {@link isSchemaSubset}. Wraps `compatible` with the * violation list so callers that only need the boolean can check * `result.compatible`, and callers that emit envelopes can project * the violations into the error details. */ export interface SchemaSubsetResult { readonly compatible: boolean; readonly violations: readonly SubsetViolation[]; } /** * Compare two JSON Schemas under the "subset acceptance" relation: * returns `compatible: true` iff every JSON value that `subset` * accepts would also be accepted by `superset` (under the P0 scope * documented at the top of this file). * * Neither argument is mutated. Order matters: `isSchemaSubset(a, b)` * checks "is b a subset of a", NOT "is a a subset of b". * * `null` / non-object inputs throw — they are programmer errors, * not schema violations. Every legitimate incompatibility is * reported via the returned {@link SubsetViolation} list. */ export declare function isSchemaSubset(superset: JsonSchema, subset: JsonSchema): SchemaSubsetResult; export type { JsonValue }; //# sourceMappingURL=schema-subset.d.ts.map