import { z } from "zod"; import { hostFromUrl, platformNameFromHost } from "./helpers/platformIdentity.js"; /** * Contrato de respuesta de las tools (#104, #167, #172, #181). * * La spec completa —y el razonamiento de cada decisión— vive en * `docs/development/response-contract.md`. Este módulo es su expresión * ejecutable: los schemas del envelope y el envoltorio que `ToolBase` aplica * al `data` de cada tool. * * Regla que gobierna todo lo de acá: **sin `anyOf`/`oneOf` de más de una rama * estructurada a ninguna profundidad, sin whitelist**. Es más estricta que la * política de entradas (`docs/development/schema-policy.md` §1) porque en * salidas no hay deuda previa que amnistiar: el contrato nace hoy. */ /** * Identidad de la instancia (#172). * * `host` es el hostname, no la URL: el esquema y la barra final no * desambiguan nada y sí cuestan tokens en cada respuesta. */ export declare const platformIdentitySchema: z.ZodObject<{ name: z.ZodString; host: z.ZodString; }, z.core.$strip>; export type PlatformIdentity = z.infer; /** * Entidad resuelta y afectada (#181 ítem 20, #167). * * `type` siempre; de los identificadores, todos los que la entidad tenga. * `uuid` siempre que exista: es el único estable a través de una publicación * —los ids numéricos de widget/template/entry cambian al publicar—. */ export declare const resourceIdentitySchema: z.ZodObject<{ type: z.ZodString; uuid: z.ZodOptional; id: z.ZodOptional; slug: z.ZodOptional; path: z.ZodOptional; name: z.ZodOptional; }, z.core.$strip>; export type ResourceIdentity = z.infer; /** * Cobertura de una lectura (#181 ítem 19). * * Responde "¿qué puedo afirmar sobre esta llamada sin hacer otra?" del lado * de las lecturas: un resultado vacío tiene que ser autoexplicativo. */ export declare const readStateSchema: z.ZodObject<{ returned: z.ZodOptional; total: z.ZodOptional; searched: z.ZodOptional; page: z.ZodOptional; per_page: z.ZodOptional; complete: z.ZodOptional; }, z.core.$strip>; /** * Post-condición de una escritura (#167). * * Qué quedó, verificable, sin tener que inferirlo desde * `channels-publish --dry_run`. Los valores persistidos van en `data`, * releídos de la respuesta de la API y no ecoados de los parámetros: una * respuesta que ecoa lo que le mandaron certifica el pedido, no el hecho. */ export declare const writeStateSchema: z.ZodObject<{ status: z.ZodOptional; published: z.ZodOptional; live_version: z.ZodOptional; pending: z.ZodOptional; affected: z.ZodOptional; }, z.core.$strip>; /** * Las dos caras de `state` se publican por separado y no como un solo objeto * con los once campos, por dos razones que apuntan igual: * * - **Correctitud.** Una lectura que declara `live_version` en su schema de * salida le está ofreciendo al modelo un campo que nunca va a llegar. * - **Footprint (#58).** Medido con la sonda del piloto: el objeto único * costaba 130 tokens de `outputSchema` por tool, y a 82 tools eso es * ~10.700 tokens fijos por sesión en `tools/list` — sobre un catálogo que * hoy pesa ~107.000. Publicar solo la cara que aplica lo corta a la mitad. * * El tipo sigue siendo uno solo para los llamadores: una tool no inventa su * propio `state`, elige cuál de las dos caras emite. */ export type ResponseState = z.infer & z.infer; /** * Cara de `state` que publica una tool. * * `"both"` existe por las tools `gated` —multi-accion con acciones de * lectura—, que legitimamente devuelven cobertura en su rama `list` y * post-condicion en su rama `update`. Publicarles una sola cara hacia fallar * la validacion del SDK en la otra rama; lo encontro el rollout, no el * piloto, que era una lectura de una sola forma. */ export type StateFace = "read" | "write" | "both"; /** * Las dos caras juntas, para las tools `gated`. Cuesta 130 tokens de * `outputSchema` contra 79 de una cara sola; a las 27 tools gated del * catalogo son ~1.400 tokens, que es el precio correcto de no mentir sobre * lo que la tool puede devolver. */ export declare const bothStateSchema: z.ZodObject<{ returned: z.ZodOptional; total: z.ZodOptional; searched: z.ZodOptional; page: z.ZodOptional; per_page: z.ZodOptional; complete: z.ZodOptional; status: z.ZodOptional; published: z.ZodOptional; live_version: z.ZodOptional; pending: z.ZodOptional; affected: z.ZodOptional; }, z.core.$strip>; /** * Desenlaces de control: la llamada no se ejecuto, y el motivo no es un * error del servidor. * * Existe porque el rollout descubrio que `confirmDelete` / `elicit` y el * rechazo del gate read-only devuelven `isError: false` **sin** * `structuredContent`: en una tool con `outputSchema` declarado, el SDK las * rechaza con `-32602`. Afectaba a toda tool `gated` en modo read-only y a * toda tool con confirmacion de borrado. * * Va en el envelope y no dentro de `data` a proposito: `message` es texto * que el cliente MCP renderiza directo al usuario —el alcance de #104 lo * excluye explicitamente—, y meterlo en `data` reintroduciria por la ventana * justo el campo que este rediseño saca por la puerta. */ export declare const controlSchema: z.ZodObject<{ cancelled: z.ZodOptional; message: z.ZodOptional; readOnly: z.ZodOptional; rejected: z.ZodOptional; reason: z.ZodOptional; }, z.core.$strip>; export type ResponseControl = z.infer; /** * El envelope, tal como lo ve un cliente. * * `platform` y `data` son obligatorias; `resource` y `state` están presentes * siempre que apliquen, y cuándo aplican lo fija el contrato, no cada tool. * * No hay campo `message` (#104): lo humano-legible vive en `content[].text`. */ export interface ResponseEnvelope { platform: PlatformIdentity; data: TData; resource?: ResourceIdentity; state?: ResponseState; control?: ResponseControl; } /** * Envuelve el schema del `data` de una tool con el envelope común. * * Cada tool declara solo lo suyo; `platform`, `resource` y `state` son * iguales para las 82 tools del catálogo y no se reescriben 82 veces. * * `face` elige qué cara de `state` se publica: `"read"` para cobertura, * `"write"` para post-condición. */ export declare function envelopeSchema(dataSchema: T, face: StateFace): z.ZodObject<{ platform: z.ZodObject<{ name: z.ZodString; host: z.ZodString; }, z.core.$strip>; data: T; resource: z.ZodOptional; id: z.ZodOptional; slug: z.ZodOptional; path: z.ZodOptional; name: z.ZodOptional; }, z.core.$strip>>; state: z.ZodOptional; total: z.ZodOptional; searched: z.ZodOptional; page: z.ZodOptional; per_page: z.ZodOptional; complete: z.ZodOptional; }, z.core.$strip>> | z.ZodOptional; published: z.ZodOptional; live_version: z.ZodOptional; pending: z.ZodOptional; affected: z.ZodOptional; }, z.core.$strip>>; control: z.ZodOptional; message: z.ZodOptional; readOnly: z.ZodOptional; rejected: z.ZodOptional; reason: z.ZodOptional; }, z.core.$strip>>; }, z.core.$strip>; /** * `complete` de la cara de lectura, afirmado solo con evidencia (§1.4). * * `complete` significa **"se que cubri todo"**, no "creo que si". La unica * evidencia que lo sostiene es el `meta` de la API o una paginacion agotada. * Sin evidencia el campo **se omite**, y la omision significa "no se" — nunca * "cubri todo". * * Existe para matar un anti-patron concreto que el censo semantico de #206 * encontro en 17 sitios: * * ```ts * const total = result.meta?.total_entries ?? spaces.length; * complete: spaces.length >= total, // sin `meta` es `n >= n`: true SIEMPRE * ``` * * El `??` degrada al lado optimista justo cuando el truncado es indetectable. * Con este helper, ese mismo sitio omite el campo en vez de mentir: * * ```ts * ...completeFrom(spaces.length, result.meta?.total_entries), * ``` */ export declare function completeFrom(returned: number, totalEntries: number | null | undefined): { complete?: boolean; }; export { hostFromUrl, platformNameFromHost }; /** * Identidad de la instancia sobre la que se está operando. * * Si la plataforma no está configurada devuelve un identidad explícita en vez * de omitir el campo: el envelope declara `platform` obligatoria, y una tool * que llegó a responder algo tiene que decir contra qué respondió. */ export declare function currentPlatformIdentity(): PlatformIdentity; //# sourceMappingURL=responseContract.d.ts.map