From 287e8c24054e26c49c4545459afd0018b7d72f49 Mon Sep 17 00:00:00 2001 From: Sravanjangam <163002695+Sravanjangam@users.noreply.github.com> Date: Sun, 23 Aug 2026 03:22:22 +0530 Subject: [PATCH] fix(ai-sdk): bound search limit and add client timeout - search_memories limit is now an integer constrained to 1-50 in the JSON schema AND clamped at execute time (clampSearchLimit): negative, fractional, or huge model-supplied values previously flowed straight into the metered Search API with full documents included, flooding agent context and inflating cost. - Supermemory client now sets timeout: 30s / maxRetries: 2 so a hung connection can't stall an agent's tool loop indefinitely. --- packages/ai-sdk/src/limit.test.ts | 26 ++++++++++++++++++++++++++ packages/ai-sdk/src/tools.ts | 26 +++++++++++++++++++++++--- 2 files changed, 49 insertions(+), 3 deletions(-) create mode 100644 packages/ai-sdk/src/limit.test.ts diff --git a/packages/ai-sdk/src/limit.test.ts b/packages/ai-sdk/src/limit.test.ts new file mode 100644 index 000000000..3c89db318 --- /dev/null +++ b/packages/ai-sdk/src/limit.test.ts @@ -0,0 +1,26 @@ +import { describe, expect, it } from "vitest" +import { clampSearchLimit } from "./tools" + +describe("clampSearchLimit", () => { + it("keeps in-range integers", () => { + expect(clampSearchLimit(1)).toBe(1) + expect(clampSearchLimit(10)).toBe(10) + expect(clampSearchLimit(50)).toBe(50) + }) + + it("clamps huge and negative values into range", () => { + expect(clampSearchLimit(1e9)).toBe(50) + expect(clampSearchLimit(-5)).toBe(1) + expect(clampSearchLimit(0)).toBe(1) + }) + + it("floors fractional values", () => { + expect(clampSearchLimit(7.9)).toBe(7) + }) + + it("falls back to the default on non-numeric input", () => { + expect(clampSearchLimit(Number.NaN)).toBe(10) + expect(clampSearchLimit(undefined)).toBe(10) + expect(clampSearchLimit("12" as unknown as number)).toBe(12) + }) +}) diff --git a/packages/ai-sdk/src/tools.ts b/packages/ai-sdk/src/tools.ts index 7d0b837cb..87b8007cc 100644 --- a/packages/ai-sdk/src/tools.ts +++ b/packages/ai-sdk/src/tools.ts @@ -21,6 +21,19 @@ type AddMemoryInput = { memory: string } +/** + * Clamp a model-supplied result limit into the 1-50 range. + * + * The JSON schema already constrains well-behaved models, but + * prompt-injected or sloppy callers can still hand negative, fractional, + * or huge values straight into a metered API. + */ +export function clampSearchLimit(value: unknown): number { + const parsed = Number(value) + if (!Number.isFinite(parsed)) return 10 + return Math.min(50, Math.max(1, Math.floor(parsed))) +} + /** * Create Supermemory tools for AI SDK */ @@ -30,6 +43,10 @@ export function supermemoryTools( ) { const client = new Supermemory({ apiKey, + // Bound tool-call latency: without a timeout a hung connection stalls + // the agent's execute() loop indefinitely. + timeout: 30_000, + maxRetries: 2, ...(config?.baseUrl ? { baseURL: config.baseUrl } : {}), }) @@ -54,8 +71,10 @@ export function supermemoryTools( default: true, }, limit: { - type: "number", - description: "Maximum number of results to return", + type: "integer", + minimum: 1, + maximum: 50, + description: "Maximum number of results to return (1-50)", default: 10, }, }, @@ -67,10 +86,11 @@ export function supermemoryTools( limit = 10, }) => { try { + const safeLimit = clampSearchLimit(limit) const response = await client.search.execute({ q: informationToGet, containerTags, - limit, + limit: safeLimit, chunkThreshold: 0.6, includeFullDocs, })