fix(tavily): reject blank extract URLs (#111333)

* fix(tavily): reject blank extract URLs

Co-authored-by: chatgpt-codex-connector[bot] <199175422+chatgpt-codex-connector[bot]@users.noreply.github.com>

* fix(tavily): normalize tool string arrays

Co-authored-by: VectorPeak <73048950+VectorPeak@users.noreply.github.com>

---------

Co-authored-by: chatgpt-codex-connector[bot] <199175422+chatgpt-codex-connector[bot]@users.noreply.github.com>
Co-authored-by: Peter Steinberger <steipete@gmail.com>
This commit is contained in:
VectorPeak
2026-07-19 09:18:33 -07:00
committed by GitHub
co-authored by chatgpt-codex-connector[bot] <199175422+chatgpt-codex-connector[bot]@users.noreply.github.com> Peter Steinberger
parent 359859d343
commit fe9e018045
3 changed files with 32 additions and 14 deletions
+2 -3
View File
@@ -3,6 +3,7 @@ import type { OpenClawPluginApi } from "openclaw/plugin-sdk/plugin-runtime";
import {
jsonResult,
readPositiveIntegerParam,
readStringArrayParam,
readStringParam,
} from "openclaw/plugin-sdk/provider-web-search";
import { Type } from "typebox";
@@ -49,9 +50,7 @@ export function createTavilyExtractTool(api: OpenClawPluginApi, ctx?: TavilyTool
"Extract clean content from one or more URLs using Tavily. Handles JS-rendered pages. Supports query-focused chunking.",
parameters: TavilyExtractToolSchema,
execute: async (_toolCallId: string, rawParams: Record<string, unknown>) => {
const urls = Array.isArray(rawParams.urls)
? (rawParams.urls as string[]).filter(Boolean)
: [];
const urls = readStringArrayParam(rawParams, "urls") ?? [];
if (urls.length === 0) {
throw new Error("tavily_extract requires at least one URL.");
}
+5 -8
View File
@@ -3,6 +3,7 @@ import type { OpenClawPluginApi } from "openclaw/plugin-sdk/plugin-runtime";
import {
jsonResult,
readPositiveIntegerParam,
readStringArrayParam,
readStringParam,
} from "openclaw/plugin-sdk/provider-web-search";
import { Type } from "typebox";
@@ -65,12 +66,8 @@ export function createTavilySearchTool(api: OpenClawPluginApi, ctx?: TavilyToolC
});
const includeAnswer = rawParams.include_answer === true;
const timeRange = readStringParam(rawParams, "time_range") || undefined;
const includeDomains = Array.isArray(rawParams.include_domains)
? (rawParams.include_domains as string[]).filter(Boolean)
: undefined;
const excludeDomains = Array.isArray(rawParams.exclude_domains)
? (rawParams.exclude_domains as string[]).filter(Boolean)
: undefined;
const includeDomains = readStringArrayParam(rawParams, "include_domains");
const excludeDomains = readStringArrayParam(rawParams, "exclude_domains");
return jsonResult(
await runTavilySearch({
@@ -81,8 +78,8 @@ export function createTavilySearchTool(api: OpenClawPluginApi, ctx?: TavilyToolC
maxResults,
includeAnswer,
timeRange,
includeDomains: includeDomains?.length ? includeDomains : undefined,
excludeDomains: excludeDomains?.length ? excludeDomains : undefined,
includeDomains,
excludeDomains,
}),
);
},
+25 -3
View File
@@ -179,8 +179,8 @@ describe("tavily tools", () => {
max_results: 5,
include_answer: true,
time_range: "week",
include_domains: ["docs.openclaw.ai", "", "openclaw.ai"],
exclude_domains: ["bad.example", ""],
include_domains: [" docs.openclaw.ai ", " ", "openclaw.ai"],
exclude_domains: [" bad.example ", ""],
});
expect(runTavilySearch).toHaveBeenCalledWith({
@@ -313,7 +313,7 @@ describe("tavily tools", () => {
await expect(
searchTool.execute("call-2", {
query: "simple",
include_domains: [""],
include_domains: [" "],
exclude_domains: [],
}),
).resolves.toEqual({
@@ -351,6 +351,28 @@ describe("tavily tools", () => {
expect(runTavilyExtract).not.toHaveBeenCalled();
});
it("rejects blank extract URLs before Tavily calls and trims valid URLs", async () => {
const tool = createTavilyExtractTool(fakeApi());
await expect(
tool.execute("extract-call", {
urls: [" "],
}),
).rejects.toThrow("tavily_extract requires at least one URL.");
expect(runTavilyExtract).not.toHaveBeenCalled();
await tool.execute("extract-call", {
urls: [" https://example.com/article "],
});
const extractParams = requireFirstMockArg(
runTavilyExtract,
"Tavily extract params",
) as TavilyExtractParams;
expect(extractParams.urls).toEqual(["https://example.com/article"]);
});
it("rejects fractional and out-of-range integer options before Tavily calls", async () => {
const searchTool = createTavilySearchTool(fakeApi());
await expect(