mirror of
https://github.com/openclaw/openclaw.git
synced 2026-08-27 12:56:01 -06:00
fix: tighten findSchema.limit from Type.Number to Type.Integer with execution guard (#105931)
* fix: tighten findSchema.limit from Type.Number to Type.Integer with execution guard Schema-level Type.Integer rejects fractional limits from model tool calls; the execution guard covers direct execute() callers. Non-finite values keep the established fallback-to-default behavior so existing callers are unaffected. Co-Authored-By: Claude <noreply@anthropic.com> * test: harden find limit execution coverage * test: simplify find limit coverage --------- Co-authored-by: Claude <noreply@anthropic.com> Co-authored-by: Altay <altay@hey.com>
This commit is contained in:
@@ -1,5 +1,6 @@
|
||||
// find tool tests cover custom search operation wiring and result-limit
|
||||
// normalization for session file discovery.
|
||||
import { Value } from "typebox/value";
|
||||
import { describe, expect, it } from "vitest";
|
||||
import { createFindToolDefinition, type FindOperations } from "./find.js";
|
||||
|
||||
@@ -17,7 +18,18 @@ function textContent(
|
||||
return first?.type === "text" ? (first.text ?? "") : "";
|
||||
}
|
||||
|
||||
function execute(tool: ReturnType<typeof createFindToolDefinition>, limit: number) {
|
||||
return tool.execute("call-1", { pattern: "*.ts", limit }, undefined, undefined, {} as never);
|
||||
}
|
||||
|
||||
describe("find tool", () => {
|
||||
it("rejects fractional limits", async () => {
|
||||
const tool = createFindToolDefinition("/workspace", { operations: operations([]) });
|
||||
|
||||
expect(Value.Check(tool.parameters, { pattern: "*.ts", limit: 1.5 })).toBe(false);
|
||||
await expect(execute(tool, 1.5)).rejects.toThrow("Limit must be an integer");
|
||||
});
|
||||
|
||||
it("clamps non-positive limits before delegating to custom search operations", async () => {
|
||||
// Clamp before delegation so custom backends never receive a zero/negative
|
||||
// limit that could make real matches disappear.
|
||||
@@ -25,13 +37,7 @@ describe("find tool", () => {
|
||||
operations: operations(["/workspace/a.ts", "/workspace/b.ts"]),
|
||||
});
|
||||
|
||||
const result = await tool.execute(
|
||||
"call-1",
|
||||
{ pattern: "*.ts", limit: -4 },
|
||||
undefined,
|
||||
undefined,
|
||||
{} as never,
|
||||
);
|
||||
const result = await execute(tool, -4);
|
||||
|
||||
expect(textContent(result)).toBe("a.ts\n\n[1 results limit reached]");
|
||||
expect(result.details?.resultLimitReached).toBe(1);
|
||||
@@ -42,13 +48,7 @@ describe("find tool", () => {
|
||||
operations: operations(["/workspace/a.ts", "/workspace/b.ts"]),
|
||||
});
|
||||
|
||||
const result = await tool.execute(
|
||||
"call-1",
|
||||
{ pattern: "*.ts", limit: Number.POSITIVE_INFINITY },
|
||||
undefined,
|
||||
undefined,
|
||||
{} as never,
|
||||
);
|
||||
const result = await execute(tool, Number.POSITIVE_INFINITY);
|
||||
|
||||
expect(textContent(result)).toBe("a.ts\nb.ts");
|
||||
expect(result.details).toBeUndefined();
|
||||
|
||||
@@ -45,7 +45,7 @@ const findSchema = Type.Object({
|
||||
description: "File glob, e.g. **/*.ts.",
|
||||
}),
|
||||
path: Type.Optional(Type.String({ description: "Search dir; default cwd." })),
|
||||
limit: Type.Optional(Type.Number({ description: "Max results; default 1000." })),
|
||||
limit: Type.Optional(Type.Integer({ description: "Max results; default 1000." })),
|
||||
});
|
||||
const DEFAULT_LIMIT = 1000;
|
||||
|
||||
@@ -192,6 +192,10 @@ export function createFindToolDefinition(
|
||||
|
||||
void (async () => {
|
||||
try {
|
||||
if (Number.isFinite(limit) && !Number.isInteger(limit)) {
|
||||
settle(() => reject(new Error("Limit must be an integer")));
|
||||
return;
|
||||
}
|
||||
const searchPath = resolveToCwd(searchDir || ".", cwd);
|
||||
const effectiveLimit = normalizePositiveLimit(limit, DEFAULT_LIMIT);
|
||||
const ops = customOps ?? defaultFindOperations;
|
||||
|
||||
Reference in New Issue
Block a user