/** * Zod schema for {@link DataContract} — the canonical wire shape for * agent-authored contract declarations. * * ## Why this file exists * * Output-side seams that type-narrow contract on the wire — * `renderOutputSchema.contract` and the various `decision` echoes — use * `z.custom()` because they trust the shape (it * originates from internal server state). * * The input seam is different: agents author contract on * `story.contract` and the handler MUST plumb them to the generator. * `z.custom()` does NOT work on input schemas — the * MCP SDK serializes input schemas as JSON Schema for `tools/list`, * and `z.custom()` has no JSON-Schema representation (it's a * TypeScript-only escape hatch). The narrow alternative * (`z.record(z.string(), z.unknown())`) erases the type and forces * `as DataContract` casts downstream, which violates the project's * Zero Workarounds Policy + Strict Typing First principle. * * The fundamental fix is this file: a real zod schema mirroring the * `DataContract` interface field-for-field. Both the protocol's * `handshakeInputSchema` and the OSS handler's `inputSchema` build * their `contract?` field from `dataContractSchema`, the type derives * via `z.infer`, and JSON-Schema serialization for MCP `tools/list` * advertises the contract surface to agents. * * ## Shape source of truth * * The TS interface in `../types/data-contract.ts` remains the * declared source of truth (consumers import the type). This file's * schemas are a structural mirror — `dataContractSchema` is typed * `z.ZodType` so any drift between schema and * interface fails compile. A future cleanup can flip the relationship * (TS via `z.infer`), but that's a broader refactor. * * ## JsonSchema posture * * Every nested `schema: JsonSchema` field on contract entries * (PropEntry, ActionEntry, StreamChannelEntry, ContextEntry, ...) * accepts `jsonSchemaSchema` — a permissive `z.object` over the * known JSON Schema draft-07 subset {@link JsonSchema} declares, * with `passthrough()` for fields the shape doesn't enumerate. We * deliberately do NOT enforce JSON Schema's full grammar at this * layer — that work belongs in * `@ggui-ai/protocol/validation/schema-subset` and runs at * render-time + blueprint-registration-time as the F4 schema * compatibility checker. Agents authoring malformed schemas surface * at that pass with a named violation reason; this layer's job is * just to accept the contract and pass it to the generator. */ import { z } from 'zod'; import type { ActionSpec, DataContract, JsonValue, JsonSchema, JsonObject } from '../types/data-contract.js'; /** * Recursive {@link JsonValue} — string | number | boolean | null | * array | object. All fields on contract entries that carry default * values, examples, or arbitrary JSON payloads use this. */ export declare const jsonValueSchema: z.ZodType; /** A JSON object — the protocol's `JsonObject`, never a bare `Record`. */ export declare const jsonObjectSchema: z.ZodType; export declare const jsonSchemaSchema: z.ZodType; /** * {@link PropEntry} — per-prop metadata in a {@link PropsSpec}. * * Shape: `{schema: {type:'string', ...}, required?, default?, ...}`. * The JSON Schema NEVER sits flat at the entry level — every entry's * schema lives in `.schema`. Authors writing `{type:'string'}` instead * of `{schema: {type:'string'}}` will hit a shape error at render time. */ export declare const propEntrySchema: z.ZodObject<{ description: z.ZodOptional; schema: z.ZodType>; required: z.ZodOptional; default: z.ZodOptional>>; example: z.ZodOptional>>; sourceTool: z.ZodOptional; }, z.core.$strict>; /** {@link PropsSpec} — wrapper `{description?, properties}` over the per-prop map. */ export declare const propsSpecSchema: z.ZodObject<{ description: z.ZodOptional; properties: z.ZodRecord; schema: z.ZodType>; required: z.ZodOptional; default: z.ZodOptional>>; example: z.ZodOptional>>; sourceTool: z.ZodOptional; }, z.core.$strict>>; }, z.core.$strict>; /** * {@link ActionEntry} — per-action metadata in an {@link ActionSpec}. * * Actions are agent-routed gestures; no dispatch discriminator. Optional * `nextStep` hints at the agent's intended next tool call (must resolve * to an `agentCapabilities.tools[*]` key on the same contract — * cross-ref enforced by the `CTR_REF_NEXT_STEP` linter). * * Anti-pattern: do NOT write `dispatch: {kind: 'tool', tool: '...'}` — * that vocabulary is retired. Use a flat optional `nextStep: ''`. */ export declare const actionEntrySchema: z.ZodObject<{ description: z.ZodOptional; label: z.ZodString; schema: z.ZodOptional>>; example: z.ZodOptional>>; icon: z.ZodOptional; confirm: z.ZodOptional; oneShot: z.ZodOptional; nextStep: z.ZodOptional; }, z.core.$strict>; /** {@link ActionSpec} — flat `Record`. */ export declare const actionSpecSchema: z.ZodRecord; label: z.ZodString; schema: z.ZodOptional>>; example: z.ZodOptional>>; icon: z.ZodOptional; confirm: z.ZodOptional; oneShot: z.ZodOptional; nextStep: z.ZodOptional; }, z.core.$strict>>; /** * READ-door variant of {@link actionSpecSchema} (ggui#1178). An entry member * this release does not name is STRIPPED, not refused; every other member is * validated exactly as the write door validates it. This is the INTERPRET * side of the two-read-paths rule (see `parseAppThemeAtReadDoor`). * * Why it exists: a runtime reads `actionSpec` off the static render slice * (`_meta["ai.ggui/render"].actionSpec`) that a possibly NEWER server wrote. * `actionEntrySchema` is `.strict()`, so a strict read would drop the whole * spec the day a newer release adds an entry member. The runtime would fall * back to `{}`: no one-shot guard and permissive gesture validation, which is * the fail-open ggui#1178 closes. Contract AUTHORING (every write door) * stays on the strict {@link actionSpecSchema}. */ export declare const actionSpecReadSchema: z.ZodRecord; label: z.ZodString; schema: z.ZodOptional>>; example: z.ZodOptional>>; icon: z.ZodOptional; confirm: z.ZodOptional; oneShot: z.ZodOptional; nextStep: z.ZodOptional; }, z.core.$strip>>; export type ActionSpecReadDoorResult = { readonly ok: true; readonly actionSpec: ActionSpec; readonly stripped: readonly string[]; } | { readonly ok: false; readonly issues: readonly string[]; }; /** * Parse an `actionSpec` at a READ door: `ok` with the spec and the entry * members that were stripped (`.`, sorted, `[]` when none), * or the issues in `path: message` form when the spec is refused for a reason * the write door would also refuse (not a record, an entry without `label`, a * member of the wrong type). The caller NAMES both outcomes: a stripped * member and a dropped spec are degradations, and a degradation is never * silent. */ export declare function parseActionSpecAtReadDoor(input: unknown): ActionSpecReadDoorResult; /** * The spent-`oneShot` names on the render slice (ggui#1223): the `actionSpec` * names declared `oneShot` that already had a COMMITTED dispatch on the card * the slice mounts. Names only — non-empty strings; the `oneShot` flag itself * is read from the card's `actionSpec`, never inferred from a listed name. */ export declare const spentOneShotsSchema: z.ZodArray; /** {@link StreamChannelEntry} — per-channel metadata in a {@link StreamSpec}. */ export declare const streamChannelEntrySchema: z.ZodObject<{ description: z.ZodOptional; schema: z.ZodType>; example: z.ZodOptional>>; mode: z.ZodOptional>; replay: z.ZodOptional>; complete: z.ZodOptional; source: z.ZodOptional>>>; }, z.core.$strict>>; }, z.core.$strict>; /** {@link StreamSpec} — flat `Record`. */ export declare const streamSpecSchema: z.ZodRecord; schema: z.ZodType>; example: z.ZodOptional>>; mode: z.ZodOptional>; replay: z.ZodOptional>; complete: z.ZodOptional; source: z.ZodOptional>>>; }, z.core.$strict>>; }, z.core.$strict>>; /** {@link ContextEntry} — per-slot metadata in a {@link ContextSpec}. */ export declare const contextEntrySchema: z.ZodObject<{ description: z.ZodOptional; schema: z.ZodType>; default: z.ZodOptional>>; debounceMs: z.ZodOptional; example: z.ZodOptional>>; }, z.core.$strict>; /** {@link ContextSpec} — flat `Record`. */ export declare const contextSpecSchema: z.ZodRecord; schema: z.ZodType>; default: z.ZodOptional>>; debounceMs: z.ZodOptional; example: z.ZodOptional>>; }, z.core.$strict>>; /** {@link AgentToolEntry} — per-tool metadata in an {@link AgentCapabilitiesSpec}. */ export declare const agentToolEntrySchema: z.ZodObject<{ serverInfo: z.ZodOptional; }, z.core.$strict>>; toolInfo: z.ZodObject<{ inputSchema: z.ZodType>; description: z.ZodOptional; outputSchema: z.ZodOptional>>; }, z.core.$strict>; usage: z.ZodOptional; example: z.ZodOptional>; output: z.ZodType>; }, z.core.$strict>>; }, z.core.$strict>; /** {@link AgentCapabilitiesSpec} — wrapper over the per-tool map. */ export declare const agentCapabilitiesSpecSchema: z.ZodObject<{ tools: z.ZodRecord; }, z.core.$strict>>; toolInfo: z.ZodObject<{ inputSchema: z.ZodType>; description: z.ZodOptional; outputSchema: z.ZodOptional>>; }, z.core.$strict>; usage: z.ZodOptional; example: z.ZodOptional>; output: z.ZodType>; }, z.core.$strict>>; }, z.core.$strict>>; }, z.core.$loose>; /** * Single source of truth for the `requires[]` field shape on gadget * descriptors. The wire-permissive `gadgetDescriptorSchema`, the * strict `strictGadgetDescriptorSchema`, AND the author-facing * `@ggui-ai/artifact-manifest#gadgetManifestSchema` all need an * identical `z.array(z.string().regex(PUBLIC_ENV_APP_KEY_RE))`. * Exported here so any future tightening (e.g., cap count, dedupe * refinement) lives in one place. * * Entries are App.publicEnv key names — `GGUI_PUBLIC_APP_*`. Wrappers * that declare a `requires` key must have a corresponding App-side * publicEnv value at render time (gate: `assertPublicEnvSatisfied`). */ export declare const gadgetRequiresSchema: z.ZodArray; /** * SRI hash format for gadget bundles. Registry install writes * `bundleSri` in this shape; iframe-runtime emits it verbatim into * the `