diff --git a/clients/web/src/test/core/rootUnion.test.ts b/clients/web/src/test/core/rootUnion.test.ts index 69100561..5a747ed5 100644 --- a/clients/web/src/test/core/rootUnion.test.ts +++ b/clients/web/src/test/core/rootUnion.test.ts @@ -901,6 +901,20 @@ describe("resolveRootUnion", () => { ).toBe(true); }); + it("counts a required name a schema never declares", () => { + // Legal, and the tool plainly takes an argument — an App tool shaped this + // way must ask rather than being auto-invoked with `{}`. + expect(declaresAnyFields({ type: "object", required: ["token"] })).toBe( + true, + ); + expect( + declaresAnyFields({ + type: "object", + allOf: [{ type: "object", required: ["token"] }], + }), + ).toBe(true); + }); + it("reports none for a bare object schema", () => { expect(declaresAnyFields({ type: "object" })).toBe(false); expect(declaresAnyFields(undefined)).toBe(false); diff --git a/clients/web/src/utils/toolUtils.test.ts b/clients/web/src/utils/toolUtils.test.ts index 07c6446f..efe0211e 100644 --- a/clients/web/src/utils/toolUtils.test.ts +++ b/clients/web/src/utils/toolUtils.test.ts @@ -145,6 +145,12 @@ describe("hasInputFields with root composition (#2123)", () => { ).toBe(true); }); + it("counts a required name the schema never declares", () => { + expect(hasInputFields(tool({ type: "object", required: ["token"] }))).toBe( + true, + ); + }); + it("still reports no fields for a bare object schema", () => { expect(hasInputFields(tool({ type: "object" }))).toBe(false); }); diff --git a/core/json/rootUnion.ts b/core/json/rootUnion.ts index b480f4e8..279cd1a7 100644 --- a/core/json/rootUnion.ts +++ b/core/json/rootUnion.ts @@ -498,6 +498,11 @@ export function declaresAnyFields( ): boolean { if (schema === undefined) return false; if (Object.keys(propertiesOf(schema)).length > 0) return true; + // A schema may require a name it never declares — `{ required: ["token"] }` + // is a legal object schema, and the tool plainly takes an argument. Counting + // it here is what stops an App tool with that shape being auto-invoked with + // `{}` for want of a `properties` map. + if (requiredOf(schema).length > 0) return true; // A `$ref`'s shape is unknown rather than empty, so it counts. Reporting "no // fields" for `anyOf: [{ $ref: … }, { $ref: … }]` would auto-invoke an App // tool with `{}` on the strength of something never read.