mirror of
https://github.com/morluto/rea.git
synced 2026-10-27 01:45:32 +00:00
* refactor(config): parse capability settings into exact states * refactor(runtime): encode replay and process outcomes exactly * refactor: make analysis states unrepresentable * refactor(mcp): register canonical schemas with SDK * fix: preserve capability and catalog invariants * fix(build): remove stale compiled modules * fix(replay): update real verifier policy dependency * fix(package): verify the complete MCP catalog * fix(test): supply complete replay reload policy * refactor(contracts): centralize output schema lookup * refactor(mcp): delegate protocol boundaries to SDK * fix(browser): approve real source capture * fix(mcp): preserve catalog guidance
95 lines
2.7 KiB
TypeScript
95 lines
2.7 KiB
TypeScript
import type { z } from "zod";
|
|
|
|
import type { AnalysisInputIssue } from "./errors.js";
|
|
|
|
/** Project Zod failures to secret-safe caller correction metadata. */
|
|
export const projectInputIssues = (
|
|
issues: readonly z.core.$ZodIssue[],
|
|
input: unknown,
|
|
): readonly AnalysisInputIssue[] =>
|
|
issues.flatMap((issue) => projectIssue(issue, input));
|
|
|
|
const projectIssue = (
|
|
issue: z.core.$ZodIssue,
|
|
input: unknown,
|
|
): readonly AnalysisInputIssue[] => {
|
|
const path = issue.path.flatMap((part) =>
|
|
typeof part === "string" || typeof part === "number" ? [part] : [],
|
|
);
|
|
if (issue.code === "invalid_union")
|
|
return issue.errors.flatMap((branch) =>
|
|
branch.flatMap((branchIssue) => projectIssue(branchIssue, input)),
|
|
);
|
|
if (issue.code === "unrecognized_keys")
|
|
return issue.keys.map((key) => ({
|
|
path: [...path, key],
|
|
reason: "unknown_argument" as const,
|
|
}));
|
|
if (issue.code === "invalid_type")
|
|
return [
|
|
{
|
|
path,
|
|
reason:
|
|
valueAtPath(input, path) === undefined
|
|
? "missing_argument"
|
|
: "invalid_type",
|
|
expected: safeExpected(issue.expected),
|
|
},
|
|
];
|
|
if (issue.code === "too_small")
|
|
return [boundedIssue(path, "minimum", numericBound(issue.minimum))];
|
|
if (issue.code === "too_big")
|
|
return [boundedIssue(path, "maximum", numericBound(issue.maximum))];
|
|
if (issue.code === "invalid_format")
|
|
return [{ path, reason: "invalid_format", expected: issue.format }];
|
|
if (issue.code === "invalid_value")
|
|
return [
|
|
{
|
|
path,
|
|
reason: "invalid_value",
|
|
expected: issue.values.filter(isSafeExpected),
|
|
},
|
|
];
|
|
return [
|
|
{
|
|
path,
|
|
reason: "invalid_value",
|
|
...(issue.message.length > 0 ? { message: issue.message } : {}),
|
|
},
|
|
];
|
|
};
|
|
|
|
const valueAtPath = (
|
|
input: unknown,
|
|
path: readonly (string | number)[],
|
|
): unknown => {
|
|
let current = input;
|
|
for (const part of path) {
|
|
if (typeof current !== "object" || current === null) return undefined;
|
|
const next: unknown = Reflect.get(current, part);
|
|
current = next;
|
|
}
|
|
return current;
|
|
};
|
|
|
|
const numericBound = (value: number | bigint): number | undefined =>
|
|
typeof value === "number" && Number.isFinite(value) ? value : undefined;
|
|
|
|
const isSafeExpected = (
|
|
value: unknown,
|
|
): value is string | number | boolean | null =>
|
|
value === null || ["string", "number", "boolean"].includes(typeof value);
|
|
|
|
const safeExpected = (value: unknown) =>
|
|
isSafeExpected(value) ? value : "schema-defined value";
|
|
|
|
const boundedIssue = (
|
|
path: readonly (string | number)[],
|
|
key: "minimum" | "maximum",
|
|
value: number | undefined,
|
|
): AnalysisInputIssue => ({
|
|
path,
|
|
reason: "out_of_range",
|
|
...(value === undefined ? {} : { [key]: value }),
|
|
});
|