diff --git a/packages/gateway-protocol/package.json b/packages/gateway-protocol/package.json index 5a997f7d2fba..679ac1709446 100644 --- a/packages/gateway-protocol/package.json +++ b/packages/gateway-protocol/package.json @@ -78,8 +78,7 @@ "prepack": "pnpm run build && node --import tsx ../../scripts/protocol-gen.ts --out ./protocol.schema.json" }, "dependencies": { - "typebox": "1.3.6", - "zod": "4.4.3" + "typebox": "1.3.6" }, "engines": { "node": ">=22.19.0" diff --git a/packages/gateway-protocol/src/install-policy-warning-error-details.ts b/packages/gateway-protocol/src/install-policy-warning-error-details.ts index 4da07ceccea4..452b46d78938 100644 --- a/packages/gateway-protocol/src/install-policy-warning-error-details.ts +++ b/packages/gateway-protocol/src/install-policy-warning-error-details.ts @@ -1,4 +1,7 @@ -import { z } from "zod"; +import { + asProtocolRecord, + normalizeOptionalProtocolString, +} from "./protocol-value-normalization.js"; /** Structured install-policy warning details carried in Gateway error payloads. */ export const INSTALL_POLICY_WARNING_ACKNOWLEDGEMENT_REQUIRED = @@ -23,28 +26,84 @@ export type InstallPolicyWarningErrorDetails = { findings?: InstallPolicyWarningErrorFinding[]; }; -const installPolicyWarningFindingSchema = z.object({ - ruleId: z.string().trim().min(1), - severity: z.enum(["info", "warn", "critical"]), - message: z.string().trim().min(1), - file: z.string().trim().min(1).optional(), - line: z.number().int().positive().optional(), - evidence: z.string().trim().min(1).optional(), -}); - -const installPolicyWarningErrorDetailsSchema = z.object({ - installPolicyCode: z.literal(INSTALL_POLICY_WARNING_ACKNOWLEDGEMENT_REQUIRED), - targetName: z.string().trim().min(1), - targetType: z.enum(["skill", "plugin"]), - requestMode: z.enum(["install", "update"]), - reason: z.string().trim().min(1), - acknowledgementToken: z.string().trim().min(1), - findings: z.array(installPolicyWarningFindingSchema).optional(), -}); +function readFinding(value: unknown): InstallPolicyWarningErrorFinding | undefined { + const record = asProtocolRecord(value); + if (!record) { + return undefined; + } + const ruleId = normalizeOptionalProtocolString(record.ruleId); + const message = normalizeOptionalProtocolString(record.message); + const severity = record.severity; + if ( + !ruleId || + !message || + (severity !== "info" && severity !== "warn" && severity !== "critical") + ) { + return undefined; + } + const file = normalizeOptionalProtocolString(record.file); + const evidence = normalizeOptionalProtocolString(record.evidence); + const line = record.line; + if ( + (record.file !== undefined && !file) || + (record.evidence !== undefined && !evidence) || + (line !== undefined && (typeof line !== "number" || !Number.isSafeInteger(line) || line <= 0)) + ) { + return undefined; + } + return { + ruleId, + severity, + message, + ...(file ? { file } : {}), + ...(line !== undefined ? { line } : {}), + ...(evidence ? { evidence } : {}), + }; +} export function readInstallPolicyWarningErrorDetails( value: unknown, ): InstallPolicyWarningErrorDetails | undefined { - const parsed = installPolicyWarningErrorDetailsSchema.safeParse(value); - return parsed.success ? parsed.data : undefined; + const record = asProtocolRecord(value); + if (!record) { + return undefined; + } + const targetName = normalizeOptionalProtocolString(record.targetName); + const reason = normalizeOptionalProtocolString(record.reason); + const acknowledgementToken = normalizeOptionalProtocolString(record.acknowledgementToken); + const targetType = record.targetType; + const requestMode = record.requestMode; + if ( + record.installPolicyCode !== INSTALL_POLICY_WARNING_ACKNOWLEDGEMENT_REQUIRED || + !targetName || + !reason || + !acknowledgementToken || + (targetType !== "skill" && targetType !== "plugin") || + (requestMode !== "install" && requestMode !== "update") + ) { + return undefined; + } + let findings: InstallPolicyWarningErrorFinding[] | undefined; + if (record.findings !== undefined) { + if (!Array.isArray(record.findings)) { + return undefined; + } + findings = []; + for (const value of record.findings) { + const finding = readFinding(value); + if (!finding) { + return undefined; + } + findings.push(finding); + } + } + return { + installPolicyCode: INSTALL_POLICY_WARNING_ACKNOWLEDGEMENT_REQUIRED, + targetName, + targetType, + requestMode, + reason, + acknowledgementToken, + ...(findings ? { findings } : {}), + }; } diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 985f8a3a0b04..dc69e89ec2b9 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -2231,9 +2231,6 @@ importers: typebox: specifier: 1.3.6 version: 1.3.6 - zod: - specifier: 4.4.3 - version: 4.4.3 packages/llm-core: dependencies: diff --git a/src/gateway/install-policy-warning-error-details.test.ts b/src/gateway/install-policy-warning-error-details.test.ts index 731616e3c38d..64ecdf1d5ac8 100644 --- a/src/gateway/install-policy-warning-error-details.test.ts +++ b/src/gateway/install-policy-warning-error-details.test.ts @@ -46,7 +46,30 @@ describe("install policy warning error details", () => { requestMode: "install", reason: "", }, + { + ...expectedWarning, + findings: [{ ...completeWarning.findings?.[0], line: 0 }], + }, + { + ...expectedWarning, + findings: [{ ...completeWarning.findings?.[0], severity: "error" }], + }, ])("rejects malformed warning details", (value) => { expect(readInstallPolicyWarningErrorDetails(value)).toBeUndefined(); }); + + it("normalizes protocol strings without changing the published dependency surface", () => { + expect( + readInstallPolicyWarningErrorDetails({ + ...expectedWarning, + targetName: " demo-plugin ", + reason: " Review required ", + acknowledgementToken: " token ", + }), + ).toMatchObject({ + targetName: "demo-plugin", + reason: "Review required", + acknowledgementToken: "token", + }); + }); });