import { type DescriptionSpillFormat } from "./spill"; import { type JsonObject } from "./types"; export type ResidualSchemaIncompatibility = "type-array" | "type-null" | "nullable" | "combiners"; export interface NormalizeSchemaOptions { unsupportedFields: (key: string) => boolean; normalizeFieldNames: boolean; collapseNullFields: boolean; normalizeTypeArrayToNullable: boolean; stripNullableKeyword: boolean; autoPropertyOrdering: boolean; ensureObjectProperties: boolean; liftStrippedToDescription: false | { keys?: (key: string) => boolean; format?: DescriptionSpillFormat; }; mergeObjectCombiners: boolean; collapseSameTypeCombiners: boolean; collapseMixedTypeCombiners: boolean; stripResidualCombinersFixpoint: boolean; extractNullableFromUnions: boolean; inferTypeForBareEnum: boolean; foldOneOfIntoAnyOf: boolean; dropNonScalarEnum: boolean; rejectResidualIncompatibilities?: ReadonlyArray; /** * Coerce 2020-12 boolean subschemas (`items: true`, `properties: {x: false}`) * to `{}` in schema positions. Boolean schemas are valid JSON Schema (so AJV * validation passes them) but the Cloud Code Assist `Schema` proto requires * a message, and a leaked boolean fails the whole request with * `Invalid value at '...parameters.properties[N].value.items'` (400). */ coerceBooleanSubschemas?: boolean; validateAndFallback?: { fallback: unknown; }; } /** Copy all keys from a schema except the specified combiner key. */ export declare function copySchemaWithout(schema: JsonObject, combiner: string): JsonObject; /** * Recursively strip any remaining anyOf/oneOf that same-type or mixed-type * collapse can handle. This is needed because object-combiner merging can * create new anyOf in merged subtrees after child normalization already ran. */ export declare function stripResidualCombiners(value: unknown, epoch?: number): unknown; export declare function normalizeSchema(value: unknown, options: NormalizeSchemaOptions): unknown; export declare function normalizeSchemaForGoogle(value: unknown): unknown; export declare function normalizeSchemaForCCA(value: unknown): unknown; export declare function normalizeSchemaForMCP(value: unknown): unknown; /** * Moonshot Flavored JSON Schema (MFJS) — the stricter subset Moonshot/Kimi * native hosts (api.moonshot.ai, api.kimi.com) validate * `tools.function.parameters` against. It rejects standard JSON Schema * constructs that OpenAI-compatible hosts accept, returning HTTP 400 * `tools.function.parameters is not a valid moonshot flavored json schema`. * Differences this normalizer reconciles: * * - `const` (incl. `anyOf`/`oneOf` whose every branch is a bare `const`) is * rejected; collapse to `enum` with an inferred scalar `type`. * - `oneOf` is not an MFJS combinator (only `anyOf` is); residual `oneOf` is * folded into `anyOf`. * - `type` must be a scalar string; `type: [...]` arrays are reduced to a * single scalar (the `null` branch is dropped — `nullable` is unsupported). * - Enum-bearing nodes get an inferred `type` (the idiomatic MFJS form; a bare * `enum` is valid too) so `anyOf` branches always carry a `type`. * - Validation/decorative keywords (`minItems`, `maxItems`, `maxLength`, * `pattern`, `format`, `title`, …) and tuple `prefixItems` are rejected and * stripped, spilling human-meaningful ones into the sibling `description`. * `default` and `description` are MFJS Meta Data fields and are preserved. * - `additionalProperties` (boolean or schema) and `type: "null"` (incl. * inside `anyOf`) are kept. * * Out of scope (absent from the built-in tool surface, spec-ambiguous to * rewrite blindly): `allOf` intersection merging, external/recursive `$ref`, * and the depth-10 limit. */ export declare function normalizeSchemaForMoonshot(value: unknown): unknown; /** * OpenAI Responses rejects `oneOf` in tool schemas even when strict mode is * disabled, and rejects every schema node with `type: "object"` unless it has * a `properties` member. Normalize only schema-valued positions so literal * payloads under `enum`, `const`, `default`, and `examples` remain unchanged. * * Identity-preserving: returns the input reference unchanged when no rewrite * occurred so callers can dedupe via reference equality (and the strict-mode * cache stays warm). If a node has both `oneOf` and `anyOf`, the two are * concatenated (the wire payload accepts a single union; preserving both * would not survive). */ export declare function sanitizeSchemaForOpenAIResponses(schema: JsonObject): JsonObject; /** * Alias for {@link sanitizeSchemaForOpenAIResponses} matching the * `normalizeSchemaFor*` dispatcher naming used elsewhere in this module. */ export declare const normalizeSchemaForOpenAIResponses: (schema: JsonObject) => JsonObject; /** * First pass of strict-mode preparation. * * Rewrites everything strict mode forbids into something it accepts: * - Drops non-structural keywords (`format`, `pattern`, `examples`, …), * `const`, `nullable`, and `additionalProperties` (re-added by * `enforceStrictSchema` as `false`). * - `type: [a, b]` → `anyOf: [{type: a, …}, {type: b, …}]`, copying only the * keywords each variant can use (e.g. `properties` stays only on the * object variant). * - `const` → single-entry `enum`. * - Description carries a `(default: X)` suffix so the model still sees the * documented default after the keyword is stripped. * - `nullable: true` wraps the whole node in `anyOf:[T,{type:"null"}]`. * * Recurses into properties, items, prefixItems, combinators, and $defs. The * `cache` WeakMap dedupes shared subgraphs; the `epoch` is the cycle guard. */ export declare function sanitizeSchemaForStrictMode(schema: Record, epoch?: number, cache?: WeakMap, Record>, root?: Record): Record; /** * Recursively enforces JSON Schema constraints required by OpenAI/Codex strict mode: * - `additionalProperties: false` on every object node * - every key in `properties` present in `required` * * Properties absent from the original `required` array were TypeBox-optional. * They are made nullable (`anyOf: [T, { type: "null" }]`) so the model can * signal omission by outputting null rather than omitting the key entirely. * * @throws {Error} When a schema node has no `type`, array-based combinator * (`anyOf`/`allOf`/`oneOf`), object-based combinator (`not`), or `$ref` — * i.e. the node is not representable in strict mode. Prefer * {@link tryEnforceStrictSchema} which catches this and degrades gracefully. */ export declare function enforceStrictSchema(schema: Record, cache?: WeakMap, Record>): Record; export declare function tryEnforceStrictSchema(schema: Record): { schema: Record; strict: boolean; };