diff --git a/src/tools/SearchTools.schema.test.ts b/src/tools/SearchTools.schema.test.ts index a708152f..d19d7a7f 100644 --- a/src/tools/SearchTools.schema.test.ts +++ b/src/tools/SearchTools.schema.test.ts @@ -19,6 +19,14 @@ describe("SearchTools Schema Validation", () => { }) .optional() .describe("Time range for search"), + returnAll: z + .preprocess((val) => { + if (typeof val === "string") { + return val.toLowerCase() === "true"; + } + return val; + }, z.boolean().optional()) + .describe("Return all matching notes"), }); test("validates correct input structure", () => { @@ -100,6 +108,69 @@ describe("SearchTools Schema Validation", () => { const result = localSearchSchema.safeParse(invalidInput); expect(result.success).toBe(false); }); + + test("accepts boolean returnAll: true", () => { + const input = { query: "find all notes", salientTerms: ["notes"], returnAll: true }; + const result = localSearchSchema.safeParse(input); + expect(result.success).toBe(true); + if (result.success) { + expect(result.data.returnAll).toBe(true); + } + }); + + test("accepts boolean returnAll: false", () => { + const input = { query: "find notes", salientTerms: ["notes"], returnAll: false }; + const result = localSearchSchema.safeParse(input); + expect(result.success).toBe(true); + if (result.success) { + expect(result.data.returnAll).toBe(false); + } + }); + + test("coerces string 'True' to boolean true", () => { + const input = { query: "find all notes", salientTerms: ["notes"], returnAll: "True" }; + const result = localSearchSchema.safeParse(input); + expect(result.success).toBe(true); + if (result.success) { + expect(result.data.returnAll).toBe(true); + } + }); + + test("coerces string 'true' to boolean true", () => { + const input = { query: "find all notes", salientTerms: ["notes"], returnAll: "true" }; + const result = localSearchSchema.safeParse(input); + expect(result.success).toBe(true); + if (result.success) { + expect(result.data.returnAll).toBe(true); + } + }); + + test("coerces string 'FALSE' to boolean false", () => { + const input = { query: "find notes", salientTerms: ["notes"], returnAll: "FALSE" }; + const result = localSearchSchema.safeParse(input); + expect(result.success).toBe(true); + if (result.success) { + expect(result.data.returnAll).toBe(false); + } + }); + + test("coerces string 'False' to boolean false", () => { + const input = { query: "find notes", salientTerms: ["notes"], returnAll: "False" }; + const result = localSearchSchema.safeParse(input); + expect(result.success).toBe(true); + if (result.success) { + expect(result.data.returnAll).toBe(false); + } + }); + + test("accepts omitted returnAll (optional)", () => { + const input = { query: "find notes", salientTerms: ["notes"] }; + const result = localSearchSchema.safeParse(input); + expect(result.success).toBe(true); + if (result.success) { + expect(result.data.returnAll).toBeUndefined(); + } + }); }); describe("webSearchTool schema", () => { diff --git a/src/tools/SearchTools.ts b/src/tools/SearchTools.ts index 94094ed4..c2d18666 100644 --- a/src/tools/SearchTools.ts +++ b/src/tools/SearchTools.ts @@ -77,8 +77,12 @@ const localSearchSchema = z.object({ .optional() .describe("Optional time range filter. Use epoch milliseconds from getTimeRangeMs result."), returnAll: z - .boolean() - .optional() + .preprocess((val) => { + if (typeof val === "string") { + return val.toLowerCase() === "true"; + } + return val; + }, z.boolean().optional()) .describe( "Set to true when the user wants ALL matching notes, not just the best few. " + "Use for requests like 'find all my X', 'list every Y', 'show me all my Z', " +