fix: address Copilot review round 16 — tie-breaking, const-aware gating, malformed declarations
- selectBranchIndex no longer stops when several branches share the supplied
constant. A branch is dropped only when a supplied value CONTRADICTS one of
its constants; the required-field and exclusive-name passes then run over the
remaining candidates, so { version: 1, phone: "555" } resolves to the SMS
branch instead of falling back to the first.
- hasMissingRequiredFields counts only the branches the values could be making
(new branchAcceptsValues): { kind: "sms", address: "x" } supplies
everything the EMAIL branch requires while contradicting its discriminator,
and would otherwise enable a submit the server refuses.
- convertToolParameters ignores a malformed property declaration when branches
agree on a type, rather than storing a non-schema as the coercion schema.
Signed-off-by: cliffhall <cliff@futurescale.com>
This commit is contained in:
parent
09914772ea
commit
7792ad71e9
2 changed files with 133 additions and 43 deletions
|
|
@ -269,6 +269,27 @@ describe("JSON Utils", () => {
|
|||
});
|
||||
});
|
||||
|
||||
it("ignores a malformed branch declaration when agreeing on a type (#2123)", () => {
|
||||
const malformedDeclaration: Tool = {
|
||||
name: "malformed-declaration",
|
||||
inputSchema: {
|
||||
type: "object",
|
||||
anyOf: [
|
||||
{ type: "object", properties: { count: null as unknown, a: {} } },
|
||||
{
|
||||
type: "object",
|
||||
properties: { count: { type: "number" }, b: {} },
|
||||
},
|
||||
],
|
||||
},
|
||||
};
|
||||
// The `null` is not a vote about the type, and must not end up standing
|
||||
// in for one — the surviving declaration is what coerces.
|
||||
expect(
|
||||
convertToolParameters(malformedDeclaration, { count: "3" }),
|
||||
).toEqual({ count: 3 });
|
||||
});
|
||||
|
||||
it("coerces a value whose schema lives on a root allOf branch (#2123)", () => {
|
||||
const allOfTool: Tool = {
|
||||
name: "allof-tool",
|
||||
|
|
|
|||
|
|
@ -811,6 +811,30 @@ describe("resolveRootUnion", () => {
|
|||
expect(selectBranchIndex(pinnedOnInherited, { x: "supplied" })).toBe(0);
|
||||
});
|
||||
|
||||
it("keeps looking when several branches share the supplied constant", () => {
|
||||
// Both pin `version`, so that constant settles nothing — but `phone`
|
||||
// belongs to one branch alone and does.
|
||||
const versioned = resolveRootUnion({
|
||||
type: "object",
|
||||
anyOf: [
|
||||
{
|
||||
type: "object",
|
||||
properties: {
|
||||
version: { const: 1 },
|
||||
address: { type: "string" },
|
||||
},
|
||||
},
|
||||
{
|
||||
type: "object",
|
||||
properties: { version: { const: 1 }, phone: { type: "string" } },
|
||||
},
|
||||
],
|
||||
}).branches;
|
||||
expect(selectBranchIndex(versioned, { version: 1, phone: "555" })).toBe(
|
||||
1,
|
||||
);
|
||||
});
|
||||
|
||||
it("reports none when the values identify nothing", () => {
|
||||
expect(selectBranchIndex(branches, {})).toBeNull();
|
||||
expect(selectBranchIndex(branches, { kind: "other" })).toBeNull();
|
||||
|
|
|
|||
Loading…
Reference in a new issue