@mochabug/adapt-sdk 0.4.6 → 0.4.9

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.
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../src/signals/index.ts", "../../src/schema-utils.ts", "../../src/openai/index.ts"],
4
- "sourcesContent": ["import type {\n JTDSchemaJson,\n SignalDescriptorJson,\n SignalFormatJson\n} from '../api';\nimport { isSchema, isValidSchema, validate as jtdValidate } from 'jtd';\nimport type { Schema, ValidationError } from 'jtd';\n\n// \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\n// JTD FUZZER (generate sample values from JTD schemas)\n// \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\n\n/** Maximum recursion depth for fuzzJTD to prevent infinite loops on self-referencing schemas. */\nconst FUZZ_MAX_DEPTH = 10;\n\n/** Generate a fuzzed (sample) value from a JTD schema. */\nexport function fuzzJTD(\n schema: JTDSchemaJson,\n definitions?: Record<string, JTDSchemaJson>,\n _depth: number = 0\n): unknown {\n if (_depth > FUZZ_MAX_DEPTH) return null;\n\n if (schema.ref) {\n const def = definitions?.[schema.ref];\n if (def) return fuzzJTD(def, definitions, _depth + 1);\n return null;\n }\n\n if (schema.type) {\n switch (schema.type) {\n case 'string':\n return 'Lorem ipsum';\n case 'timestamp':\n return '2024-01-15T10:30:00Z';\n case 'float32':\n case 'float64':\n return 42.5;\n case 'int8':\n case 'uint8':\n case 'int16':\n case 'uint16':\n case 'int32':\n case 'uint32':\n return BigInt(42);\n case 'boolean':\n return true;\n default:\n return null;\n }\n }\n\n if (schema.enum) {\n return schema.enum[0] ?? null;\n }\n\n if (schema.elements) {\n return [\n fuzzJTD(schema.elements, definitions, _depth + 1),\n fuzzJTD(schema.elements, definitions, _depth + 1)\n ];\n }\n\n if (schema.values) {\n return { key1: fuzzJTD(schema.values, definitions, _depth + 1) };\n }\n\n if (schema.properties || schema.optionalProperties) {\n const obj: Record<string, unknown> = {};\n if (schema.properties) {\n for (const [k, v] of Object.entries(schema.properties)) {\n obj[k] = fuzzJTD(v, definitions, _depth + 1);\n }\n }\n if (schema.optionalProperties) {\n for (const [k, v] of Object.entries(schema.optionalProperties)) {\n obj[k] = fuzzJTD(v, definitions, _depth + 1);\n }\n }\n return obj;\n }\n\n if (schema.discriminator && schema.mapping) {\n const firstKey = Object.keys(schema.mapping)[0];\n if (firstKey) {\n const variant = schema.mapping[firstKey]!;\n const obj = fuzzJTD(variant, definitions, _depth + 1) as Record<\n string,\n unknown\n >;\n obj[schema.discriminator] = firstKey;\n return obj;\n }\n return null;\n }\n\n // Empty schema\n return null;\n}\n\n// \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\n// JTD VALUE VALIDATION (delegates to the `jtd` npm package)\n// \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\n\nconst INTEGER_TYPES = new Set([\n 'int8',\n 'uint8',\n 'int16',\n 'uint16',\n 'int32',\n 'uint32'\n]);\n\nfunction formatInstancePath(instancePath: string[]): string {\n if (instancePath.length === 0) return '/';\n return instancePath\n .map((p) => (/^\\d+$/.test(p) ? `[${p}]` : `.${p}`))\n .join('');\n}\n\nfunction resolveSchemaParent(\n schema: Record<string, unknown>,\n schemaPath: string[]\n): Record<string, unknown> {\n let current: any = schema;\n for (let i = 0; i < schemaPath.length - 1; i++) {\n if (current && typeof current === 'object' && schemaPath[i]! in current) {\n current = current[schemaPath[i]!];\n } else {\n break;\n }\n }\n return current as Record<string, unknown>;\n}\n\nfunction resolveInstanceValue(value: unknown, instancePath: string[]): unknown {\n let current = value;\n for (const key of instancePath) {\n if (current && typeof current === 'object') {\n current = (current as Record<string, unknown>)[key];\n } else {\n return undefined;\n }\n }\n return current;\n}\n\nfunction formatValidationError(\n error: ValidationError,\n schema: Record<string, unknown>,\n value: unknown\n): string {\n const { instancePath, schemaPath } = error;\n const path = formatInstancePath(instancePath);\n const lastKey = schemaPath[schemaPath.length - 1];\n\n if (lastKey === 'type') {\n const parent = resolveSchemaParent(schema, schemaPath);\n const jtdType = parent?.type as string;\n if (jtdType === 'boolean') return `${path}: expected boolean`;\n if (jtdType === 'string') return `${path}: expected string`;\n if (jtdType === 'timestamp') return `${path}: expected timestamp string`;\n if (INTEGER_TYPES.has(jtdType)) return `${path}: expected integer`;\n return `${path}: expected number`;\n }\n\n if (lastKey === 'enum') {\n const parent = resolveSchemaParent(schema, schemaPath);\n const enumValues = parent?.enum as string[];\n return `${path}: expected one of [${enumValues.join(', ')}]`;\n }\n\n if (\n schemaPath.length >= 2 &&\n schemaPath[schemaPath.length - 2] === 'properties' &&\n lastKey\n ) {\n const prefix = path === '/' ? '' : path;\n return `${prefix}.${lastKey}: missing required property`;\n }\n\n if (lastKey === 'elements') return `${path}: expected array`;\n\n if (\n lastKey === 'properties' ||\n lastKey === 'optionalProperties' ||\n lastKey === 'values'\n ) {\n return `${path}: expected object`;\n }\n\n if (lastKey === 'discriminator')\n return `${path}: expected string discriminator`;\n\n if (lastKey === 'mapping') {\n const discValue = resolveInstanceValue(value, instancePath);\n return `${path}: unknown variant \"${discValue}\"`;\n }\n\n if (schemaPath.length === 0) return `${path}: unexpected property`;\n\n return `${path}: validation error at /${schemaPath.join('/')}`;\n}\n\n/**\n * Validate a value against a JTD schema, returning an array of error messages.\n * Delegates to the `jtd` npm package for spec-compliant validation.\n */\nexport function validateJsonAgainstJTD(\n schema: JTDSchemaJson,\n value: unknown,\n definitions?: Record<string, JTDSchemaJson>\n): string[] {\n const defs =\n definitions ??\n (schema.definitions as Record<string, JTDSchemaJson> | undefined);\n\n const schemaForValidation = defs\n ? ({ ...schema, definitions: defs } as Schema)\n : (schema as Schema);\n\n // The jtd lib throws on invalid schemas (e.g., unresolved refs).\n if (!isValidSchema(schemaForValidation)) {\n if (schema.ref) {\n return [`/: unknown ref \"${schema.ref}\"`];\n }\n return [`/: invalid schema`];\n }\n\n const errors = jtdValidate(schemaForValidation, value, {\n maxDepth: MAX_VALIDATION_DEPTH,\n maxErrors: MAX_VALIDATION_ERRORS\n });\n\n return errors.map((err) =>\n formatValidationError(\n err,\n schemaForValidation as Record<string, unknown>,\n value\n )\n );\n}\n\n// \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\n// SIGNAL-LEVEL VALIDATION (JSON values against JTD formats)\n// \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\n\n/** Validate a JSON value against a single SignalFormatJson. */\nexport function validateJsonAgainstSignal(\n format: SignalFormatJson,\n jsonValue: unknown\n): string[];\n/** Validate a JSON value against a SignalDescriptorJson (valid if value matches ANY JTD format). */\nexport function validateJsonAgainstSignal(\n descriptor: SignalDescriptorJson,\n jsonValue: unknown\n): string[];\nexport function validateJsonAgainstSignal(\n formatOrDescriptor: SignalFormatJson | SignalDescriptorJson,\n jsonValue: unknown\n): string[] {\n // Descriptor path \u2014 has `formats` array\n if (\n 'formats' in formatOrDescriptor &&\n Array.isArray(formatOrDescriptor.formats)\n ) {\n const jtdFormats = (\n formatOrDescriptor.formats as SignalFormatJson[]\n ).filter((f) => f.jtdSchema);\n if (jtdFormats.length === 0) {\n return ['/: no JTD formats in signal descriptor'];\n }\n // Valid if value matches ANY JTD format\n for (const format of jtdFormats) {\n const errors = validateJsonAgainstJTD(format.jtdSchema!, jsonValue);\n if (errors.length === 0) return [];\n }\n // None matched \u2014 return errors from last format attempt\n return validateJsonAgainstJTD(\n jtdFormats[jtdFormats.length - 1]!.jtdSchema!,\n jsonValue\n );\n }\n\n // Single format path\n const format = formatOrDescriptor as SignalFormatJson;\n if (!format.jtdSchema) {\n return ['/: signal format has no jtdSchema'];\n }\n return validateJsonAgainstJTD(format.jtdSchema, jsonValue);\n}\n\n// \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\n// JTD \u2192 JSON SCHEMA CONVERTER\n// \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\n\n/**\n * Convert JTD (JSON Type Definition) schema to JSON Schema for validation.\n *\n * Supports all 8 JTD schema forms per RFC 8927:\n * - Empty form: {}\n * - Ref form: { ref: \"...\" }\n * - Type form: { type: \"...\" }\n * - Enum form: { enum: [...] }\n * - Elements form: { elements: {...} }\n * - Properties form: { properties: {...}, optionalProperties: {...} }\n * - Values form: { values: {...} }\n * - Discriminator form: { discriminator: \"...\", mapping: {...} }\n */\nexport function jtdToJsonSchema(\n jtdSchema: JTDSchemaJson,\n definitions?: Record<string, JTDSchemaJson>,\n isRoot = true\n): any {\n if (!jtdSchema || typeof jtdSchema !== 'object') {\n return {};\n }\n\n const isNullable = jtdSchema['nullable'];\n const defs =\n definitions ||\n (jtdSchema['definitions'] as Record<string, JTDSchemaJson> | undefined);\n\n const result: any = {};\n\n if (isRoot) {\n result.$schema = 'http://json-schema.org/draft-07/schema#';\n }\n\n // Ref form\n if (jtdSchema['ref']) {\n const refName = jtdSchema['ref'] as string;\n if (defs && defs[refName]) {\n const resolvedSchema = jtdToJsonSchema(\n defs[refName] as JTDSchemaJson,\n defs,\n false\n );\n\n if (isNullable) {\n if (resolvedSchema.type) {\n resolvedSchema.type = Array.isArray(resolvedSchema.type)\n ? [...resolvedSchema.type, 'null']\n : [resolvedSchema.type, 'null'];\n } else {\n return {\n ...resolvedSchema,\n anyOf: [resolvedSchema, { type: 'null' }],\n ...(isRoot\n ? { $schema: 'http://json-schema.org/draft-07/schema#' }\n : {})\n };\n }\n }\n\n return isRoot\n ? {\n ...resolvedSchema,\n $schema: 'http://json-schema.org/draft-07/schema#'\n }\n : resolvedSchema;\n }\n return isRoot ? result : {};\n }\n\n // Type form\n if (jtdSchema['type']) {\n const typeMap: Record<string, string> = {\n boolean: 'boolean',\n string: 'string',\n timestamp: 'string',\n float32: 'number',\n float64: 'number',\n int8: 'integer',\n uint8: 'integer',\n int16: 'integer',\n uint16: 'integer',\n int32: 'integer',\n uint32: 'integer'\n };\n const jtdType = jtdSchema['type'] as string;\n const baseType = typeMap[jtdType] || 'string';\n\n result.type = isNullable ? [baseType, 'null'] : baseType;\n\n if (baseType === 'string' && jtdType === 'timestamp') {\n result.format = 'date-time';\n }\n return result;\n }\n\n // Enum form\n if (jtdSchema['enum']) {\n result.type = isNullable ? ['string', 'null'] : 'string';\n result.enum = isNullable\n ? [...(jtdSchema['enum'] as string[]), null]\n : jtdSchema['enum'];\n return result;\n }\n\n // Elements form\n if (jtdSchema['elements']) {\n result.type = isNullable ? ['array', 'null'] : 'array';\n result.items = jtdToJsonSchema(\n jtdSchema['elements'] as JTDSchemaJson,\n defs,\n false\n );\n return result;\n }\n\n // Properties form\n if (jtdSchema['properties'] || jtdSchema['optionalProperties']) {\n result.type = isNullable ? ['object', 'null'] : 'object';\n result.properties = {};\n result.required = [];\n\n if (jtdSchema['properties']) {\n for (const [key, value] of Object.entries(jtdSchema['properties'])) {\n result.properties[key] = jtdToJsonSchema(\n value as JTDSchemaJson,\n defs,\n false\n );\n result.required.push(key);\n }\n }\n\n if (jtdSchema['optionalProperties']) {\n for (const [key, value] of Object.entries(\n jtdSchema['optionalProperties']\n )) {\n result.properties[key] = jtdToJsonSchema(\n value as JTDSchemaJson,\n defs,\n false\n );\n }\n }\n\n // JTD is strict by default\n if (jtdSchema['additionalProperties'] !== true) {\n result.additionalProperties = false;\n }\n\n return result;\n }\n\n // Values form\n if (jtdSchema['values']) {\n result.type = isNullable ? ['object', 'null'] : 'object';\n result.additionalProperties = jtdToJsonSchema(\n jtdSchema['values'] as JTDSchemaJson,\n defs,\n false\n );\n return result;\n }\n\n // Discriminator form\n if (jtdSchema['discriminator'] && jtdSchema['mapping']) {\n const variants = [];\n const discriminatorKey = jtdSchema['discriminator'] as string;\n\n for (const [tag, schema] of Object.entries(jtdSchema['mapping'])) {\n const variantSchema = jtdToJsonSchema(\n schema as JTDSchemaJson,\n defs,\n false\n );\n\n if (!variantSchema.type) {\n variantSchema.type = 'object';\n } else if (\n variantSchema.type !== 'object' &&\n !Array.isArray(variantSchema.type)\n ) {\n variantSchema.type = 'object';\n }\n\n variantSchema.properties = variantSchema.properties || {};\n variantSchema.properties[discriminatorKey] = { const: tag };\n\n variantSchema.required = variantSchema.required || [];\n if (!variantSchema.required.includes(discriminatorKey)) {\n variantSchema.required.unshift(discriminatorKey);\n }\n\n // JTD discriminator variants are strict by default\n if (variantSchema.additionalProperties === undefined) {\n variantSchema.additionalProperties = false;\n }\n\n variants.push(variantSchema);\n }\n\n if (isNullable) {\n variants.push({ type: 'null' });\n }\n\n result.oneOf = variants;\n return result;\n }\n\n // Empty form\n return result;\n}\n\n// \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\n// JTD SCHEMA VALIDATION (using the `jtd` npm package)\n// \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\n\nexport const MAX_VALIDATION_DEPTH = 32;\nexport const MAX_VALIDATION_ERRORS = 100;\n\n/** Validate that the input is a valid JTD schema. */\nexport function validateJTDSchema(\n schema: unknown\n): { valid: true; schema: JTDSchemaJson } | { valid: false; error: string } {\n if (!isSchema(schema)) {\n return {\n valid: false,\n error: 'Invalid JTD schema: Schema does not match JTD structure'\n };\n }\n\n if (!isValidSchema(schema)) {\n return {\n valid: false,\n error:\n 'Invalid JTD schema: Schema has semantic errors (e.g., circular references, conflicting forms)'\n };\n }\n\n return { valid: true, schema: schema as unknown as JTDSchemaJson };\n}\n\n/** Parse and validate a JSON string. */\nexport function validateJSON(jsonString: string): {\n valid: boolean;\n data?: unknown;\n error?: string;\n} {\n try {\n const data = JSON.parse(jsonString);\n return { valid: true, data };\n } catch (error) {\n return {\n valid: false,\n error: `Invalid JSON: ${error instanceof Error ? error.message : 'Parse error'}`\n };\n }\n}\n\n/** Parse a JSON string and validate it as a JTD schema. */\nexport function validateJTDSchemaString(\n schemaString: string\n): { valid: true; schema: JTDSchemaJson } | { valid: false; error: string } {\n const jsonResult = validateJSON(schemaString);\n if (!jsonResult.valid) return { valid: false, error: jsonResult.error! };\n\n return validateJTDSchema(jsonResult.data);\n}\n\n// \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\n// MONACO EDITOR CONFIG FOR JTD SCHEMA EDITING\n// \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\n\n/** Monaco editor JSON schema configuration for JTD schema IntelliSense and validation. */\nexport const JTD_SCHEMA_CONFIG = {\n uri: 'https://jsontypedef.com/schemas/jtd-schema.json',\n fileMatch: ['inmemory://jtd-schema/*.json'],\n schema: {\n $schema: 'http://json-schema.org/draft-07/schema#',\n type: 'object',\n properties: {\n type: {\n description: 'Type form - defines a primitive type',\n enum: [\n 'boolean',\n 'string',\n 'timestamp',\n 'float32',\n 'float64',\n 'int8',\n 'uint8',\n 'int16',\n 'uint16',\n 'int32',\n 'uint32'\n ]\n },\n enum: {\n description: 'Enum form - defines a set of string values',\n type: 'array',\n items: { type: 'string' }\n },\n elements: {\n description: 'Elements form - defines an array with typed elements',\n $ref: '#'\n },\n properties: {\n description:\n 'Properties form - defines an object with required properties',\n type: 'object',\n additionalProperties: { $ref: '#' }\n },\n optionalProperties: {\n description: 'Optional properties - defines optional object properties',\n type: 'object',\n additionalProperties: { $ref: '#' }\n },\n values: {\n description: 'Values form - defines a map/dictionary with typed values',\n $ref: '#'\n },\n discriminator: {\n description: 'Discriminator form - defines a tagged union',\n type: 'string'\n },\n mapping: {\n description: 'Mapping for discriminator form',\n type: 'object',\n additionalProperties: { $ref: '#' }\n },\n ref: {\n description: 'Ref form - references a definition',\n type: 'string'\n },\n definitions: {\n description: 'Top-level definitions for reusable schemas',\n type: 'object',\n additionalProperties: { $ref: '#' }\n },\n nullable: {\n description: 'Allow null values',\n type: 'boolean'\n },\n metadata: {\n description: 'Custom metadata for documentation',\n type: 'object'\n },\n additionalProperties: {\n description: 'Allow additional properties (properties form only)',\n type: 'boolean'\n }\n }\n }\n};\n", "/**\n * Shared utilities for JSON Schema providers (OpenAI, Anthropic, Gemini).\n *\n * Contains constants, types, convert output pipeline, definition pooling,\n * and schema inspection helpers used identically across providers.\n */\nimport type { JTDSchemaJson, SignalDescriptorJson, SignalFormatJson } from './api';\nimport { validateJsonAgainstJTD } from './signals';\n\n// \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\n// Constants\n// \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\n\nexport const MAX_REF_DEPTH = 32;\n\nexport const INTEGER_BOUNDS: Record<string, { minimum: number; maximum: number }> = {\n int8: { minimum: -128, maximum: 127 },\n uint8: { minimum: 0, maximum: 255 },\n int16: { minimum: -32768, maximum: 32767 },\n uint16: { minimum: 0, maximum: 65535 },\n int32: { minimum: -2147483648, maximum: 2147483647 },\n uint32: { minimum: 0, maximum: 4294967295 }\n};\n\nexport const FLOAT32_BOUNDS = { minimum: -3.4028235e38, maximum: 3.4028235e38 };\n\nexport const UNSTRUCTURED_DESC =\n 'Must be a valid JSON value encoded as a string. ' +\n \"For objects: '{\\\"key\\\": \\\"value\\\"}', for arrays: '[1, 2]', for primitives: '\\\"hello\\\"' or '42' or 'true' or 'null'.\";\n\nexport const UNSTRUCTURED_VARIANT_DESC =\n 'The variant data encoded as a valid JSON string ' +\n '(e.g. \\'{\"key\": \"value\"}\\').';\n\nexport const VALUES_ARRAY_DESC =\n 'Array of key-value entries representing a map/dictionary. ' +\n \"Each entry has a string 'key' (the property name) and a 'value' (the property value).\";\n\n// \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\n// Primitives\n// \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\n\nexport function nullableType(baseType: string, nullable: boolean): string | string[] {\n return nullable ? [baseType, 'null'] : baseType;\n}\n\n// \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\n// Deep equal\n// \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\n\nexport function deepEqual(a: unknown, b: unknown): boolean {\n if (a === b) return true;\n if (a == null || b == null || typeof a !== typeof b) return false;\n if (typeof a !== 'object') return false;\n if (Array.isArray(a))\n return Array.isArray(b) && a.length === b.length && a.every((v, i) => deepEqual(v, b[i]));\n if (Array.isArray(b)) return false;\n const ao = a as Record<string, unknown>, bo = b as Record<string, unknown>;\n const keys = Object.keys(ao);\n if (keys.length !== Object.keys(bo).length) return false;\n for (const k of keys) {\n if (!(k in bo) || !deepEqual(ao[k], bo[k])) return false;\n }\n return true;\n}\n\n// \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\n// JTD schema inspection (OpenAI + Anthropic)\n// \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\n\nexport function isUnstructuredSchema(schema: JTDSchemaJson): boolean {\n if (!schema || typeof schema !== 'object') return true;\n\n if (schema.type || schema.enum || schema.elements || schema.values || schema.discriminator || schema.ref) {\n return false;\n }\n\n const hasRealProps =\n (schema.properties && Object.keys(schema.properties).length > 0) ||\n (schema.optionalProperties && Object.keys(schema.optionalProperties).length > 0);\n if (hasRealProps) return false;\n\n if ((schema.properties !== undefined || schema.optionalProperties !== undefined) && schema.additionalProperties === true) {\n return true;\n }\n\n if (schema.properties !== undefined || schema.optionalProperties !== undefined) {\n return false;\n }\n\n return true;\n}\n\nexport function isRootUnstructured(\n schema: JTDSchemaJson,\n defs: Record<string, JTDSchemaJson> | undefined\n): boolean {\n let resolved = schema;\n let depth = 0;\n while (resolved.ref && depth < MAX_REF_DEPTH) {\n const target = defs?.[resolved.ref];\n if (!target) return false;\n resolved = target;\n depth++;\n }\n if (depth >= MAX_REF_DEPTH) return false;\n return isUnstructuredSchema(resolved);\n}\n\n// \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\n// Convert result types\n// \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\n\nexport interface ConvertSuccessJson {\n success: true;\n kind: 'json';\n data: unknown;\n}\n\nexport interface ConvertSuccessBinary {\n success: true;\n kind: 'binary';\n mimeType: string;\n data: Uint8Array;\n filename?: string;\n}\n\nexport interface ConvertSuccessSignals {\n success: true;\n kind: 'signals';\n signals: Record<string, unknown>;\n}\n\nexport interface ConvertFailure {\n success: false;\n errors: string[];\n}\n\nexport type ConvertSuccess = ConvertSuccessJson | ConvertSuccessBinary | ConvertSuccessSignals;\nexport type ConvertResult = ConvertSuccess | ConvertFailure;\n\n// \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\n// Convert output pipeline (identical across all 3 providers)\n// \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\n\nexport function convertJTDOutput(\n jtdSchema: JTDSchemaJson,\n json: unknown,\n coerce: (value: unknown, schema: JTDSchemaJson, defs: Record<string, JTDSchemaJson> | undefined, depth: number) => unknown\n): ConvertResult {\n const defs = jtdSchema.definitions;\n const coerced = coerce(json, jtdSchema, defs, 0);\n const errors = validateJsonAgainstJTD(jtdSchema, coerced, defs);\n if (errors.length > 0) return { success: false, errors };\n return { success: true, kind: 'json', data: coerced };\n}\n\nexport function convertMimeOutput(mimeType: string, json: unknown): ConvertResult {\n if (typeof json !== 'object' || json === null) {\n return {\n success: false,\n errors: [`/: expected object with 'data' field for MIME type ${mimeType}, got ${typeof json}`]\n };\n }\n const rec = json as Record<string, unknown>;\n if (typeof rec.data !== 'string') {\n return {\n success: false,\n errors: [`/data: expected base64 string for MIME type ${mimeType}, got ${typeof rec.data}`]\n };\n }\n const result: ConvertSuccessBinary = {\n success: true,\n kind: 'binary',\n mimeType,\n data: new Uint8Array(Buffer.from(rec.data, 'base64'))\n };\n if (typeof rec.filename === 'string' && rec.filename) {\n result.filename = rec.filename;\n }\n return result;\n}\n\nexport function convertSingleFormatOutput(\n fmt: SignalFormatJson,\n json: unknown,\n coerce: (value: unknown, schema: JTDSchemaJson, defs: Record<string, JTDSchemaJson> | undefined, depth: number) => unknown\n): ConvertResult {\n if (fmt.mimeType) return convertMimeOutput(fmt.mimeType, json);\n if (fmt.jtdSchema) return convertJTDOutput(fmt.jtdSchema, json, coerce);\n return { success: false, errors: ['Signal format has neither jtdSchema nor mimeType'] };\n}\n\nexport function convertSignalOutput(\n descriptor: SignalDescriptorJson,\n json: unknown,\n coerce: (value: unknown, schema: JTDSchemaJson, defs: Record<string, JTDSchemaJson> | undefined, depth: number) => unknown\n): ConvertResult {\n const formats = descriptor.formats as SignalFormatJson[] | undefined;\n if (!formats || formats.length === 0) {\n return { success: false, errors: ['Signal descriptor has no formats'] };\n }\n\n if (formats.length === 1) {\n return convertSingleFormatOutput(formats[0], json, coerce);\n }\n\n if (typeof json !== 'object' || json === null) {\n return { success: false, errors: [`/: expected object with format properties, got ${typeof json}`] };\n }\n\n const rec = json as Record<string, unknown>;\n for (let i = 0; i < formats.length; i++) {\n const key = `format_${i}`;\n if (rec[key] !== undefined && rec[key] !== null) {\n return convertSingleFormatOutput(formats[i], rec[key], coerce);\n }\n }\n\n if (descriptor.optional) {\n return { success: true, kind: 'json', data: null };\n }\n\n const keys = formats.map((_, i) => `format_${i}`);\n return {\n success: false,\n errors: [`Signal '${descriptor.name ?? 'unnamed'}' is required but no format was provided. Set exactly one of: ${keys.join(', ')}.`]\n };\n}\n\nexport function convertSignalsOutput(\n descriptors: SignalDescriptorJson[],\n json: unknown,\n coerce: (value: unknown, schema: JTDSchemaJson, defs: Record<string, JTDSchemaJson> | undefined, depth: number) => unknown\n): ConvertResult {\n if (typeof json !== 'object' || json === null) {\n return { success: false, errors: ['/: expected object with signal properties, got ' + typeof json] };\n }\n\n const rec = json as Record<string, unknown>;\n const signals: Record<string, unknown> = {};\n const errors: string[] = [];\n\n for (const desc of descriptors) {\n if (!desc.name) continue;\n const value = rec[desc.name];\n if (value === undefined || value === null) continue;\n\n const result = convertSignalOutput(desc, value, coerce);\n if (!result.success) {\n for (const err of result.errors) {\n errors.push(`/${desc.name}${err.startsWith('/') ? '' : ': '}${err}`);\n }\n } else if (result.kind === 'binary') {\n signals[desc.name] = { data: result.data, mimeType: result.mimeType, filename: result.filename };\n } else if (result.kind === 'json') {\n signals[desc.name] = result.data;\n }\n }\n\n if (errors.length > 0) return { success: false, errors };\n return { success: true, kind: 'signals', signals };\n}\n\n// \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\n// Schema tree walk (for providers that use $ref/$defs)\n// \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\n\n/** Minimal shape for $ref-bearing schemas (satisfied by OpenAISchema and AnthropicSchema). */\ninterface RefNode {\n properties?: Record<string, RefNode>;\n items?: RefNode;\n anyOf?: RefNode[];\n $defs?: Record<string, RefNode>;\n $ref?: string;\n}\n\nexport function walkSchema<T extends RefNode>(schema: T, fn: (node: T) => void): void {\n fn(schema);\n if (schema.properties) for (const v of Object.values(schema.properties)) walkSchema(v as T, fn);\n if (schema.items) walkSchema(schema.items as T, fn);\n if (schema.anyOf) for (const v of schema.anyOf) walkSchema(v as T, fn);\n if (schema.$defs) for (const v of Object.values(schema.$defs)) walkSchema(v as T, fn);\n}\n\n// \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\n// Definition pooling for fromSignals (OpenAI + Anthropic)\n// \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\n\n/** Recursively rewrite JTD `ref` values in a schema according to a rename map. */\nfunction rewriteJtdRefs(schema: JTDSchemaJson, renames: Map<string, string>): JTDSchemaJson {\n if (!schema || typeof schema !== 'object') return schema;\n\n const result: JTDSchemaJson = { ...schema };\n\n if (result.ref && renames.has(result.ref)) {\n result.ref = renames.get(result.ref)!;\n }\n if (result.properties) {\n result.properties = Object.fromEntries(\n Object.entries(result.properties).map(([k, v]) => [k, rewriteJtdRefs(v, renames)])\n );\n }\n if (result.optionalProperties) {\n result.optionalProperties = Object.fromEntries(\n Object.entries(result.optionalProperties).map(([k, v]) => [k, rewriteJtdRefs(v, renames)])\n );\n }\n if (result.elements) result.elements = rewriteJtdRefs(result.elements, renames);\n if (result.values) result.values = rewriteJtdRefs(result.values, renames);\n if (result.mapping) {\n result.mapping = Object.fromEntries(\n Object.entries(result.mapping).map(([k, v]) => [k, rewriteJtdRefs(v, renames)])\n );\n }\n // Definitions are rewritten at pool level, strip from the copy\n if (result.definitions) delete result.definitions;\n\n return result;\n}\n\n/**\n * Pre-scan all signal descriptors, pool their JTD definitions.\n * Deduplicates identical definition SETS; renames conflicts with `_N` suffix.\n *\n * Equivalence is checked per-format: all definitions within a single format's\n * `definitions` block are treated as a unit. Two formats with the same set of\n * definition names and identical schemas (deep-equal) share their defs.\n * If ANY definition name conflicts (same name, different schema), ALL defs\n * from that format get their own namespace to preserve internal cross-references.\n *\n * Returns the merged JTD definitions and signal copies with:\n * - refs rewritten to match merged names\n * - definitions stripped (they live in the merged pool now)\n */\nexport function poolSignalDefinitions(signals: SignalDescriptorJson[]): {\n mergedDefs: Record<string, JTDSchemaJson>;\n signalsCopy: SignalDescriptorJson[];\n} {\n const mergedDefs: Record<string, JTDSchemaJson> = {};\n\n // For each format that has definitions, compute its rename map.\n // Key: `signalIdx:formatIdx`, Value: Map<oldName, newName>\n const renamesByFormat = new Map<string, Map<string, string>>();\n\n for (let si = 0; si < signals.length; si++) {\n const formats = signals[si].formats as SignalFormatJson[] | undefined;\n if (!formats) continue;\n for (let fi = 0; fi < formats.length; fi++) {\n const defs = formats[fi].jtdSchema?.definitions;\n if (!defs || Object.keys(defs).length === 0) continue;\n\n // Check each definition against the merged pool\n const renames = new Map<string, string>();\n let hasConflict = false;\n\n for (const [name, defSchema] of Object.entries(defs)) {\n const existing = mergedDefs[name];\n if (!existing) continue; // new name, no conflict\n if (deepEqual(existing, defSchema)) continue; // identical, dedup\n hasConflict = true;\n break;\n }\n\n if (hasConflict) {\n // This format has at least one conflict. Rename ALL its definitions\n // to avoid broken cross-references between shared and renamed defs.\n for (const [name, defSchema] of Object.entries(defs)) {\n const existing = mergedDefs[name];\n if (existing && deepEqual(existing, defSchema)) {\n // Identical to existing \u2014 can still share\n continue;\n }\n if (existing) {\n // Conflict \u2014 find unique name\n let n = 1;\n while (mergedDefs[`${name}_${n}`]) n++;\n const newName = `${name}_${n}`;\n renames.set(name, newName);\n mergedDefs[newName] = defSchema;\n } else {\n mergedDefs[name] = defSchema;\n }\n }\n } else {\n // No conflicts \u2014 all defs are either new or identical (deduped)\n for (const [name, defSchema] of Object.entries(defs)) {\n if (!mergedDefs[name]) mergedDefs[name] = defSchema;\n }\n }\n\n if (renames.size > 0) renamesByFormat.set(`${si}:${fi}`, renames);\n }\n }\n\n // Rewrite refs inside renamed definitions (they may reference other renamed defs)\n for (const [, renames] of renamesByFormat) {\n for (const [, newName] of renames) {\n mergedDefs[newName] = rewriteJtdRefs(mergedDefs[newName], renames);\n }\n }\n\n // Build signal copies with rewritten refs and stripped definitions\n const signalsCopy: SignalDescriptorJson[] = signals.map((signal, si) => {\n const formats = signal.formats as SignalFormatJson[] | undefined;\n if (!formats) return signal;\n\n let hasChanges = false;\n const newFormats = formats.map((fmt, fi) => {\n if (!fmt.jtdSchema) return fmt;\n const renames = renamesByFormat.get(`${si}:${fi}`);\n const hasDefs = fmt.jtdSchema.definitions && Object.keys(fmt.jtdSchema.definitions).length > 0;\n if (!renames && !hasDefs) return fmt;\n\n hasChanges = true;\n const rewritten = renames\n ? rewriteJtdRefs(fmt.jtdSchema, renames)\n : { ...fmt.jtdSchema };\n delete rewritten.definitions;\n return { ...fmt, jtdSchema: rewritten };\n });\n\n return hasChanges ? { ...signal, formats: newFormats } : signal;\n });\n\n return { mergedDefs, signalsCopy };\n}\n", "// Copyright 2024-2025 Mochabug, LLC. All rights reserved.\n// Licensed under the Apache License, Version 2.0.\n\nimport type {\n JTDSchemaJson,\n SignalDescriptorJson,\n SignalFormatJson\n} from '../api';\nimport {\n MAX_REF_DEPTH,\n INTEGER_BOUNDS,\n FLOAT32_BOUNDS,\n UNSTRUCTURED_DESC,\n UNSTRUCTURED_VARIANT_DESC,\n VALUES_ARRAY_DESC,\n nullableType,\n isUnstructuredSchema,\n isRootUnstructured,\n poolSignalDefinitions,\n convertJTDOutput as sharedConvertJTDOutput,\n convertSignalOutput as sharedConvertSignalOutput,\n convertSignalsOutput as sharedConvertSignalsOutput,\n type ConvertSuccessJson,\n type ConvertSuccessBinary,\n type ConvertSuccessSignals,\n type ConvertFailure,\n type ConvertSuccess,\n type ConvertResult,\n} from '../schema-utils';\nimport { validateJsonAgainstJTD } from '../signals';\n\n// \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\n// Types\n// \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\n\n/**\n * JSON Schema subset accepted by the OpenAI structured-output API (strict mode).\n *\n * This is the provider-specific representation produced by {@link OpenAIConversion}.\n * It covers the keywords that OpenAI inspects when constraining model output in\n * strict mode: `type`, `properties`, `required`, `additionalProperties`, `enum`,\n * `format`, `items`, `minimum`/`maximum`, `anyOf`, `$defs`, `$ref`, and\n * `description`.\n */\nexport interface OpenAISchema {\n type?: string | string[];\n description?: string;\n properties?: Record<string, OpenAISchema>;\n required?: string[];\n additionalProperties?: false;\n enum?: (string | number | null)[];\n format?: string;\n items?: OpenAISchema;\n minimum?: number;\n maximum?: number;\n anyOf?: OpenAISchema[];\n $defs?: Record<string, OpenAISchema>;\n $ref?: string;\n}\n\n// Re-export shared types for public API compatibility\nexport type {\n ConvertSuccessJson,\n ConvertSuccessBinary,\n ConvertSuccessSignals,\n ConvertFailure,\n ConvertSuccess,\n ConvertResult,\n} from '../schema-utils';\n\n\n// \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\n// Helpers\n// \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\n\n/** Check if a JTD schema is unstructured (empty form or equivalent). */\n\n\n/** Make a schema nullable by adding \"null\" to the type. */\nfunction makeNullable(schema: OpenAISchema): OpenAISchema {\n if (Array.isArray(schema.type) && schema.type.includes('null')) return schema;\n if (schema.type === 'null') return schema;\n\n // $ref \u2192 wrap in anyOf with null\n if (schema.$ref) {\n return { anyOf: [schema, { type: 'null' }] };\n }\n\n // anyOf \u2192 add null variant\n if (schema.anyOf) {\n if (schema.anyOf.some((s) => s.type === 'null')) return schema;\n return { ...schema, anyOf: [...schema.anyOf, { type: 'null' }] };\n }\n\n // Standard: add null to type and enum\n const result = { ...schema };\n if (result.type) {\n result.type = Array.isArray(result.type)\n ? [...result.type, 'null']\n : [result.type, 'null'];\n }\n if (result.enum && !result.enum.includes(null)) {\n result.enum = [...result.enum, null];\n }\n return result;\n}\n\n\n// \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\n// Metadata helper\n// \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\n\nconst METADATA_KNOWN_KEYS = new Set(['title', 'label', 'name', 'description']);\n\nfunction applyMetadata(result: OpenAISchema, schema: JTDSchemaJson): void {\n const meta = schema.metadata as Record<string, unknown> | undefined;\n if (!meta) return;\n\n const descParts: string[] = [];\n\n // OAI doesn't support title \u2014 fold title/label/name into description\n const titleCandidate =\n (typeof meta['title'] === 'string' && meta['title'].trim()) ||\n (typeof meta['label'] === 'string' && meta['label'].trim()) ||\n (typeof meta['name'] === 'string' && meta['name'].trim());\n\n const descText =\n typeof meta['description'] === 'string' ? meta['description'].trim() : '';\n\n if (titleCandidate && descText) {\n descParts.push(`${titleCandidate}. ${descText}`);\n } else if (titleCandidate) {\n descParts.push(titleCandidate);\n } else if (descText) {\n descParts.push(descText);\n }\n\n // Extra metadata keys as description hints\n const extras: string[] = [];\n for (const [key, value] of Object.entries(meta)) {\n if (METADATA_KNOWN_KEYS.has(key)) continue;\n if (typeof value === 'string' && value.trim()) {\n extras.push(`[${key}: ${value.trim()}]`);\n } else if (typeof value === 'number' || typeof value === 'boolean') {\n extras.push(`[${key}: ${value}]`);\n }\n }\n if (extras.length > 0) {\n descParts.push(extras.join(' '));\n }\n\n if (descParts.length > 0) {\n const newDesc = descParts.join(' ');\n result.description = result.description\n ? `${result.description} ${newDesc}`\n : newDesc;\n }\n}\n\n// \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\n// Schema conversion (JTD \u2192 OpenAI JSON Schema)\n// \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\n\nfunction convertNode(\n schema: JTDSchemaJson,\n defs: Record<string, JTDSchemaJson> | undefined,\n depth: number\n): OpenAISchema {\n if (depth > MAX_REF_DEPTH) {\n throw new Error(`Schema nesting exceeded ${MAX_REF_DEPTH} levels`);\n }\n\n if (!schema || typeof schema !== 'object') {\n return { type: 'string', description: UNSTRUCTURED_DESC };\n }\n\n // Unstructured (empty form or AP:true with no real props) \u2192 string\n if (isUnstructuredSchema(schema)) {\n const result: OpenAISchema = {\n type: nullableType('string', schema.nullable === true),\n description: UNSTRUCTURED_DESC\n };\n applyMetadata(result, schema);\n return result;\n }\n\n const isNullable = schema.nullable === true;\n\n // Ref form \u2192 $ref\n if (schema.ref) {\n const refName = schema.ref;\n if (!defs?.[refName]) {\n throw new Error(`Unresolved ref \"${refName}\"`);\n }\n if (isNullable) {\n const result: OpenAISchema = {\n anyOf: [{ $ref: `#/$defs/${refName}` }, { type: 'null' }]\n };\n applyMetadata(result, schema);\n return result;\n }\n // Bare $ref \u2014 skip metadata (JSON Schema $ref may ignore sibling keywords)\n return { $ref: `#/$defs/${refName}` };\n }\n\n // Type form\n if (schema.type) {\n const result: OpenAISchema = {};\n\n const intBounds =\n INTEGER_BOUNDS[schema.type as keyof typeof INTEGER_BOUNDS];\n if (intBounds) {\n result.type = nullableType('integer', isNullable);\n result.minimum = intBounds.minimum;\n result.maximum = intBounds.maximum;\n } else\n switch (schema.type) {\n case 'boolean':\n result.type = nullableType('boolean', isNullable);\n break;\n case 'string':\n result.type = nullableType('string', isNullable);\n break;\n case 'timestamp':\n result.type = nullableType('string', isNullable);\n result.format = 'date-time';\n break;\n case 'float32':\n result.type = nullableType('number', isNullable);\n result.minimum = FLOAT32_BOUNDS.minimum;\n result.maximum = FLOAT32_BOUNDS.maximum;\n break;\n case 'float64':\n result.type = nullableType('number', isNullable);\n break;\n default:\n throw new Error(`Unknown JTD type \"${schema.type}\"`);\n }\n\n applyMetadata(result, schema);\n return result;\n }\n\n // Enum form\n if (schema.enum) {\n const result: OpenAISchema = {\n type: nullableType('string', isNullable),\n enum: isNullable ? [...schema.enum, null] : schema.enum\n };\n applyMetadata(result, schema);\n return result;\n }\n\n // Elements form\n if (schema.elements) {\n const result: OpenAISchema = {\n type: nullableType('array', isNullable),\n items: convertNode(schema.elements, defs, depth + 1)\n };\n applyMetadata(result, schema);\n return result;\n }\n\n // Properties form (has real props \u2014 unstructured was caught above)\n if (schema.properties || schema.optionalProperties) {\n const props: Record<string, OpenAISchema> = {};\n const required: string[] = [];\n\n if (schema.properties) {\n for (const [key, propSchema] of Object.entries(schema.properties)) {\n props[key] = convertNode(propSchema, defs, depth + 1);\n required.push(key);\n }\n }\n\n // Optional properties \u2192 required + nullable (null means \"not provided\")\n if (schema.optionalProperties) {\n for (const [key, propSchema] of Object.entries(\n schema.optionalProperties\n )) {\n const converted = convertNode(propSchema, defs, depth + 1);\n const nullable = makeNullable(converted);\n nullable.description = nullable.description\n ? `(Optional, null if not provided) ${nullable.description}`\n : 'Optional. Set to null if not provided.';\n props[key] = nullable;\n required.push(key);\n }\n }\n\n const result: OpenAISchema = {\n type: nullableType('object', isNullable),\n properties: props,\n additionalProperties: false,\n required: required\n };\n applyMetadata(result, schema);\n return result;\n }\n\n // Values form \u2192 array of {key, value} objects\n if (schema.values) {\n const valueSchema = convertNode(schema.values, defs, depth + 1);\n const result: OpenAISchema = {\n type: nullableType('array', isNullable),\n description: VALUES_ARRAY_DESC,\n items: {\n type: 'object',\n properties: {\n key: { type: 'string', description: 'The property name.' },\n value: valueSchema\n },\n required: ['key', 'value'],\n additionalProperties: false\n }\n };\n applyMetadata(result, schema);\n return result;\n }\n\n // Discriminator form \u2192 anyOf with flat variants\n if (schema.discriminator && schema.mapping) {\n const discKey = schema.discriminator;\n const mapping = schema.mapping;\n const variants: OpenAISchema[] = [];\n\n for (const [tag, variantSchema] of Object.entries(mapping)) {\n let variantOAI: OpenAISchema;\n\n if (isUnstructuredSchema(variantSchema)) {\n // Unstructured variant: disc + _data string\n variantOAI = {\n type: 'object',\n properties: {\n [discKey]: { type: 'string', enum: [tag] },\n _data: {\n type: 'string',\n description: UNSTRUCTURED_VARIANT_DESC\n }\n },\n required: [discKey, '_data'],\n additionalProperties: false\n };\n } else {\n // Per RFC 8927, mapping values are always properties form,\n // and the discriminator tag never appears in variant properties.\n variantOAI = convertNode(variantSchema, defs, depth + 1);\n variantOAI.properties![discKey] = { type: 'string', enum: [tag] };\n (variantOAI.required ??= []).unshift(discKey);\n variantOAI.additionalProperties = false;\n }\n\n applyMetadata(variantOAI, variantSchema);\n variants.push(variantOAI);\n }\n\n if (isNullable) variants.push({ type: 'null' });\n const result: OpenAISchema = { anyOf: variants };\n applyMetadata(result, schema);\n return result;\n }\n\n // Fallback\n return { type: 'string', description: UNSTRUCTURED_DESC };\n}\n\n// \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\n// Coercion (OpenAI output \u2192 JTD-conforming JSON)\n// \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\n\nconst INTEGER_TYPE_SET = new Set(Object.keys(INTEGER_BOUNDS));\n\nfunction coerceToJTD(\n value: unknown,\n schema: JTDSchemaJson,\n defs: Record<string, JTDSchemaJson> | undefined,\n depth: number\n): unknown {\n if (depth > MAX_REF_DEPTH) return value;\n if (value === null || value === undefined) return value;\n\n // Ref form \u2014 resolve in original JTD definitions and recurse\n if (schema.ref) {\n const resolved = defs?.[schema.ref];\n if (resolved) {\n return coerceToJTD(value, resolved, defs, depth + 1);\n }\n return value;\n }\n\n // Unstructured schema \u2014 parse JSON string back to value\n if (isUnstructuredSchema(schema)) {\n if (typeof value === 'string') {\n try {\n return JSON.parse(value);\n } catch {\n return value;\n }\n }\n return value;\n }\n\n // Type form \u2014 integer coercion\n if (schema.type && INTEGER_TYPE_SET.has(schema.type)) {\n if (typeof value === 'number' && !Number.isInteger(value)) {\n return Math.round(value);\n }\n return value;\n }\n\n // Elements form\n if (schema.elements && Array.isArray(value)) {\n const elemSchema = schema.elements;\n return value.map((item) => coerceToJTD(item, elemSchema, defs, depth + 1));\n }\n\n // Properties form \u2014 strip null optionals\n if (\n (schema.properties || schema.optionalProperties) &&\n typeof value === 'object' &&\n value !== null\n ) {\n const obj = { ...(value as Record<string, unknown>) };\n\n if (schema.properties) {\n for (const [k, propSchema] of Object.entries(schema.properties)) {\n if (k in obj) {\n obj[k] = coerceToJTD(obj[k], propSchema, defs, depth + 1);\n }\n }\n }\n\n // Optional properties: null \u2192 omit\n if (schema.optionalProperties) {\n for (const [k, propSchema] of Object.entries(schema.optionalProperties)) {\n if (k in obj) {\n if (obj[k] === null) {\n delete obj[k];\n } else {\n obj[k] = coerceToJTD(obj[k], propSchema, defs, depth + 1);\n }\n }\n }\n }\n\n return obj;\n }\n\n // Values form \u2014 convert array of {key, value} to Record\n if (schema.values && Array.isArray(value)) {\n const result: Record<string, unknown> = {};\n for (const entry of value) {\n if (typeof entry === 'object' && entry !== null) {\n const rec = entry as Record<string, unknown>;\n const key = rec.key as string;\n result[key] = coerceToJTD(rec.value, schema.values, defs, depth + 1);\n }\n }\n return result;\n }\n\n // Discriminator form \u2014 flat object, coerce variant + strip null optionals\n if (\n schema.discriminator &&\n schema.mapping &&\n typeof value === 'object' &&\n value !== null\n ) {\n const rec = value as Record<string, unknown>;\n const discKey = schema.discriminator;\n const tag = rec[discKey];\n if (typeof tag !== 'string') return value;\n const variantSchema = schema.mapping[tag];\n\n if (!variantSchema) return value;\n\n // Unstructured variant: parse _data\n if (isUnstructuredSchema(variantSchema)) {\n const dataStr = rec._data;\n if (typeof dataStr === 'string') {\n try {\n const parsed = JSON.parse(dataStr);\n if (typeof parsed === 'object' && parsed !== null) {\n return { [discKey]: tag, ...parsed };\n }\n } catch {\n /* validation catches */\n }\n }\n return { [discKey]: tag };\n }\n\n // Build flat result: discriminator + variant properties\n const result: Record<string, unknown> = { [discKey]: tag };\n\n if (variantSchema.properties) {\n for (const [k, propSchema] of Object.entries(variantSchema.properties)) {\n if (k in rec) {\n result[k] = coerceToJTD(rec[k], propSchema, defs, depth + 1);\n }\n }\n }\n\n if (variantSchema.optionalProperties) {\n for (const [k, propSchema] of Object.entries(\n variantSchema.optionalProperties\n )) {\n if (k in rec && rec[k] !== null) {\n result[k] = coerceToJTD(rec[k], propSchema, defs, depth + 1);\n }\n }\n }\n\n return result;\n }\n\n return value;\n}\n\n// \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\n// MIME format helpers\n// \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\n\nfunction convertMimeFormat(mimeType: string): OpenAISchema {\n return {\n type: 'object',\n description: `Binary content (MIME type: ${mimeType}).`,\n properties: {\n data: {\n type: 'string',\n description: `The base64-encoded binary content (MIME type: ${mimeType}).`\n },\n filename: {\n type: ['string', 'null'],\n description:\n 'Optional filename for the binary content (e.g. \"report.pdf\"). Set to null if not applicable.'\n }\n },\n required: ['data', 'filename'],\n additionalProperties: false\n };\n}\n\n// \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\n// Public API\n// \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\n\n/**\n * Converts JTD schemas or Adapt signal descriptors into OpenAI-compatible\n * JSON Schema (strict mode), and converts raw LLM output back into\n * validated data.\n *\n * ### JTD form mapping\n * - **type** -- mapped to the corresponding JSON Schema `type` (`string`,\n * `number`, `integer`, `boolean`). Integer sub-types (int8 .. uint32) add\n * `minimum`/`maximum` bounds. `timestamp` becomes `string` with\n * `format: \"date-time\"`.\n * - **enum** -- mapped to `type: \"string\"` with an `enum` array.\n * - **elements** -- mapped to `type: \"array\"` with `items`.\n * - **properties / optionalProperties** -- mapped to `type: \"object\"` with\n * `properties`, `required`, and `additionalProperties: false`. OpenAI\n * strict mode requires all properties to be present, so **optional\n * properties are made required and nullable** (their schemas are wrapped\n * with a `\"null\"` type variant). During {@link convert}, null values for\n * optional properties are stripped back out.\n * - **values** -- mapped to an array of `{key, value}` objects because\n * OpenAI strict mode does not support `additionalProperties`. During\n * {@link convert}, the array is coerced back into a plain `Record`.\n * - **discriminator** -- mapped to `anyOf` with one variant per mapping\n * entry. Each variant is a flat object containing the discriminator key\n * (with a single-value `enum`) plus the variant's own properties.\n *\n * ### Unstructured schemas\n * Empty JTD schemas or schemas with only `additionalProperties: true` are\n * considered unstructured. They produce an empty `{}` JSON Schema with\n * `strict: false`, and during {@link convert} the raw string output is\n * parsed via `JSON.parse` back into an arbitrary value.\n *\n * ### MIME formats\n * MIME signal formats become `type: \"string\"` with a description requesting\n * base64-encoded content. On {@link convert}, the base64 string is decoded\n * into a {@link ConvertSuccessBinary} result.\n *\n * ### Serialization\n * `JSON.stringify(conv)` produces a JSON-safe object via {@link toJSON}.\n * Use {@link fromJSON} to restore a fully functional instance from the\n * serialized form (string or parsed object).\n *\n * ```ts\n * const conv = OpenAIConversion.fromJTD(jtdSchema);\n * conv.schema; // OpenAI JSON Schema to pass to the LLM\n * conv.strict; // whether strict mode applies\n * conv.convert(output); // coerce + validate -> ConvertResult\n *\n * // Serialize & restore\n * const cached = JSON.stringify(conv);\n * const restored = OpenAIConversion.fromJSON(cached);\n * ```\n */\nexport class OpenAIConversion<T extends ConvertSuccess = ConvertSuccess> {\n /** The OpenAI-specific JSON Schema to pass to the OpenAI API as the response format. */\n readonly schema: OpenAISchema;\n /**\n * Whether the schema qualifies for OpenAI strict structured-output mode.\n *\n * This is `false` when the root JTD schema is empty or unstructured (e.g.\n * the empty schema `{}` or a properties form with no real properties and\n * `additionalProperties: true`). In that case `schema` is `{}` and the\n * model output is unconstrained.\n */\n readonly strict: boolean;\n /**\n * The original JTD schema used to build this conversion.\n * Set when created via {@link fromJTD}; `undefined` when created via {@link fromSignal}.\n */\n readonly jtdSchema?: JTDSchemaJson;\n /**\n * The original signal descriptor used to build this conversion.\n * Set when created via {@link fromSignal}; `undefined` when created via {@link fromJTD}.\n */\n readonly descriptor?: SignalDescriptorJson;\n /**\n * The original signal descriptors used to build this conversion.\n * Set when created via {@link fromSignals}; `undefined` otherwise.\n */\n readonly descriptors?: SignalDescriptorJson[];\n\n // \u2500\u2500 Factories \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\n /**\n * Build a conversion from a JTD schema.\n *\n * Recursively converts every JTD form into the equivalent OpenAI JSON Schema\n * node. Definitions (`jtdSchema.definitions`) are emitted as `$defs` at the\n * root level, and references become `$ref` pointers.\n *\n * If the root schema is unstructured, the result has an empty `schema` and\n * `strict: false`.\n *\n * @param jtdSchema - A valid JTD schema (RFC 8927).\n * @returns A new {@link OpenAIConversion} with `jtdSchema` set.\n * @throws If the schema contains an unknown JTD type, an unresolved `ref`,\n * or nesting exceeding 32 levels.\n */\n static fromJTD(\n jtdSchema: JTDSchemaJson\n ): OpenAIConversion<ConvertSuccessJson> {\n const defs = jtdSchema.definitions;\n const strict = !isRootUnstructured(jtdSchema, defs);\n\n let schema: OpenAISchema;\n if (strict) {\n schema = convertNode(jtdSchema, defs, 0);\n\n // Attach $defs at root\n if (defs && Object.keys(defs).length > 0) {\n schema.$defs = Object.fromEntries(\n Object.entries(defs).map(([name, defSchema]) => [\n name,\n convertNode(defSchema, defs, 0)\n ])\n );\n }\n } else {\n schema = {};\n applyMetadata(schema, jtdSchema);\n }\n\n return new OpenAIConversion(schema, strict, jtdSchema, undefined);\n }\n\n /**\n * Build a conversion from an Adapt signal descriptor.\n *\n * If the descriptor has a single format, its schema is used directly\n * (unwrapped). If it has multiple formats, a wrapper object schema is\n * produced with `format_0`, `format_1`, ... properties -- each made\n * required and nullable so the LLM populates exactly one and sets the\n * rest to `null`.\n *\n * @param descriptor - An Adapt signal descriptor with one or more formats.\n * @returns A new {@link OpenAIConversion} with `descriptor` set.\n * @throws If the descriptor has no formats, or a format has neither\n * `jtdSchema` nor `mimeType`.\n */\n static fromSignal(\n descriptor: SignalDescriptorJson,\n sharedDefs?: Record<string, JTDSchemaJson>\n ): OpenAIConversion<ConvertSuccessJson | ConvertSuccessBinary> {\n const { schema, strict } = buildSignalSchema(descriptor, sharedDefs);\n return new OpenAIConversion(schema, strict, undefined, descriptor);\n }\n\n /**\n * Build a conversion from multiple signal descriptors.\n *\n * Produces an object schema with one property per signal, suitable for\n * use as structured output parameters. Each signal's schema is built via\n * {@link fromSignal} internally.\n *\n * For OpenAI strict mode, optional signals are made nullable and all\n * properties are added to `required`.\n *\n * If there is only one signal with a single format, the schema is\n * unwrapped to avoid unnecessary nesting \u2014 the signal's format schema\n * is used directly as the object property value.\n *\n * Receiver/transceiver compatible:\n * ```ts\n * const conv = OpenAIConversion.fromSignals(\n * receiver.signals!,\n * receiver.description,\n * receiver.name\n * );\n * ```\n *\n * @param signals - Array of signal descriptors.\n * @param description - Optional description for the compound schema.\n * @param label - Optional label (not used by OpenAI, reserved for other providers).\n * @returns A new {@link OpenAIConversion} with `descriptors` set.\n * @throws If any signal has no formats or an invalid format.\n */\n static fromSignals(\n signals: SignalDescriptorJson[],\n description?: string,\n label?: string\n ): OpenAIConversion<ConvertSuccessSignals> {\n // Pool all JTD definitions across all signals \u2014 dedup identical, rename conflicts\n const { mergedDefs, signalsCopy } = poolSignalDefinitions(signals);\n\n const properties: Record<string, OpenAISchema> = {};\n const required: string[] = [];\n let allStrict = true;\n\n for (const signal of signalsCopy) {\n if (!signal.name) continue;\n // Pass merged defs so buildFormatSchema uses them for ref resolution\n // and does NOT attach $defs at the signal level\n const conv = OpenAIConversion.fromSignal(signal, mergedDefs);\n if (!conv.strict) allStrict = false;\n\n if (signal.optional) {\n properties[signal.name] = makeNullable(conv.schema);\n } else {\n properties[signal.name] = conv.schema;\n }\n required.push(signal.name);\n }\n\n const schema: OpenAISchema = {\n type: 'object',\n properties,\n required,\n additionalProperties: false\n };\n\n // Attach pooled $defs at root \u2014 the only place they should ever live\n if (Object.keys(mergedDefs).length > 0) {\n schema.$defs = Object.fromEntries(\n Object.entries(mergedDefs).map(([name, defSchema]) => [\n name,\n convertNode(defSchema, mergedDefs, 0)\n ])\n );\n }\n\n if (label && description) {\n schema.description = `${label}. ${description}`;\n } else if (label) {\n schema.description = label;\n } else if (description) {\n schema.description = description;\n }\n\n return new OpenAIConversion(\n schema,\n allStrict,\n undefined,\n undefined,\n signals // original signals for serialization (fromJSON recreates)\n );\n }\n\n /**\n * Restore an {@link OpenAIConversion} from its serialized form.\n *\n * Accepts either the JSON string produced by `JSON.stringify(conv)` or the\n * already-parsed plain object. The original `jtdSchema`, `descriptor`, or\n * `descriptors` is re-processed through the corresponding factory, so the\n * restored instance is fully functional.\n *\n * @param data - A JSON string or parsed object previously produced by {@link toJSON}.\n * @returns A fully reconstructed {@link OpenAIConversion}.\n * @throws If the serialized data contains neither `jtdSchema`, `descriptor`, nor `descriptors`.\n */\n static fromJSON(data: string | Record<string, unknown>): OpenAIConversion {\n const parsed = typeof data === 'string' ? JSON.parse(data) : data;\n if (parsed.descriptors) {\n return OpenAIConversion.fromSignals(\n parsed.descriptors as SignalDescriptorJson[],\n parsed.description as string | undefined,\n parsed.label as string | undefined\n );\n }\n if (parsed.descriptor) {\n return OpenAIConversion.fromSignal(\n parsed.descriptor as SignalDescriptorJson\n );\n }\n if (parsed.jtdSchema) {\n return OpenAIConversion.fromJTD(parsed.jtdSchema as JTDSchemaJson);\n }\n throw new Error(\n 'Cannot deserialize: missing jtdSchema, descriptor, or descriptors'\n );\n }\n\n // \u2500\u2500 Constructor (private) \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\n private constructor(\n schema: OpenAISchema,\n strict: boolean,\n jtdSchema: JTDSchemaJson | undefined,\n descriptor: SignalDescriptorJson | undefined,\n descriptors?: SignalDescriptorJson[]\n ) {\n this.schema = schema;\n this.strict = strict;\n this.jtdSchema = jtdSchema;\n this.descriptor = descriptor;\n this.descriptors = descriptors;\n }\n\n // \u2500\u2500 Convert \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\n /**\n * Convert raw LLM output into a validated result.\n *\n * For JTD-based conversions, the value is coerced to conform to the original\n * JTD schema:\n * - Non-integer numbers are rounded for integer types.\n * - Null values on optional properties are stripped (OpenAI required+nullable\n * encoding is reversed).\n * - `{key, value}` arrays from the values form are reassembled into Records.\n * - Unstructured/empty schemas parse the raw JSON string via `JSON.parse`.\n *\n * The coerced value is then validated against the JTD schema. Returns\n * {@link ConvertSuccessJson} on success.\n *\n * For MIME-based conversions, the value is expected to be a base64 string\n * which is decoded into raw bytes. Returns {@link ConvertSuccessBinary}.\n *\n * For multi-format signals, the first non-null `format_N` property is\n * selected and converted individually.\n *\n * @param json - The raw value returned by the OpenAI API (typically parsed JSON).\n * @returns A {@link ConvertResult} indicating success or failure with errors.\n */\n convert = (json: unknown): T | ConvertFailure => {\n if (this.descriptors) {\n return sharedConvertSignalsOutput(this.descriptors, json, coerceToJTD) as T | ConvertFailure;\n }\n if (this.jtdSchema) {\n return sharedConvertJTDOutput(this.jtdSchema, json, coerceToJTD) as T | ConvertFailure;\n }\n if (this.descriptor) {\n return sharedConvertSignalOutput(this.descriptor, json, coerceToJTD) as T | ConvertFailure;\n }\n return { success: false, errors: ['No source schema available'] };\n };\n\n // \u2500\u2500 Serialization \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\n /**\n * Produce a JSON-serializable representation of this conversion.\n *\n * The output includes the OpenAI `schema`, `strict` flag, and either the\n * original `jtdSchema` or `descriptor` (whichever was used to create this\n * instance), which is sufficient to fully restore the conversion via\n * {@link fromJSON}.\n *\n * Called automatically by `JSON.stringify(conv)`.\n */\n toJSON() {\n if (this.descriptors) {\n const result: Record<string, unknown> = {\n schema: this.schema,\n strict: this.strict,\n descriptors: this.descriptors\n };\n if (this.schema.description) result.description = this.schema.description;\n return result;\n }\n if (this.descriptor) {\n return {\n schema: this.schema,\n strict: this.strict,\n descriptor: this.descriptor\n };\n }\n return {\n schema: this.schema,\n strict: this.strict,\n jtdSchema: this.jtdSchema\n };\n }\n}\n\n// \u2500\u2500 Convert helpers (stateless, used by the class) \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\n\n// \u2500\u2500 Signal schema builder \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\nfunction buildSignalSchema(\n descriptor: SignalDescriptorJson,\n sharedDefs?: Record<string, JTDSchemaJson>\n): {\n schema: OpenAISchema;\n strict: boolean;\n} {\n const formats = descriptor.formats as SignalFormatJson[] | undefined;\n if (!formats || formats.length === 0) {\n throw new Error('Signal descriptor has no formats');\n }\n\n if (formats.length === 1) {\n const [fmt] = formats;\n const { schema, strict } = buildFormatSchema(fmt, sharedDefs);\n if (descriptor.label || descriptor.description) {\n const parts: string[] = [];\n if (descriptor.label) parts.push(descriptor.label);\n if (descriptor.description) parts.push(descriptor.description);\n const newDesc = parts.join('. ');\n schema.description = schema.description\n ? `${newDesc} ${schema.description}`\n : newDesc;\n }\n return { schema, strict };\n }\n\n // Multi-format wrapper \u2014 all format props are required + nullable\n const wrapperProps: Record<string, OpenAISchema> = {};\n const required: string[] = [];\n\n for (const [i, fmt] of formats.entries()) {\n const { schema: fmtSchema } = buildFormatSchema(fmt, sharedDefs);\n const key = `format_${i}`;\n\n if (fmt.jtdSchema) {\n fmtSchema.description = fmtSchema.description\n ? `Structured JSON data. ${fmtSchema.description}`\n : 'Structured JSON data.';\n }\n\n wrapperProps[key] = makeNullable(fmtSchema);\n required.push(key);\n }\n\n const keys = Object.keys(wrapperProps);\n const baseDesc = descriptor.description ?? '';\n const isOptional = descriptor.optional === true;\n const guidance = isOptional\n ? 'Provide at most ONE of the following format properties, set all others to null.'\n : `You MUST provide exactly ONE of the following format properties: ${keys.join(', ')}. Set all others to null.`;\n\n const schema: OpenAISchema = {\n type: 'object',\n description: baseDesc ? `${baseDesc} ${guidance}` : guidance,\n properties: wrapperProps,\n required,\n additionalProperties: false\n };\n\n return { schema, strict: true };\n}\n\n/**\n * Build JSON Schema for a single signal format.\n *\n * @param sharedDefs - When provided (fromSignals path), use these merged JTD\n * definitions for ref resolution and do NOT attach $defs to the output schema.\n * When absent (standalone fromJTD/fromSignal), attach $defs at schema root.\n */\nfunction buildFormatSchema(\n fmt: SignalFormatJson,\n sharedDefs?: Record<string, JTDSchemaJson>\n): {\n schema: OpenAISchema;\n strict: boolean;\n} {\n if (fmt.jtdSchema) {\n // Use shared defs if provided (fromSignals), otherwise fall back to format-local defs\n const defs = sharedDefs ?? fmt.jtdSchema.definitions;\n const strict = !isRootUnstructured(fmt.jtdSchema, defs);\n\n let schema: OpenAISchema;\n if (strict) {\n schema = convertNode(fmt.jtdSchema, defs, 0);\n\n // Only attach $defs in standalone mode (no shared defs).\n // When shared defs are provided, the caller (fromSignals) attaches them at root.\n if (!sharedDefs && defs && Object.keys(defs).length > 0) {\n schema.$defs = Object.fromEntries(\n Object.entries(defs).map(([name, defSchema]) => [\n name,\n convertNode(defSchema, defs, 0)\n ])\n );\n }\n } else {\n schema = {};\n applyMetadata(schema, fmt.jtdSchema);\n }\n\n return { schema, strict };\n }\n if (fmt.mimeType) {\n return { schema: convertMimeFormat(fmt.mimeType), strict: true };\n }\n throw new Error('Signal format has neither jtdSchema nor mimeType');\n}\n\n// \u2500\u2500 Convenience aliases \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\n/**\n * Convenience alias for {@link OpenAIConversion.fromJTD}.\n *\n * @param jtdSchema - A valid JTD schema (RFC 8927).\n * @returns A new {@link OpenAIConversion}.\n */\nexport function convertToOpenAISchema(\n jtdSchema: JTDSchemaJson\n): OpenAIConversion {\n return OpenAIConversion.fromJTD(jtdSchema);\n}\n\n/**\n * Convenience alias for {@link OpenAIConversion.fromSignal}.\n *\n * @param descriptor - An Adapt signal descriptor.\n * @returns A new {@link OpenAIConversion}.\n */\nexport function convertSignalToOpenAISchema(\n descriptor: SignalDescriptorJson\n): OpenAIConversion {\n return OpenAIConversion.fromSignal(descriptor);\n}\n"],
5
- "mappings": "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",
4
+ "sourcesContent": ["import type {\n JTDSchemaJson,\n SignalDescriptorJson,\n SignalFormatJson\n} from '../api';\nimport { isSchema, isValidSchema, validate as jtdValidate } from 'jtd';\nimport type { Schema, ValidationError } from 'jtd';\n\n// \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\n// JTD FUZZER (generate sample values from JTD schemas)\n// \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\n\n/** Maximum recursion depth for fuzzJTD to prevent infinite loops on self-referencing schemas. */\nconst FUZZ_MAX_DEPTH = 10;\n\n/** Generate a fuzzed (sample) value from a JTD schema. */\nexport function fuzzJTD(\n schema: JTDSchemaJson,\n definitions?: Record<string, JTDSchemaJson>,\n _depth: number = 0\n): unknown {\n if (_depth > FUZZ_MAX_DEPTH) return null;\n\n if (schema.ref) {\n const def = definitions?.[schema.ref];\n if (def) return fuzzJTD(def, definitions, _depth + 1);\n return null;\n }\n\n if (schema.type) {\n switch (schema.type) {\n case 'string':\n return 'Lorem ipsum';\n case 'timestamp':\n return '2024-01-15T10:30:00Z';\n case 'float32':\n case 'float64':\n return 42.5;\n case 'int8':\n case 'uint8':\n case 'int16':\n case 'uint16':\n case 'int32':\n case 'uint32':\n return BigInt(42);\n case 'boolean':\n return true;\n default:\n return null;\n }\n }\n\n if (schema.enum) {\n return schema.enum[0] ?? null;\n }\n\n if (schema.elements) {\n return [\n fuzzJTD(schema.elements, definitions, _depth + 1),\n fuzzJTD(schema.elements, definitions, _depth + 1)\n ];\n }\n\n if (schema.values) {\n return { key1: fuzzJTD(schema.values, definitions, _depth + 1) };\n }\n\n if (schema.properties || schema.optionalProperties) {\n const obj: Record<string, unknown> = {};\n if (schema.properties) {\n for (const [k, v] of Object.entries(schema.properties)) {\n obj[k] = fuzzJTD(v, definitions, _depth + 1);\n }\n }\n if (schema.optionalProperties) {\n for (const [k, v] of Object.entries(schema.optionalProperties)) {\n obj[k] = fuzzJTD(v, definitions, _depth + 1);\n }\n }\n return obj;\n }\n\n if (schema.discriminator && schema.mapping) {\n const firstKey = Object.keys(schema.mapping)[0];\n if (firstKey) {\n const variant = schema.mapping[firstKey]!;\n const obj = fuzzJTD(variant, definitions, _depth + 1) as Record<\n string,\n unknown\n >;\n obj[schema.discriminator] = firstKey;\n return obj;\n }\n return null;\n }\n\n // Empty schema\n return null;\n}\n\n// \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\n// JTD VALUE VALIDATION (delegates to the `jtd` npm package)\n// \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\n\nconst INTEGER_TYPES = new Set([\n 'int8',\n 'uint8',\n 'int16',\n 'uint16',\n 'int32',\n 'uint32'\n]);\n\nfunction formatInstancePath(instancePath: string[]): string {\n if (instancePath.length === 0) return '/';\n return instancePath\n .map((p) => (/^\\d+$/.test(p) ? `[${p}]` : `.${p}`))\n .join('');\n}\n\nfunction resolveSchemaParent(\n schema: Record<string, unknown>,\n schemaPath: string[]\n): Record<string, unknown> {\n let current: any = schema;\n for (let i = 0; i < schemaPath.length - 1; i++) {\n if (current && typeof current === 'object' && schemaPath[i]! in current) {\n current = current[schemaPath[i]!];\n } else {\n break;\n }\n }\n return current as Record<string, unknown>;\n}\n\nfunction resolveInstanceValue(value: unknown, instancePath: string[]): unknown {\n let current = value;\n for (const key of instancePath) {\n if (current && typeof current === 'object') {\n current = (current as Record<string, unknown>)[key];\n } else {\n return undefined;\n }\n }\n return current;\n}\n\nfunction formatValidationError(\n error: ValidationError,\n schema: Record<string, unknown>,\n value: unknown\n): string {\n const { instancePath, schemaPath } = error;\n const path = formatInstancePath(instancePath);\n const lastKey = schemaPath[schemaPath.length - 1];\n\n if (lastKey === 'type') {\n const parent = resolveSchemaParent(schema, schemaPath);\n const jtdType = parent?.type as string;\n if (jtdType === 'boolean') return `${path}: expected boolean`;\n if (jtdType === 'string') return `${path}: expected string`;\n if (jtdType === 'timestamp') return `${path}: expected timestamp string`;\n if (INTEGER_TYPES.has(jtdType)) return `${path}: expected integer`;\n return `${path}: expected number`;\n }\n\n if (lastKey === 'enum') {\n const parent = resolveSchemaParent(schema, schemaPath);\n const enumValues = parent?.enum as string[];\n return `${path}: expected one of [${enumValues.join(', ')}]`;\n }\n\n if (\n schemaPath.length >= 2 &&\n schemaPath[schemaPath.length - 2] === 'properties' &&\n lastKey\n ) {\n const prefix = path === '/' ? '' : path;\n return `${prefix}.${lastKey}: missing required property`;\n }\n\n if (lastKey === 'elements') return `${path}: expected array`;\n\n if (\n lastKey === 'properties' ||\n lastKey === 'optionalProperties' ||\n lastKey === 'values'\n ) {\n return `${path}: expected object`;\n }\n\n if (lastKey === 'discriminator')\n return `${path}: expected string discriminator`;\n\n if (lastKey === 'mapping') {\n const discValue = resolveInstanceValue(value, instancePath);\n return `${path}: unknown variant \"${discValue}\"`;\n }\n\n if (schemaPath.length === 0) return `${path}: unexpected property`;\n\n return `${path}: validation error at /${schemaPath.join('/')}`;\n}\n\n/**\n * Validate a value against a JTD schema, returning an array of error messages.\n * Delegates to the `jtd` npm package for spec-compliant validation.\n */\nexport function validateJsonAgainstJTD(\n schema: JTDSchemaJson,\n value: unknown,\n definitions?: Record<string, JTDSchemaJson>\n): string[] {\n const defs =\n definitions ??\n (schema.definitions as Record<string, JTDSchemaJson> | undefined);\n\n const schemaForValidation = defs\n ? ({ ...schema, definitions: defs } as Schema)\n : (schema as Schema);\n\n // The jtd lib throws on invalid schemas (e.g., unresolved refs).\n if (!isValidSchema(schemaForValidation)) {\n if (schema.ref) {\n return [`/: unknown ref \"${schema.ref}\"`];\n }\n return [`/: invalid schema`];\n }\n\n const errors = jtdValidate(schemaForValidation, value, {\n maxDepth: MAX_VALIDATION_DEPTH,\n maxErrors: MAX_VALIDATION_ERRORS\n });\n\n return errors.map((err) =>\n formatValidationError(\n err,\n schemaForValidation as Record<string, unknown>,\n value\n )\n );\n}\n\n// \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\n// SIGNAL-LEVEL VALIDATION (JSON values against JTD formats)\n// \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\n\n/** Validate a JSON value against a single SignalFormatJson. */\nexport function validateJsonAgainstSignal(\n format: SignalFormatJson,\n jsonValue: unknown\n): string[];\n/** Validate a JSON value against a SignalDescriptorJson (valid if value matches ANY JTD format). */\nexport function validateJsonAgainstSignal(\n descriptor: SignalDescriptorJson,\n jsonValue: unknown\n): string[];\nexport function validateJsonAgainstSignal(\n formatOrDescriptor: SignalFormatJson | SignalDescriptorJson,\n jsonValue: unknown\n): string[] {\n // Descriptor path \u2014 has `formats` array\n if (\n 'formats' in formatOrDescriptor &&\n Array.isArray(formatOrDescriptor.formats)\n ) {\n const jtdFormats = (\n formatOrDescriptor.formats as SignalFormatJson[]\n ).filter((f) => f.jtdSchema);\n if (jtdFormats.length === 0) {\n return ['/: no JTD formats in signal descriptor'];\n }\n // Valid if value matches ANY JTD format\n for (const format of jtdFormats) {\n const errors = validateJsonAgainstJTD(format.jtdSchema!, jsonValue);\n if (errors.length === 0) return [];\n }\n // None matched \u2014 return errors from last format attempt\n return validateJsonAgainstJTD(\n jtdFormats[jtdFormats.length - 1]!.jtdSchema!,\n jsonValue\n );\n }\n\n // Single format path\n const format = formatOrDescriptor as SignalFormatJson;\n if (!format.jtdSchema) {\n return ['/: signal format has no jtdSchema'];\n }\n return validateJsonAgainstJTD(format.jtdSchema, jsonValue);\n}\n\n// \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\n// JTD \u2192 JSON SCHEMA CONVERTER\n// \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\n\n/**\n * Convert JTD (JSON Type Definition) schema to JSON Schema for validation.\n *\n * Supports all 8 JTD schema forms per RFC 8927:\n * - Empty form: {}\n * - Ref form: { ref: \"...\" }\n * - Type form: { type: \"...\" }\n * - Enum form: { enum: [...] }\n * - Elements form: { elements: {...} }\n * - Properties form: { properties: {...}, optionalProperties: {...} }\n * - Values form: { values: {...} }\n * - Discriminator form: { discriminator: \"...\", mapping: {...} }\n */\nexport function jtdToJsonSchema(\n jtdSchema: JTDSchemaJson,\n definitions?: Record<string, JTDSchemaJson>,\n isRoot = true\n): any {\n if (!jtdSchema || typeof jtdSchema !== 'object') {\n return {};\n }\n\n const isNullable = jtdSchema['nullable'];\n const defs =\n definitions ||\n (jtdSchema['definitions'] as Record<string, JTDSchemaJson> | undefined);\n\n const result: any = {};\n\n if (isRoot) {\n result.$schema = 'http://json-schema.org/draft-07/schema#';\n }\n\n // Ref form\n if (jtdSchema['ref']) {\n const refName = jtdSchema['ref'] as string;\n if (defs && defs[refName]) {\n const resolvedSchema = jtdToJsonSchema(\n defs[refName] as JTDSchemaJson,\n defs,\n false\n );\n\n if (isNullable) {\n if (resolvedSchema.type) {\n resolvedSchema.type = Array.isArray(resolvedSchema.type)\n ? [...resolvedSchema.type, 'null']\n : [resolvedSchema.type, 'null'];\n } else {\n return {\n ...resolvedSchema,\n anyOf: [resolvedSchema, { type: 'null' }],\n ...(isRoot\n ? { $schema: 'http://json-schema.org/draft-07/schema#' }\n : {})\n };\n }\n }\n\n return isRoot\n ? {\n ...resolvedSchema,\n $schema: 'http://json-schema.org/draft-07/schema#'\n }\n : resolvedSchema;\n }\n return isRoot ? result : {};\n }\n\n // Type form\n if (jtdSchema['type']) {\n const typeMap: Record<string, string> = {\n boolean: 'boolean',\n string: 'string',\n timestamp: 'string',\n float32: 'number',\n float64: 'number',\n int8: 'integer',\n uint8: 'integer',\n int16: 'integer',\n uint16: 'integer',\n int32: 'integer',\n uint32: 'integer'\n };\n const jtdType = jtdSchema['type'] as string;\n const baseType = typeMap[jtdType] || 'string';\n\n result.type = isNullable ? [baseType, 'null'] : baseType;\n\n if (baseType === 'string' && jtdType === 'timestamp') {\n result.format = 'date-time';\n }\n return result;\n }\n\n // Enum form\n if (jtdSchema['enum']) {\n result.type = isNullable ? ['string', 'null'] : 'string';\n result.enum = isNullable\n ? [...(jtdSchema['enum'] as string[]), null]\n : jtdSchema['enum'];\n return result;\n }\n\n // Elements form\n if (jtdSchema['elements']) {\n result.type = isNullable ? ['array', 'null'] : 'array';\n result.items = jtdToJsonSchema(\n jtdSchema['elements'] as JTDSchemaJson,\n defs,\n false\n );\n return result;\n }\n\n // Properties form\n if (jtdSchema['properties'] || jtdSchema['optionalProperties']) {\n result.type = isNullable ? ['object', 'null'] : 'object';\n result.properties = {};\n result.required = [];\n\n if (jtdSchema['properties']) {\n for (const [key, value] of Object.entries(jtdSchema['properties'])) {\n result.properties[key] = jtdToJsonSchema(\n value as JTDSchemaJson,\n defs,\n false\n );\n result.required.push(key);\n }\n }\n\n if (jtdSchema['optionalProperties']) {\n for (const [key, value] of Object.entries(\n jtdSchema['optionalProperties']\n )) {\n result.properties[key] = jtdToJsonSchema(\n value as JTDSchemaJson,\n defs,\n false\n );\n }\n }\n\n // JTD is strict by default\n if (jtdSchema['additionalProperties'] !== true) {\n result.additionalProperties = false;\n }\n\n return result;\n }\n\n // Values form\n if (jtdSchema['values']) {\n result.type = isNullable ? ['object', 'null'] : 'object';\n result.additionalProperties = jtdToJsonSchema(\n jtdSchema['values'] as JTDSchemaJson,\n defs,\n false\n );\n return result;\n }\n\n // Discriminator form\n if (jtdSchema['discriminator'] && jtdSchema['mapping']) {\n const variants = [];\n const discriminatorKey = jtdSchema['discriminator'] as string;\n\n for (const [tag, schema] of Object.entries(jtdSchema['mapping'])) {\n const variantSchema = jtdToJsonSchema(\n schema as JTDSchemaJson,\n defs,\n false\n );\n\n if (!variantSchema.type) {\n variantSchema.type = 'object';\n } else if (\n variantSchema.type !== 'object' &&\n !Array.isArray(variantSchema.type)\n ) {\n variantSchema.type = 'object';\n }\n\n variantSchema.properties = variantSchema.properties || {};\n variantSchema.properties[discriminatorKey] = { const: tag };\n\n variantSchema.required = variantSchema.required || [];\n if (!variantSchema.required.includes(discriminatorKey)) {\n variantSchema.required.unshift(discriminatorKey);\n }\n\n // JTD discriminator variants are strict by default\n if (variantSchema.additionalProperties === undefined) {\n variantSchema.additionalProperties = false;\n }\n\n variants.push(variantSchema);\n }\n\n if (isNullable) {\n variants.push({ type: 'null' });\n }\n\n result.oneOf = variants;\n return result;\n }\n\n // Empty form\n return result;\n}\n\n// \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\n// JTD SCHEMA VALIDATION (using the `jtd` npm package)\n// \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\n\nexport const MAX_VALIDATION_DEPTH = 32;\nexport const MAX_VALIDATION_ERRORS = 100;\n\n/** Validate that the input is a valid JTD schema. */\nexport function validateJTDSchema(\n schema: unknown\n): { valid: true; schema: JTDSchemaJson } | { valid: false; error: string } {\n if (!isSchema(schema)) {\n return {\n valid: false,\n error: 'Invalid JTD schema: Schema does not match JTD structure'\n };\n }\n\n if (!isValidSchema(schema)) {\n return {\n valid: false,\n error:\n 'Invalid JTD schema: Schema has semantic errors (e.g., circular references, conflicting forms)'\n };\n }\n\n return { valid: true, schema: schema as unknown as JTDSchemaJson };\n}\n\n/** Parse and validate a JSON string. */\nexport function validateJSON(jsonString: string): {\n valid: boolean;\n data?: unknown;\n error?: string;\n} {\n try {\n const data = JSON.parse(jsonString);\n return { valid: true, data };\n } catch (error) {\n return {\n valid: false,\n error: `Invalid JSON: ${error instanceof Error ? error.message : 'Parse error'}`\n };\n }\n}\n\n/** Parse a JSON string and validate it as a JTD schema. */\nexport function validateJTDSchemaString(\n schemaString: string\n): { valid: true; schema: JTDSchemaJson } | { valid: false; error: string } {\n const jsonResult = validateJSON(schemaString);\n if (!jsonResult.valid) return { valid: false, error: jsonResult.error! };\n\n return validateJTDSchema(jsonResult.data);\n}\n\n// \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\n// MONACO EDITOR CONFIG FOR JTD SCHEMA EDITING\n// \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\n\n/** Monaco editor JSON schema configuration for JTD schema IntelliSense and validation. */\nexport const JTD_SCHEMA_CONFIG = {\n uri: 'https://jsontypedef.com/schemas/jtd-schema.json',\n fileMatch: ['inmemory://jtd-schema/*.json'],\n schema: {\n $schema: 'http://json-schema.org/draft-07/schema#',\n type: 'object',\n properties: {\n type: {\n description: 'Type form - defines a primitive type',\n enum: [\n 'boolean',\n 'string',\n 'timestamp',\n 'float32',\n 'float64',\n 'int8',\n 'uint8',\n 'int16',\n 'uint16',\n 'int32',\n 'uint32'\n ]\n },\n enum: {\n description: 'Enum form - defines a set of string values',\n type: 'array',\n items: { type: 'string' }\n },\n elements: {\n description: 'Elements form - defines an array with typed elements',\n $ref: '#'\n },\n properties: {\n description:\n 'Properties form - defines an object with required properties',\n type: 'object',\n additionalProperties: { $ref: '#' }\n },\n optionalProperties: {\n description: 'Optional properties - defines optional object properties',\n type: 'object',\n additionalProperties: { $ref: '#' }\n },\n values: {\n description: 'Values form - defines a map/dictionary with typed values',\n $ref: '#'\n },\n discriminator: {\n description: 'Discriminator form - defines a tagged union',\n type: 'string'\n },\n mapping: {\n description: 'Mapping for discriminator form',\n type: 'object',\n additionalProperties: { $ref: '#' }\n },\n ref: {\n description: 'Ref form - references a definition',\n type: 'string'\n },\n definitions: {\n description: 'Top-level definitions for reusable schemas',\n type: 'object',\n additionalProperties: { $ref: '#' }\n },\n nullable: {\n description: 'Allow null values',\n type: 'boolean'\n },\n metadata: {\n description: 'Custom metadata for documentation',\n type: 'object'\n },\n additionalProperties: {\n description: 'Allow additional properties (properties form only)',\n type: 'boolean'\n }\n }\n }\n};\n", "/**\n * Shared utilities for JSON Schema providers (OpenAI, Anthropic, Gemini).\n *\n * Contains constants, types, convert output pipeline, definition pooling,\n * and schema inspection helpers used identically across providers.\n */\nimport type {\n JTDSchemaJson,\n SignalDescriptorJson,\n SignalFormatJson\n} from './api';\nimport { validateJsonAgainstJTD } from './signals';\n\n// \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\n// Constants\n// \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\n\nexport const MAX_REF_DEPTH = 32;\n\nexport const INTEGER_BOUNDS: Record<\n string,\n { minimum: number; maximum: number }\n> = {\n int8: { minimum: -128, maximum: 127 },\n uint8: { minimum: 0, maximum: 255 },\n int16: { minimum: -32768, maximum: 32767 },\n uint16: { minimum: 0, maximum: 65535 },\n int32: { minimum: -2147483648, maximum: 2147483647 },\n uint32: { minimum: 0, maximum: 4294967295 }\n};\n\nexport const FLOAT32_BOUNDS = { minimum: -3.4028235e38, maximum: 3.4028235e38 };\n\nexport const UNSTRUCTURED_DESC =\n 'Must be a valid JSON value encoded as a string. ' +\n \"For objects: '{\\\"key\\\": \\\"value\\\"}', for arrays: '[1, 2]', for primitives: '\\\"hello\\\"' or '42' or 'true' or 'null'.\";\n\nexport const UNSTRUCTURED_VARIANT_DESC =\n 'The variant data encoded as a valid JSON string ' +\n '(e.g. \\'{\"key\": \"value\"}\\').';\n\nexport const VALUES_ARRAY_DESC =\n 'Array of key-value entries representing a map/dictionary. ' +\n \"Each entry has a string 'key' (the property name) and a 'value' (the property value).\";\n\n// \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\n// Primitives\n// \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\n\nexport function nullableType(\n baseType: string,\n nullable: boolean\n): string | string[] {\n return nullable ? [baseType, 'null'] : baseType;\n}\n\n// \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\n// Deep equal\n// \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\n\nexport function deepEqual(a: unknown, b: unknown): boolean {\n if (a === b) return true;\n if (a == null || b == null || typeof a !== typeof b) return false;\n if (typeof a !== 'object') return false;\n if (Array.isArray(a))\n return (\n Array.isArray(b) &&\n a.length === b.length &&\n a.every((v, i) => deepEqual(v, b[i]))\n );\n if (Array.isArray(b)) return false;\n const ao = a as Record<string, unknown>,\n bo = b as Record<string, unknown>;\n const keys = Object.keys(ao);\n if (keys.length !== Object.keys(bo).length) return false;\n for (const k of keys) {\n if (!(k in bo) || !deepEqual(ao[k], bo[k])) return false;\n }\n return true;\n}\n\n// \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\n// JTD schema inspection (OpenAI + Anthropic)\n// \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\n\nexport function isUnstructuredSchema(schema: JTDSchemaJson): boolean {\n if (!schema || typeof schema !== 'object') return true;\n\n if (\n schema.type ||\n schema.enum ||\n schema.elements ||\n schema.values ||\n schema.discriminator ||\n schema.ref\n ) {\n return false;\n }\n\n const hasRealProps =\n (schema.properties && Object.keys(schema.properties).length > 0) ||\n (schema.optionalProperties &&\n Object.keys(schema.optionalProperties).length > 0);\n if (hasRealProps) return false;\n\n if (\n (schema.properties !== undefined ||\n schema.optionalProperties !== undefined) &&\n schema.additionalProperties === true\n ) {\n return true;\n }\n\n if (\n schema.properties !== undefined ||\n schema.optionalProperties !== undefined\n ) {\n return false;\n }\n\n return true;\n}\n\nexport function isRootUnstructured(\n schema: JTDSchemaJson,\n defs: Record<string, JTDSchemaJson> | undefined\n): boolean {\n let resolved = schema;\n let depth = 0;\n while (resolved.ref && depth < MAX_REF_DEPTH) {\n const target = defs?.[resolved.ref];\n if (!target) return false;\n resolved = target;\n depth++;\n }\n if (depth >= MAX_REF_DEPTH) return false;\n return isUnstructuredSchema(resolved);\n}\n\n// \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\n// Convert result types\n// \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\n\nexport interface ConvertSuccessJson {\n success: true;\n kind: 'json';\n data: unknown;\n}\n\nexport interface ConvertSuccessBinary {\n success: true;\n kind: 'binary';\n mimeType: string;\n data: Uint8Array;\n filename?: string;\n}\n\nexport interface ConvertSuccessSignals {\n success: true;\n kind: 'signals';\n signals: Record<string, unknown>;\n}\n\nexport interface ConvertFailure {\n success: false;\n errors: string[];\n}\n\nexport type ConvertSuccess =\n | ConvertSuccessJson\n | ConvertSuccessBinary\n | ConvertSuccessSignals;\nexport type ConvertResult = ConvertSuccess | ConvertFailure;\n\n// \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\n// Convert output pipeline (identical across all 3 providers)\n// \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\n\nexport function convertJTDOutput(\n jtdSchema: JTDSchemaJson,\n json: unknown,\n coerce: (\n value: unknown,\n schema: JTDSchemaJson,\n defs: Record<string, JTDSchemaJson> | undefined,\n depth: number\n ) => unknown\n): ConvertResult {\n const defs = jtdSchema.definitions;\n const coerced = coerce(json, jtdSchema, defs, 0);\n const errors = validateJsonAgainstJTD(jtdSchema, coerced, defs);\n if (errors.length > 0) return { success: false, errors };\n return { success: true, kind: 'json', data: coerced };\n}\n\nexport function convertMimeOutput(\n mimeType: string,\n json: unknown\n): ConvertResult {\n if (typeof json !== 'object' || json === null) {\n return {\n success: false,\n errors: [\n `/: expected object with 'data' field for MIME type ${mimeType}, got ${typeof json}`\n ]\n };\n }\n const rec = json as Record<string, unknown>;\n if (typeof rec.data !== 'string') {\n return {\n success: false,\n errors: [\n `/data: expected base64 string for MIME type ${mimeType}, got ${typeof rec.data}`\n ]\n };\n }\n const result: ConvertSuccessBinary = {\n success: true,\n kind: 'binary',\n mimeType,\n data: new Uint8Array(Buffer.from(rec.data, 'base64'))\n };\n if (typeof rec.filename === 'string' && rec.filename) {\n result.filename = rec.filename;\n }\n return result;\n}\n\nexport function convertSingleFormatOutput(\n fmt: SignalFormatJson,\n json: unknown,\n coerce: (\n value: unknown,\n schema: JTDSchemaJson,\n defs: Record<string, JTDSchemaJson> | undefined,\n depth: number\n ) => unknown\n): ConvertResult {\n if (fmt.mimeType) return convertMimeOutput(fmt.mimeType, json);\n if (fmt.jtdSchema) return convertJTDOutput(fmt.jtdSchema, json, coerce);\n return {\n success: false,\n errors: ['Signal format has neither jtdSchema nor mimeType']\n };\n}\n\nexport function convertSignalOutput(\n descriptor: SignalDescriptorJson,\n json: unknown,\n coerce: (\n value: unknown,\n schema: JTDSchemaJson,\n defs: Record<string, JTDSchemaJson> | undefined,\n depth: number\n ) => unknown\n): ConvertResult {\n const formats = descriptor.formats as SignalFormatJson[] | undefined;\n if (!formats || formats.length === 0) {\n return { success: false, errors: ['Signal descriptor has no formats'] };\n }\n\n if (formats.length === 1) {\n return convertSingleFormatOutput(formats[0], json, coerce);\n }\n\n if (typeof json !== 'object' || json === null) {\n return {\n success: false,\n errors: [`/: expected object with format properties, got ${typeof json}`]\n };\n }\n\n const rec = json as Record<string, unknown>;\n for (let i = 0; i < formats.length; i++) {\n const key = `format_${i}`;\n if (rec[key] !== undefined && rec[key] !== null) {\n return convertSingleFormatOutput(formats[i], rec[key], coerce);\n }\n }\n\n if (descriptor.optional) {\n return { success: true, kind: 'json', data: null };\n }\n\n const keys = formats.map((_, i) => `format_${i}`);\n return {\n success: false,\n errors: [\n `Signal '${descriptor.name ?? 'unnamed'}' is required but no format was provided. Set exactly one of: ${keys.join(', ')}.`\n ]\n };\n}\n\nexport function convertSignalsOutput(\n descriptors: SignalDescriptorJson[],\n json: unknown,\n coerce: (\n value: unknown,\n schema: JTDSchemaJson,\n defs: Record<string, JTDSchemaJson> | undefined,\n depth: number\n ) => unknown\n): ConvertResult {\n if (typeof json !== 'object' || json === null) {\n return {\n success: false,\n errors: ['/: expected object with signal properties, got ' + typeof json]\n };\n }\n\n const rec = json as Record<string, unknown>;\n const signals: Record<string, unknown> = {};\n const errors: string[] = [];\n\n for (const desc of descriptors) {\n if (!desc.name) continue;\n const value = rec[desc.name];\n if (value === undefined || value === null) continue;\n\n const result = convertSignalOutput(desc, value, coerce);\n if (!result.success) {\n for (const err of result.errors) {\n errors.push(`/${desc.name}${err.startsWith('/') ? '' : ': '}${err}`);\n }\n } else if (result.kind === 'binary') {\n signals[desc.name] = {\n data: result.data,\n mimeType: result.mimeType,\n filename: result.filename\n };\n } else if (result.kind === 'json') {\n signals[desc.name] = result.data;\n }\n }\n\n if (errors.length > 0) return { success: false, errors };\n return { success: true, kind: 'signals', signals };\n}\n\n// \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\n// Schema tree walk (for providers that use $ref/$defs)\n// \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\n\n/** Minimal shape for $ref-bearing schemas (satisfied by OpenAISchema and AnthropicSchema). */\ninterface RefNode {\n properties?: Record<string, RefNode>;\n items?: RefNode;\n anyOf?: RefNode[];\n $defs?: Record<string, RefNode>;\n $ref?: string;\n}\n\nexport function walkSchema<T extends RefNode>(\n schema: T,\n fn: (node: T) => void\n): void {\n fn(schema);\n if (schema.properties)\n for (const v of Object.values(schema.properties)) walkSchema(v as T, fn);\n if (schema.items) walkSchema(schema.items as T, fn);\n if (schema.anyOf) for (const v of schema.anyOf) walkSchema(v as T, fn);\n if (schema.$defs)\n for (const v of Object.values(schema.$defs)) walkSchema(v as T, fn);\n}\n\n// \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\n// Definition pooling for fromSignals (OpenAI + Anthropic)\n// \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\n\n/** Recursively rewrite JTD `ref` values in a schema according to a rename map. */\nfunction rewriteJtdRefs(\n schema: JTDSchemaJson,\n renames: Map<string, string>\n): JTDSchemaJson {\n if (!schema || typeof schema !== 'object') return schema;\n\n const result: JTDSchemaJson = { ...schema };\n\n if (result.ref && renames.has(result.ref)) {\n result.ref = renames.get(result.ref)!;\n }\n if (result.properties) {\n result.properties = Object.fromEntries(\n Object.entries(result.properties).map(([k, v]) => [\n k,\n rewriteJtdRefs(v, renames)\n ])\n );\n }\n if (result.optionalProperties) {\n result.optionalProperties = Object.fromEntries(\n Object.entries(result.optionalProperties).map(([k, v]) => [\n k,\n rewriteJtdRefs(v, renames)\n ])\n );\n }\n if (result.elements)\n result.elements = rewriteJtdRefs(result.elements, renames);\n if (result.values) result.values = rewriteJtdRefs(result.values, renames);\n if (result.mapping) {\n result.mapping = Object.fromEntries(\n Object.entries(result.mapping).map(([k, v]) => [\n k,\n rewriteJtdRefs(v, renames)\n ])\n );\n }\n // Definitions are rewritten at pool level, strip from the copy\n if (result.definitions) delete result.definitions;\n\n return result;\n}\n\n/**\n * Pre-scan all signal descriptors, pool their JTD definitions.\n * Deduplicates identical definition SETS; renames conflicts with `_N` suffix.\n *\n * Equivalence is checked per-format: all definitions within a single format's\n * `definitions` block are treated as a unit. Two formats with the same set of\n * definition names and identical schemas (deep-equal) share their defs.\n * If ANY definition name conflicts (same name, different schema), ALL defs\n * from that format get their own namespace to preserve internal cross-references.\n *\n * Returns the merged JTD definitions and signal copies with:\n * - refs rewritten to match merged names\n * - definitions stripped (they live in the merged pool now)\n */\nexport function poolSignalDefinitions(signals: SignalDescriptorJson[]): {\n mergedDefs: Record<string, JTDSchemaJson>;\n signalsCopy: SignalDescriptorJson[];\n} {\n const mergedDefs: Record<string, JTDSchemaJson> = {};\n\n // For each format that has definitions, compute its rename map.\n // Key: `signalIdx:formatIdx`, Value: Map<oldName, newName>\n const renamesByFormat = new Map<string, Map<string, string>>();\n\n for (let si = 0; si < signals.length; si++) {\n const formats = signals[si].formats as SignalFormatJson[] | undefined;\n if (!formats) continue;\n for (let fi = 0; fi < formats.length; fi++) {\n const defs = formats[fi].jtdSchema?.definitions;\n if (!defs || Object.keys(defs).length === 0) continue;\n\n // Check each definition against the merged pool\n const renames = new Map<string, string>();\n let hasConflict = false;\n\n for (const [name, defSchema] of Object.entries(defs)) {\n const existing = mergedDefs[name];\n if (!existing) continue; // new name, no conflict\n if (deepEqual(existing, defSchema)) continue; // identical, dedup\n hasConflict = true;\n break;\n }\n\n if (hasConflict) {\n // This format has at least one conflict. Rename ALL its definitions\n // to avoid broken cross-references between shared and renamed defs.\n for (const [name, defSchema] of Object.entries(defs)) {\n const existing = mergedDefs[name];\n if (existing && deepEqual(existing, defSchema)) {\n // Identical to existing \u2014 can still share\n continue;\n }\n if (existing) {\n // Conflict \u2014 find unique name\n let n = 1;\n while (mergedDefs[`${name}_${n}`]) n++;\n const newName = `${name}_${n}`;\n renames.set(name, newName);\n mergedDefs[newName] = defSchema;\n } else {\n mergedDefs[name] = defSchema;\n }\n }\n } else {\n // No conflicts \u2014 all defs are either new or identical (deduped)\n for (const [name, defSchema] of Object.entries(defs)) {\n if (!mergedDefs[name]) mergedDefs[name] = defSchema;\n }\n }\n\n if (renames.size > 0) renamesByFormat.set(`${si}:${fi}`, renames);\n }\n }\n\n // Rewrite refs inside renamed definitions (they may reference other renamed defs)\n for (const [, renames] of renamesByFormat) {\n for (const [, newName] of renames) {\n mergedDefs[newName] = rewriteJtdRefs(mergedDefs[newName], renames);\n }\n }\n\n // Build signal copies with rewritten refs and stripped definitions\n const signalsCopy: SignalDescriptorJson[] = signals.map((signal, si) => {\n const formats = signal.formats as SignalFormatJson[] | undefined;\n if (!formats) return signal;\n\n let hasChanges = false;\n const newFormats = formats.map((fmt, fi) => {\n if (!fmt.jtdSchema) return fmt;\n const renames = renamesByFormat.get(`${si}:${fi}`);\n const hasDefs =\n fmt.jtdSchema.definitions &&\n Object.keys(fmt.jtdSchema.definitions).length > 0;\n if (!renames && !hasDefs) return fmt;\n\n hasChanges = true;\n const rewritten = renames\n ? rewriteJtdRefs(fmt.jtdSchema, renames)\n : { ...fmt.jtdSchema };\n delete rewritten.definitions;\n return { ...fmt, jtdSchema: rewritten };\n });\n\n return hasChanges ? { ...signal, formats: newFormats } : signal;\n });\n\n return { mergedDefs, signalsCopy };\n}\n\n// \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\n// Cycle description \u2014 shared by Anthropic and Gemini for recursive ref fallbacks\n// \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\n\nfunction describeJTDType(schema: JTDSchemaJson): string {\n if (!schema || typeof schema !== 'object') return 'any';\n if (schema.ref) return schema.ref;\n if (schema.type) return schema.type;\n if (schema.enum) return `one of: ${schema.enum.join(', ')}`;\n if (schema.elements) return `array of ${describeJTDType(schema.elements)}`;\n if (schema.values) return `map of ${describeJTDType(schema.values)}`;\n if (schema.properties || schema.optionalProperties) return 'object';\n if (schema.discriminator) return `union on '${schema.discriminator}'`;\n return 'any';\n}\n\n/**\n * Build a human-readable description of a JTD definition for use as a\n * fallback at recursive ref break points. Used by both Anthropic and Gemini\n * converters when a cycle is detected.\n */\nexport function describeDefinitionForCycle(\n defName: string,\n def: JTDSchemaJson\n): string {\n const parts: string[] = [\n `Recursive nesting. This field accepts the same object structure as its parent with the following shape:`\n ];\n\n const required: string[] = [];\n const optional: string[] = [];\n\n if (def.properties) {\n for (const [key, prop] of Object.entries(def.properties)) {\n required.push(`${key} (${describeJTDType(prop)})`);\n }\n }\n if (def.optionalProperties) {\n for (const [key, prop] of Object.entries(def.optionalProperties)) {\n optional.push(`${key} (${describeJTDType(prop)})`);\n }\n }\n\n if (required.length > 0) {\n parts.push(`Required: ${required.join(', ')}.`);\n }\n if (optional.length > 0) {\n const shown = optional.slice(0, 8);\n const suffix =\n optional.length > 8 ? `, and ${optional.length - 8} more` : '';\n parts.push(`Optional: ${shown.join(', ')}${suffix}.`);\n }\n\n if (def.elements) {\n parts.push(`Array of ${describeJTDType(def.elements)}.`);\n }\n if (def.values) {\n parts.push(`Map with values of type ${describeJTDType(def.values)}.`);\n }\n\n return parts.join(' ');\n}\n", "// Copyright 2024-2025 Mochabug, LLC. All rights reserved.\n// Licensed under the Apache License, Version 2.0.\n\nimport type {\n JTDSchemaJson,\n SignalDescriptorJson,\n SignalFormatJson\n} from '../api';\nimport {\n MAX_REF_DEPTH,\n INTEGER_BOUNDS,\n FLOAT32_BOUNDS,\n UNSTRUCTURED_DESC,\n UNSTRUCTURED_VARIANT_DESC,\n VALUES_ARRAY_DESC,\n nullableType,\n isUnstructuredSchema,\n isRootUnstructured,\n poolSignalDefinitions,\n convertJTDOutput as sharedConvertJTDOutput,\n convertSignalOutput as sharedConvertSignalOutput,\n convertSignalsOutput as sharedConvertSignalsOutput,\n type ConvertSuccessJson,\n type ConvertSuccessBinary,\n type ConvertSuccessSignals,\n type ConvertFailure,\n type ConvertSuccess,\n type ConvertResult,\n} from '../schema-utils';\nimport { validateJsonAgainstJTD } from '../signals';\n\n// \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\n// Types\n// \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\n\n/**\n * JSON Schema subset accepted by the OpenAI structured-output API (strict mode).\n *\n * This is the provider-specific representation produced by {@link OpenAIConversion}.\n * It covers the keywords that OpenAI inspects when constraining model output in\n * strict mode: `type`, `properties`, `required`, `additionalProperties`, `enum`,\n * `format`, `items`, `minimum`/`maximum`, `anyOf`, `$defs`, `$ref`, and\n * `description`.\n */\nexport interface OpenAISchema {\n type?: string | string[];\n description?: string;\n properties?: Record<string, OpenAISchema>;\n required?: string[];\n additionalProperties?: false;\n enum?: (string | number | null)[];\n format?: string;\n items?: OpenAISchema;\n minimum?: number;\n maximum?: number;\n anyOf?: OpenAISchema[];\n $defs?: Record<string, OpenAISchema>;\n $ref?: string;\n}\n\n// Re-export shared types for public API compatibility\nexport type {\n ConvertSuccessJson,\n ConvertSuccessBinary,\n ConvertSuccessSignals,\n ConvertFailure,\n ConvertSuccess,\n ConvertResult,\n} from '../schema-utils';\n\n\n// \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\n// Helpers\n// \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\n\n/** Check if a JTD schema is unstructured (empty form or equivalent). */\n\n\n/** Make a schema nullable by adding \"null\" to the type. */\nfunction makeNullable(schema: OpenAISchema): OpenAISchema {\n if (Array.isArray(schema.type) && schema.type.includes('null')) return schema;\n if (schema.type === 'null') return schema;\n\n // $ref \u2192 wrap in anyOf with null\n if (schema.$ref) {\n return { anyOf: [schema, { type: 'null' }] };\n }\n\n // anyOf \u2192 add null variant\n if (schema.anyOf) {\n if (schema.anyOf.some((s) => s.type === 'null')) return schema;\n return { ...schema, anyOf: [...schema.anyOf, { type: 'null' }] };\n }\n\n // Standard: add null to type and enum\n const result = { ...schema };\n if (result.type) {\n result.type = Array.isArray(result.type)\n ? [...result.type, 'null']\n : [result.type, 'null'];\n }\n if (result.enum && !result.enum.includes(null)) {\n result.enum = [...result.enum, null];\n }\n return result;\n}\n\n\n// \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\n// Metadata helper\n// \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\n\nconst METADATA_KNOWN_KEYS = new Set(['title', 'label', 'name', 'description']);\n\nfunction applyMetadata(result: OpenAISchema, schema: JTDSchemaJson): void {\n const meta = schema.metadata as Record<string, unknown> | undefined;\n if (!meta) return;\n\n const descParts: string[] = [];\n\n // OAI doesn't support title \u2014 fold title/label/name into description\n const titleCandidate =\n (typeof meta['title'] === 'string' && meta['title'].trim()) ||\n (typeof meta['label'] === 'string' && meta['label'].trim()) ||\n (typeof meta['name'] === 'string' && meta['name'].trim());\n\n const descText =\n typeof meta['description'] === 'string' ? meta['description'].trim() : '';\n\n if (titleCandidate && descText) {\n descParts.push(`${titleCandidate}. ${descText}`);\n } else if (titleCandidate) {\n descParts.push(titleCandidate);\n } else if (descText) {\n descParts.push(descText);\n }\n\n // Extra metadata keys as description hints\n const extras: string[] = [];\n for (const [key, value] of Object.entries(meta)) {\n if (METADATA_KNOWN_KEYS.has(key)) continue;\n if (typeof value === 'string' && value.trim()) {\n extras.push(`[${key}: ${value.trim()}]`);\n } else if (typeof value === 'number' || typeof value === 'boolean') {\n extras.push(`[${key}: ${value}]`);\n }\n }\n if (extras.length > 0) {\n descParts.push(extras.join(' '));\n }\n\n if (descParts.length > 0) {\n const newDesc = descParts.join(' ');\n result.description = result.description\n ? `${result.description} ${newDesc}`\n : newDesc;\n }\n}\n\n// \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\n// Schema conversion (JTD \u2192 OpenAI JSON Schema)\n// \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\n\nfunction convertNode(\n schema: JTDSchemaJson,\n defs: Record<string, JTDSchemaJson> | undefined,\n depth: number\n): OpenAISchema {\n if (depth > MAX_REF_DEPTH) {\n throw new Error(`Schema nesting exceeded ${MAX_REF_DEPTH} levels`);\n }\n\n if (!schema || typeof schema !== 'object') {\n return { type: 'string', description: UNSTRUCTURED_DESC };\n }\n\n // Unstructured (empty form or AP:true with no real props) \u2192 string\n if (isUnstructuredSchema(schema)) {\n const result: OpenAISchema = {\n type: nullableType('string', schema.nullable === true),\n description: UNSTRUCTURED_DESC\n };\n applyMetadata(result, schema);\n return result;\n }\n\n const isNullable = schema.nullable === true;\n\n // Ref form \u2192 $ref\n if (schema.ref) {\n const refName = schema.ref;\n if (!defs?.[refName]) {\n throw new Error(`Unresolved ref \"${refName}\"`);\n }\n if (isNullable) {\n const result: OpenAISchema = {\n anyOf: [{ $ref: `#/$defs/${refName}` }, { type: 'null' }]\n };\n applyMetadata(result, schema);\n return result;\n }\n // Bare $ref \u2014 skip metadata (JSON Schema $ref may ignore sibling keywords)\n return { $ref: `#/$defs/${refName}` };\n }\n\n // Type form\n if (schema.type) {\n const result: OpenAISchema = {};\n\n const intBounds =\n INTEGER_BOUNDS[schema.type as keyof typeof INTEGER_BOUNDS];\n if (intBounds) {\n result.type = nullableType('integer', isNullable);\n result.minimum = intBounds.minimum;\n result.maximum = intBounds.maximum;\n } else\n switch (schema.type) {\n case 'boolean':\n result.type = nullableType('boolean', isNullable);\n break;\n case 'string':\n result.type = nullableType('string', isNullable);\n break;\n case 'timestamp':\n result.type = nullableType('string', isNullable);\n result.format = 'date-time';\n break;\n case 'float32':\n result.type = nullableType('number', isNullable);\n result.minimum = FLOAT32_BOUNDS.minimum;\n result.maximum = FLOAT32_BOUNDS.maximum;\n break;\n case 'float64':\n result.type = nullableType('number', isNullable);\n break;\n default:\n throw new Error(`Unknown JTD type \"${schema.type}\"`);\n }\n\n applyMetadata(result, schema);\n return result;\n }\n\n // Enum form\n if (schema.enum) {\n const result: OpenAISchema = {\n type: nullableType('string', isNullable),\n enum: isNullable ? [...schema.enum, null] : schema.enum\n };\n applyMetadata(result, schema);\n return result;\n }\n\n // Elements form\n if (schema.elements) {\n const result: OpenAISchema = {\n type: nullableType('array', isNullable),\n items: convertNode(schema.elements, defs, depth + 1)\n };\n applyMetadata(result, schema);\n return result;\n }\n\n // Properties form (has real props \u2014 unstructured was caught above)\n if (schema.properties || schema.optionalProperties) {\n const props: Record<string, OpenAISchema> = {};\n const required: string[] = [];\n\n if (schema.properties) {\n for (const [key, propSchema] of Object.entries(schema.properties)) {\n props[key] = convertNode(propSchema, defs, depth + 1);\n required.push(key);\n }\n }\n\n // Optional properties \u2192 required + nullable (null means \"not provided\")\n if (schema.optionalProperties) {\n for (const [key, propSchema] of Object.entries(\n schema.optionalProperties\n )) {\n const converted = convertNode(propSchema, defs, depth + 1);\n const nullable = makeNullable(converted);\n nullable.description = nullable.description\n ? `(Optional, null if not provided) ${nullable.description}`\n : 'Optional. Set to null if not provided.';\n props[key] = nullable;\n required.push(key);\n }\n }\n\n const result: OpenAISchema = {\n type: nullableType('object', isNullable),\n properties: props,\n additionalProperties: false,\n required: required\n };\n applyMetadata(result, schema);\n return result;\n }\n\n // Values form \u2192 array of {key, value} objects\n if (schema.values) {\n const valueSchema = convertNode(schema.values, defs, depth + 1);\n const result: OpenAISchema = {\n type: nullableType('array', isNullable),\n description: VALUES_ARRAY_DESC,\n items: {\n type: 'object',\n properties: {\n key: { type: 'string', description: 'The property name.' },\n value: valueSchema\n },\n required: ['key', 'value'],\n additionalProperties: false\n }\n };\n applyMetadata(result, schema);\n return result;\n }\n\n // Discriminator form \u2192 anyOf with flat variants\n if (schema.discriminator && schema.mapping) {\n const discKey = schema.discriminator;\n const mapping = schema.mapping;\n const variants: OpenAISchema[] = [];\n\n for (const [tag, variantSchema] of Object.entries(mapping)) {\n let variantOAI: OpenAISchema;\n\n if (isUnstructuredSchema(variantSchema)) {\n // Unstructured variant: disc + _data string\n variantOAI = {\n type: 'object',\n properties: {\n [discKey]: { type: 'string', enum: [tag] },\n _data: {\n type: 'string',\n description: UNSTRUCTURED_VARIANT_DESC\n }\n },\n required: [discKey, '_data'],\n additionalProperties: false\n };\n } else {\n // Per RFC 8927, mapping values are always properties form,\n // and the discriminator tag never appears in variant properties.\n variantOAI = convertNode(variantSchema, defs, depth + 1);\n variantOAI.properties![discKey] = { type: 'string', enum: [tag] };\n (variantOAI.required ??= []).unshift(discKey);\n variantOAI.additionalProperties = false;\n }\n\n applyMetadata(variantOAI, variantSchema);\n variants.push(variantOAI);\n }\n\n if (isNullable) variants.push({ type: 'null' });\n const result: OpenAISchema = { anyOf: variants };\n applyMetadata(result, schema);\n return result;\n }\n\n // Fallback\n return { type: 'string', description: UNSTRUCTURED_DESC };\n}\n\n// \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\n// Coercion (OpenAI output \u2192 JTD-conforming JSON)\n// \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\n\nconst INTEGER_TYPE_SET = new Set(Object.keys(INTEGER_BOUNDS));\n\nfunction coerceToJTD(\n value: unknown,\n schema: JTDSchemaJson,\n defs: Record<string, JTDSchemaJson> | undefined,\n depth: number\n): unknown {\n if (depth > MAX_REF_DEPTH) return value;\n if (value === null || value === undefined) return value;\n\n // Ref form \u2014 resolve in original JTD definitions and recurse\n if (schema.ref) {\n const resolved = defs?.[schema.ref];\n if (resolved) {\n return coerceToJTD(value, resolved, defs, depth + 1);\n }\n return value;\n }\n\n // Unstructured schema \u2014 parse JSON string back to value\n if (isUnstructuredSchema(schema)) {\n if (typeof value === 'string') {\n try {\n return JSON.parse(value);\n } catch {\n return value;\n }\n }\n return value;\n }\n\n // Type form \u2014 integer coercion\n if (schema.type && INTEGER_TYPE_SET.has(schema.type)) {\n if (typeof value === 'number' && !Number.isInteger(value)) {\n return Math.round(value);\n }\n return value;\n }\n\n // Elements form\n if (schema.elements && Array.isArray(value)) {\n const elemSchema = schema.elements;\n return value.map((item) => coerceToJTD(item, elemSchema, defs, depth + 1));\n }\n\n // Properties form \u2014 strip null optionals\n if (\n (schema.properties || schema.optionalProperties) &&\n typeof value === 'object' &&\n value !== null\n ) {\n const obj = { ...(value as Record<string, unknown>) };\n\n if (schema.properties) {\n for (const [k, propSchema] of Object.entries(schema.properties)) {\n if (k in obj) {\n obj[k] = coerceToJTD(obj[k], propSchema, defs, depth + 1);\n }\n }\n }\n\n // Optional properties: null \u2192 omit\n if (schema.optionalProperties) {\n for (const [k, propSchema] of Object.entries(schema.optionalProperties)) {\n if (k in obj) {\n if (obj[k] === null) {\n delete obj[k];\n } else {\n obj[k] = coerceToJTD(obj[k], propSchema, defs, depth + 1);\n }\n }\n }\n }\n\n return obj;\n }\n\n // Values form \u2014 convert array of {key, value} to Record\n if (schema.values && Array.isArray(value)) {\n const result: Record<string, unknown> = {};\n for (const entry of value) {\n if (typeof entry === 'object' && entry !== null) {\n const rec = entry as Record<string, unknown>;\n const key = rec.key as string;\n result[key] = coerceToJTD(rec.value, schema.values, defs, depth + 1);\n }\n }\n return result;\n }\n\n // Discriminator form \u2014 flat object, coerce variant + strip null optionals\n if (\n schema.discriminator &&\n schema.mapping &&\n typeof value === 'object' &&\n value !== null\n ) {\n const rec = value as Record<string, unknown>;\n const discKey = schema.discriminator;\n const tag = rec[discKey];\n if (typeof tag !== 'string') return value;\n const variantSchema = schema.mapping[tag];\n\n if (!variantSchema) return value;\n\n // Unstructured variant: parse _data\n if (isUnstructuredSchema(variantSchema)) {\n const dataStr = rec._data;\n if (typeof dataStr === 'string') {\n try {\n const parsed = JSON.parse(dataStr);\n if (typeof parsed === 'object' && parsed !== null) {\n return { [discKey]: tag, ...parsed };\n }\n } catch {\n /* validation catches */\n }\n }\n return { [discKey]: tag };\n }\n\n // Build flat result: discriminator + variant properties\n const result: Record<string, unknown> = { [discKey]: tag };\n\n if (variantSchema.properties) {\n for (const [k, propSchema] of Object.entries(variantSchema.properties)) {\n if (k in rec) {\n result[k] = coerceToJTD(rec[k], propSchema, defs, depth + 1);\n }\n }\n }\n\n if (variantSchema.optionalProperties) {\n for (const [k, propSchema] of Object.entries(\n variantSchema.optionalProperties\n )) {\n if (k in rec && rec[k] !== null) {\n result[k] = coerceToJTD(rec[k], propSchema, defs, depth + 1);\n }\n }\n }\n\n return result;\n }\n\n return value;\n}\n\n// \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\n// MIME format helpers\n// \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\n\nfunction convertMimeFormat(mimeType: string): OpenAISchema {\n return {\n type: 'object',\n description: `Binary content (MIME type: ${mimeType}).`,\n properties: {\n data: {\n type: 'string',\n description: `The base64-encoded binary content (MIME type: ${mimeType}).`\n },\n filename: {\n type: ['string', 'null'],\n description:\n 'Optional filename for the binary content (e.g. \"report.pdf\"). Set to null if not applicable.'\n }\n },\n required: ['data', 'filename'],\n additionalProperties: false\n };\n}\n\n// \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\n// Public API\n// \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\n\n/**\n * Converts JTD schemas or Adapt signal descriptors into OpenAI-compatible\n * JSON Schema (strict mode), and converts raw LLM output back into\n * validated data.\n *\n * ### JTD form mapping\n * - **type** -- mapped to the corresponding JSON Schema `type` (`string`,\n * `number`, `integer`, `boolean`). Integer sub-types (int8 .. uint32) add\n * `minimum`/`maximum` bounds. `timestamp` becomes `string` with\n * `format: \"date-time\"`.\n * - **enum** -- mapped to `type: \"string\"` with an `enum` array.\n * - **elements** -- mapped to `type: \"array\"` with `items`.\n * - **properties / optionalProperties** -- mapped to `type: \"object\"` with\n * `properties`, `required`, and `additionalProperties: false`. OpenAI\n * strict mode requires all properties to be present, so **optional\n * properties are made required and nullable** (their schemas are wrapped\n * with a `\"null\"` type variant). During {@link convert}, null values for\n * optional properties are stripped back out.\n * - **values** -- mapped to an array of `{key, value}` objects because\n * OpenAI strict mode does not support `additionalProperties`. During\n * {@link convert}, the array is coerced back into a plain `Record`.\n * - **discriminator** -- mapped to `anyOf` with one variant per mapping\n * entry. Each variant is a flat object containing the discriminator key\n * (with a single-value `enum`) plus the variant's own properties.\n *\n * ### Unstructured schemas\n * Empty JTD schemas or schemas with only `additionalProperties: true` are\n * considered unstructured. They produce an empty `{}` JSON Schema with\n * `strict: false`, and during {@link convert} the raw string output is\n * parsed via `JSON.parse` back into an arbitrary value.\n *\n * ### MIME formats\n * MIME signal formats become `type: \"string\"` with a description requesting\n * base64-encoded content. On {@link convert}, the base64 string is decoded\n * into a {@link ConvertSuccessBinary} result.\n *\n * ### Serialization\n * `JSON.stringify(conv)` produces a JSON-safe object via {@link toJSON}.\n * Use {@link fromJSON} to restore a fully functional instance from the\n * serialized form (string or parsed object).\n *\n * ```ts\n * const conv = OpenAIConversion.fromJTD(jtdSchema);\n * conv.schema; // OpenAI JSON Schema to pass to the LLM\n * conv.strict; // whether strict mode applies\n * conv.convert(output); // coerce + validate -> ConvertResult\n *\n * // Serialize & restore\n * const cached = JSON.stringify(conv);\n * const restored = OpenAIConversion.fromJSON(cached);\n * ```\n */\nexport class OpenAIConversion<T extends ConvertSuccess = ConvertSuccess> {\n /** The OpenAI-specific JSON Schema to pass to the OpenAI API as the response format. */\n readonly schema: OpenAISchema;\n /**\n * Whether the schema qualifies for OpenAI strict structured-output mode.\n *\n * This is `false` when the root JTD schema is empty or unstructured (e.g.\n * the empty schema `{}` or a properties form with no real properties and\n * `additionalProperties: true`). In that case `schema` is `{}` and the\n * model output is unconstrained.\n */\n readonly strict: boolean;\n /**\n * The original JTD schema used to build this conversion.\n * Set when created via {@link fromJTD}; `undefined` when created via {@link fromSignal}.\n */\n readonly jtdSchema?: JTDSchemaJson;\n /**\n * The original signal descriptor used to build this conversion.\n * Set when created via {@link fromSignal}; `undefined` when created via {@link fromJTD}.\n */\n readonly descriptor?: SignalDescriptorJson;\n /**\n * The original signal descriptors used to build this conversion.\n * Set when created via {@link fromSignals}; `undefined` otherwise.\n */\n readonly descriptors?: SignalDescriptorJson[];\n\n // \u2500\u2500 Factories \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\n /**\n * Build a conversion from a JTD schema.\n *\n * Recursively converts every JTD form into the equivalent OpenAI JSON Schema\n * node. Definitions (`jtdSchema.definitions`) are emitted as `$defs` at the\n * root level, and references become `$ref` pointers.\n *\n * If the root schema is unstructured, the result has an empty `schema` and\n * `strict: false`.\n *\n * @param jtdSchema - A valid JTD schema (RFC 8927).\n * @returns A new {@link OpenAIConversion} with `jtdSchema` set.\n * @throws If the schema contains an unknown JTD type, an unresolved `ref`,\n * or nesting exceeding 32 levels.\n */\n static fromJTD(\n jtdSchema: JTDSchemaJson\n ): OpenAIConversion<ConvertSuccessJson> {\n const defs = jtdSchema.definitions;\n const strict = !isRootUnstructured(jtdSchema, defs);\n\n let schema: OpenAISchema;\n if (strict) {\n schema = convertNode(jtdSchema, defs, 0);\n\n // Attach $defs at root\n if (defs && Object.keys(defs).length > 0) {\n schema.$defs = Object.fromEntries(\n Object.entries(defs).map(([name, defSchema]) => [\n name,\n convertNode(defSchema, defs, 0)\n ])\n );\n }\n } else {\n schema = {};\n applyMetadata(schema, jtdSchema);\n }\n\n return new OpenAIConversion(schema, strict, jtdSchema, undefined);\n }\n\n /**\n * Build a conversion from an Adapt signal descriptor.\n *\n * If the descriptor has a single format, its schema is used directly\n * (unwrapped). If it has multiple formats, a wrapper object schema is\n * produced with `format_0`, `format_1`, ... properties -- each made\n * required and nullable so the LLM populates exactly one and sets the\n * rest to `null`.\n *\n * @param descriptor - An Adapt signal descriptor with one or more formats.\n * @returns A new {@link OpenAIConversion} with `descriptor` set.\n * @throws If the descriptor has no formats, or a format has neither\n * `jtdSchema` nor `mimeType`.\n */\n static fromSignal(\n descriptor: SignalDescriptorJson,\n sharedDefs?: Record<string, JTDSchemaJson>\n ): OpenAIConversion<ConvertSuccessJson | ConvertSuccessBinary> {\n const { schema, strict } = buildSignalSchema(descriptor, sharedDefs);\n return new OpenAIConversion(schema, strict, undefined, descriptor);\n }\n\n /**\n * Build a conversion from multiple signal descriptors.\n *\n * Produces an object schema with one property per signal, suitable for\n * use as structured output parameters. Each signal's schema is built via\n * {@link fromSignal} internally.\n *\n * For OpenAI strict mode, optional signals are made nullable and all\n * properties are added to `required`.\n *\n * If there is only one signal with a single format, the schema is\n * unwrapped to avoid unnecessary nesting \u2014 the signal's format schema\n * is used directly as the object property value.\n *\n * Receiver/transceiver compatible:\n * ```ts\n * const conv = OpenAIConversion.fromSignals(\n * receiver.signals!,\n * receiver.description,\n * receiver.name\n * );\n * ```\n *\n * @param signals - Array of signal descriptors.\n * @param description - Optional description for the compound schema.\n * @param label - Optional label (not used by OpenAI, reserved for other providers).\n * @returns A new {@link OpenAIConversion} with `descriptors` set.\n * @throws If any signal has no formats or an invalid format.\n */\n static fromSignals(\n signals: SignalDescriptorJson[],\n description?: string,\n label?: string\n ): OpenAIConversion<ConvertSuccessSignals> {\n // Pool all JTD definitions across all signals \u2014 dedup identical, rename conflicts\n const { mergedDefs, signalsCopy } = poolSignalDefinitions(signals);\n\n const properties: Record<string, OpenAISchema> = {};\n const required: string[] = [];\n let allStrict = true;\n\n for (const signal of signalsCopy) {\n if (!signal.name) continue;\n // Pass merged defs so buildFormatSchema uses them for ref resolution\n // and does NOT attach $defs at the signal level\n const conv = OpenAIConversion.fromSignal(signal, mergedDefs);\n if (!conv.strict) allStrict = false;\n\n if (signal.optional) {\n properties[signal.name] = makeNullable(conv.schema);\n } else {\n properties[signal.name] = conv.schema;\n }\n required.push(signal.name);\n }\n\n const schema: OpenAISchema = {\n type: 'object',\n properties,\n required,\n additionalProperties: false\n };\n\n // Attach pooled $defs at root \u2014 the only place they should ever live\n if (Object.keys(mergedDefs).length > 0) {\n schema.$defs = Object.fromEntries(\n Object.entries(mergedDefs).map(([name, defSchema]) => [\n name,\n convertNode(defSchema, mergedDefs, 0)\n ])\n );\n }\n\n if (label && description) {\n schema.description = `${label}. ${description}`;\n } else if (label) {\n schema.description = label;\n } else if (description) {\n schema.description = description;\n }\n\n return new OpenAIConversion(\n schema,\n allStrict,\n undefined,\n undefined,\n signals // original signals for serialization (fromJSON recreates)\n );\n }\n\n /**\n * Restore an {@link OpenAIConversion} from its serialized form.\n *\n * Accepts either the JSON string produced by `JSON.stringify(conv)` or the\n * already-parsed plain object. The original `jtdSchema`, `descriptor`, or\n * `descriptors` is re-processed through the corresponding factory, so the\n * restored instance is fully functional.\n *\n * @param data - A JSON string or parsed object previously produced by {@link toJSON}.\n * @returns A fully reconstructed {@link OpenAIConversion}.\n * @throws If the serialized data contains neither `jtdSchema`, `descriptor`, nor `descriptors`.\n */\n static fromJSON(data: string | Record<string, unknown>): OpenAIConversion {\n const parsed = typeof data === 'string' ? JSON.parse(data) : data;\n if (parsed.descriptors) {\n return OpenAIConversion.fromSignals(\n parsed.descriptors as SignalDescriptorJson[],\n parsed.description as string | undefined,\n parsed.label as string | undefined\n );\n }\n if (parsed.descriptor) {\n return OpenAIConversion.fromSignal(\n parsed.descriptor as SignalDescriptorJson\n );\n }\n if (parsed.jtdSchema) {\n return OpenAIConversion.fromJTD(parsed.jtdSchema as JTDSchemaJson);\n }\n throw new Error(\n 'Cannot deserialize: missing jtdSchema, descriptor, or descriptors'\n );\n }\n\n // \u2500\u2500 Constructor (private) \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\n private constructor(\n schema: OpenAISchema,\n strict: boolean,\n jtdSchema: JTDSchemaJson | undefined,\n descriptor: SignalDescriptorJson | undefined,\n descriptors?: SignalDescriptorJson[]\n ) {\n this.schema = schema;\n this.strict = strict;\n this.jtdSchema = jtdSchema;\n this.descriptor = descriptor;\n this.descriptors = descriptors;\n }\n\n // \u2500\u2500 Convert \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\n /**\n * Convert raw LLM output into a validated result.\n *\n * For JTD-based conversions, the value is coerced to conform to the original\n * JTD schema:\n * - Non-integer numbers are rounded for integer types.\n * - Null values on optional properties are stripped (OpenAI required+nullable\n * encoding is reversed).\n * - `{key, value}` arrays from the values form are reassembled into Records.\n * - Unstructured/empty schemas parse the raw JSON string via `JSON.parse`.\n *\n * The coerced value is then validated against the JTD schema. Returns\n * {@link ConvertSuccessJson} on success.\n *\n * For MIME-based conversions, the value is expected to be a base64 string\n * which is decoded into raw bytes. Returns {@link ConvertSuccessBinary}.\n *\n * For multi-format signals, the first non-null `format_N` property is\n * selected and converted individually.\n *\n * @param json - The raw value returned by the OpenAI API (typically parsed JSON).\n * @returns A {@link ConvertResult} indicating success or failure with errors.\n */\n convert = (json: unknown): T | ConvertFailure => {\n if (this.descriptors) {\n return sharedConvertSignalsOutput(this.descriptors, json, coerceToJTD) as T | ConvertFailure;\n }\n if (this.jtdSchema) {\n return sharedConvertJTDOutput(this.jtdSchema, json, coerceToJTD) as T | ConvertFailure;\n }\n if (this.descriptor) {\n return sharedConvertSignalOutput(this.descriptor, json, coerceToJTD) as T | ConvertFailure;\n }\n return { success: false, errors: ['No source schema available'] };\n };\n\n // \u2500\u2500 Serialization \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\n /**\n * Produce a JSON-serializable representation of this conversion.\n *\n * The output includes the OpenAI `schema`, `strict` flag, and either the\n * original `jtdSchema` or `descriptor` (whichever was used to create this\n * instance), which is sufficient to fully restore the conversion via\n * {@link fromJSON}.\n *\n * Called automatically by `JSON.stringify(conv)`.\n */\n toJSON() {\n if (this.descriptors) {\n const result: Record<string, unknown> = {\n schema: this.schema,\n strict: this.strict,\n descriptors: this.descriptors\n };\n if (this.schema.description) result.description = this.schema.description;\n return result;\n }\n if (this.descriptor) {\n return {\n schema: this.schema,\n strict: this.strict,\n descriptor: this.descriptor\n };\n }\n return {\n schema: this.schema,\n strict: this.strict,\n jtdSchema: this.jtdSchema\n };\n }\n}\n\n// \u2500\u2500 Convert helpers (stateless, used by the class) \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\n\n// \u2500\u2500 Signal schema builder \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\nfunction buildSignalSchema(\n descriptor: SignalDescriptorJson,\n sharedDefs?: Record<string, JTDSchemaJson>\n): {\n schema: OpenAISchema;\n strict: boolean;\n} {\n const formats = descriptor.formats as SignalFormatJson[] | undefined;\n if (!formats || formats.length === 0) {\n throw new Error('Signal descriptor has no formats');\n }\n\n if (formats.length === 1) {\n const [fmt] = formats;\n const { schema, strict } = buildFormatSchema(fmt, sharedDefs);\n if (descriptor.label || descriptor.description) {\n const parts: string[] = [];\n if (descriptor.label) parts.push(descriptor.label);\n if (descriptor.description) parts.push(descriptor.description);\n const newDesc = parts.join('. ');\n schema.description = schema.description\n ? `${newDesc} ${schema.description}`\n : newDesc;\n }\n return { schema, strict };\n }\n\n // Multi-format wrapper \u2014 all format props are required + nullable\n const wrapperProps: Record<string, OpenAISchema> = {};\n const required: string[] = [];\n\n for (const [i, fmt] of formats.entries()) {\n const { schema: fmtSchema } = buildFormatSchema(fmt, sharedDefs);\n const key = `format_${i}`;\n\n if (fmt.jtdSchema) {\n fmtSchema.description = fmtSchema.description\n ? `Structured JSON data. ${fmtSchema.description}`\n : 'Structured JSON data.';\n }\n\n wrapperProps[key] = makeNullable(fmtSchema);\n required.push(key);\n }\n\n const keys = Object.keys(wrapperProps);\n const baseDesc = descriptor.description ?? '';\n const isOptional = descriptor.optional === true;\n const guidance = isOptional\n ? 'Provide at most ONE of the following format properties, set all others to null.'\n : `You MUST provide exactly ONE of the following format properties: ${keys.join(', ')}. Set all others to null.`;\n\n const schema: OpenAISchema = {\n type: 'object',\n description: baseDesc ? `${baseDesc} ${guidance}` : guidance,\n properties: wrapperProps,\n required,\n additionalProperties: false\n };\n\n return { schema, strict: true };\n}\n\n/**\n * Build JSON Schema for a single signal format.\n *\n * @param sharedDefs - When provided (fromSignals path), use these merged JTD\n * definitions for ref resolution and do NOT attach $defs to the output schema.\n * When absent (standalone fromJTD/fromSignal), attach $defs at schema root.\n */\nfunction buildFormatSchema(\n fmt: SignalFormatJson,\n sharedDefs?: Record<string, JTDSchemaJson>\n): {\n schema: OpenAISchema;\n strict: boolean;\n} {\n if (fmt.jtdSchema) {\n // Use shared defs if provided (fromSignals), otherwise fall back to format-local defs\n const defs = sharedDefs ?? fmt.jtdSchema.definitions;\n const strict = !isRootUnstructured(fmt.jtdSchema, defs);\n\n let schema: OpenAISchema;\n if (strict) {\n schema = convertNode(fmt.jtdSchema, defs, 0);\n\n // Only attach $defs in standalone mode (no shared defs).\n // When shared defs are provided, the caller (fromSignals) attaches them at root.\n if (!sharedDefs && defs && Object.keys(defs).length > 0) {\n schema.$defs = Object.fromEntries(\n Object.entries(defs).map(([name, defSchema]) => [\n name,\n convertNode(defSchema, defs, 0)\n ])\n );\n }\n } else {\n schema = {};\n applyMetadata(schema, fmt.jtdSchema);\n }\n\n return { schema, strict };\n }\n if (fmt.mimeType) {\n return { schema: convertMimeFormat(fmt.mimeType), strict: true };\n }\n throw new Error('Signal format has neither jtdSchema nor mimeType');\n}\n\n// \u2500\u2500 Convenience aliases \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\n/**\n * Convenience alias for {@link OpenAIConversion.fromJTD}.\n *\n * @param jtdSchema - A valid JTD schema (RFC 8927).\n * @returns A new {@link OpenAIConversion}.\n */\nexport function convertToOpenAISchema(\n jtdSchema: JTDSchemaJson\n): OpenAIConversion {\n return OpenAIConversion.fromJTD(jtdSchema);\n}\n\n/**\n * Convenience alias for {@link OpenAIConversion.fromSignal}.\n *\n * @param descriptor - An Adapt signal descriptor.\n * @returns A new {@link OpenAIConversion}.\n */\nexport function convertSignalToOpenAISchema(\n descriptor: SignalDescriptorJson\n): OpenAIConversion {\n return OpenAIConversion.fromSignal(descriptor);\n}\n"],
5
+ "mappings": "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",
6
6
  "names": ["isSchema", "isValidSchema", "jtdValidate", "INTEGER_TYPES", "formatInstancePath", "instancePath", "p", "resolveSchemaParent", "schema", "schemaPath", "current", "resolveInstanceValue", "value", "key", "formatValidationError", "error", "path", "lastKey", "jtdType", "enumValues", "discValue", "validateJsonAgainstJTD", "definitions", "defs", "schemaForValidation", "isValidSchema", "jtdValidate", "MAX_VALIDATION_DEPTH", "MAX_VALIDATION_ERRORS", "err", "MAX_VALIDATION_DEPTH", "MAX_VALIDATION_ERRORS", "MAX_REF_DEPTH", "INTEGER_BOUNDS", "FLOAT32_BOUNDS", "UNSTRUCTURED_DESC", "UNSTRUCTURED_VARIANT_DESC", "VALUES_ARRAY_DESC", "nullableType", "baseType", "nullable", "deepEqual", "a", "b", "v", "i", "ao", "bo", "keys", "k", "isUnstructuredSchema", "schema", "isRootUnstructured", "defs", "resolved", "depth", "target", "convertJTDOutput", "jtdSchema", "json", "coerce", "coerced", "errors", "validateJsonAgainstJTD", "convertMimeOutput", "mimeType", "rec", "result", "convertSingleFormatOutput", "fmt", "convertSignalOutput", "descriptor", "formats", "key", "_", "convertSignalsOutput", "descriptors", "signals", "desc", "value", "err", "rewriteJtdRefs", "schema", "renames", "result", "k", "v", "poolSignalDefinitions", "signals", "mergedDefs", "renamesByFormat", "si", "formats", "fi", "defs", "hasConflict", "name", "defSchema", "existing", "deepEqual", "n", "newName", "signalsCopy", "signal", "hasChanges", "newFormats", "fmt", "hasDefs", "rewritten", "makeNullable", "schema", "s", "result", "METADATA_KNOWN_KEYS", "applyMetadata", "meta", "descParts", "titleCandidate", "descText", "extras", "key", "value", "newDesc", "convertNode", "defs", "depth", "MAX_REF_DEPTH", "UNSTRUCTURED_DESC", "isUnstructuredSchema", "nullableType", "isNullable", "refName", "intBounds", "INTEGER_BOUNDS", "FLOAT32_BOUNDS", "props", "required", "propSchema", "converted", "nullable", "valueSchema", "VALUES_ARRAY_DESC", "discKey", "mapping", "variants", "tag", "variantSchema", "variantOAI", "UNSTRUCTURED_VARIANT_DESC", "INTEGER_TYPE_SET", "coerceToJTD", "resolved", "elemSchema", "item", "obj", "k", "entry", "rec", "dataStr", "parsed", "convertMimeFormat", "mimeType", "OpenAIConversion", "_OpenAIConversion", "jtdSchema", "strict", "isRootUnstructured", "name", "defSchema", "descriptor", "sharedDefs", "buildSignalSchema", "signals", "description", "label", "mergedDefs", "signalsCopy", "poolSignalDefinitions", "properties", "allStrict", "signal", "conv", "data", "descriptors", "json", "convertSignalsOutput", "convertJTDOutput", "convertSignalOutput", "formats", "fmt", "buildFormatSchema", "parts", "wrapperProps", "i", "fmtSchema", "keys", "baseDesc", "guidance", "convertToOpenAISchema", "convertSignalToOpenAISchema"]
7
7
  }
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=gemini-fc.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"gemini-fc.test.d.ts","sourceRoot":"","sources":["../../src/gemini/gemini-fc.test.ts"],"names":[],"mappings":""}